Сожалеем, но текст оригинала доступен только зарегистрированным пользователям. |
Або, якщо ви потребуєте графічний інтерфейс, використайте CMake-GUI для більш зручної конфігурації проєкта: Створивши робочий простір OpenCV для обраного IDE, відкрийте проєкт та виберіть "install target" для побудови бібліотеки. Коли процес завершиться, можна налаштувати приклад з використанням новозбудованої OpenCV.
Atronax
21.02.21 в 11:13
|
|
Створюємо вікно OpenGL в середовищі OpenCV
Atronax
22.02.21 в 08:39
|
|
Встановивши бібліотеку OpenCV з підтримкою OpenGL, можемо створити й перше вікно. Налаштування починається з виклику функції для створення вікна з відповідними параметрами - назвою та відміткою OpenGL: cv::namedWindow(ARWindowName, cv::WINDOW_OPENGL);
Atronax
22.02.21 в 08:44
|
|
ARWindowName - це константна стрічка, що позначає назву вікна. У нашому випадку використаємо безмаркерну доповнену реальність. Виклик створить відповідне вікно. Відмітка cv::WINDOW_OPENGL вказує на те, що воно призначене для OpenGL. Потім встановлюємо бажаний розмір вікна: cv::resizeWindow(ARWindowName, 640, 480); Після цього налаштовуємо контекст малювання: cv::setOpenGlContext(ARWindowName);
Atronax
22.02.21 в 08:50
|
|
Тепер вікно готове для використання. Щоб на ньому зобразити будь-що, маємо зареєструвати функцію зворотного виклику таким чином: cv::setOpenGlDrawCallback(ARWindowName, drawAR, NULL); У цьому разу drawAR викликатиметься як відповідь на кожну подію відображення вікна. Перший аргумент встановлює назву, другий - функцію зворотного виклику, а третій - опціональний аргумент останньої.
Atronax
22.02.21 в 09:04
|
|
Функція drawAR повинна мати такий вигляд: void drawAR(void* param) { // Малюємо щось з використанням OpenGL } Щоб повідомити системі про необхідність перемалювати вікно, використовуємо функцію cv::updateWindow: cv::updateWindow(ARWindowName);
Atronax
22.02.21 в 09:05
|
|
Захоплюємо відео за допомогою OpenCV
Atronax
22.02.21 в 09:05
|
|
Бібліотека OpenCV дозволяє легко отримувати кадри з більшості вебкамер та відеофайлів. Щоб захопити відео, можна скористатися класом cv::VideoCapture як показано в частині "Отримуємо доступ до вебкамери" у розділі 1 "Ефект мультиплікація та зміна кольору шкіри для Android".
Atronax
22.02.21 в 09:07
|
|
Візуалізуємо доповнену реальність
Atronax
22.02.21 в 09:07
|
|
Покажемо структуру ARDrawingContext, що зберігатиме всі важливі дані для візуалізації: Останнє зображення, отримане з камери Матриця калібрування камери Поза шаблона в 3D (за наявності) Внутрішні дані, пов'язані з OpenGL (ідентифікатор текстури і так далі)
Atronax
22.02.21 в 09:09
|
|
ARDrawingContext.hpp Наступний код оголошує клас ARDrawingContext: class ARDrawingContext { public: ARDrawingContext(const CameraCalibration& c); bool patternPresent; Transformation patternPose; //! Зображає вікно OpenGL void draw(); //! Встановлює нове зображення в якості фону void updateBackground(const cv::Mat& frame); private: //! Активує відеокадр в якості фону void drawCameraFrame (); //! Зображає доповнену реальність void drawAugmentedScene(); //! Будує дійсну матрицю проєкції //! на основі даних калібрування камери для AR void buildProjectionMatrix(const Matrix33& calibration, int w, int h, Matrix44& result); //! Малює вісь координат void drawCoordinateAxis(); //! Зображає модель куба void drawCubeModel(); private: bool m_textureInitialized; unsigned int m_backgroundTextureId; CameraCalibration m_calibration; cv::Mat m_backgroundImage; };
Atronax
22.02.21 в 09:16
|
|
ARDrawingContext.cpp Налаштування вікна OpenGL відбувається в конструкторі так: ARDrawingContext::ARDrawingContext(std::string windowName, cv::Size frameSize, const CameraCalibration& c) : m_isTextureInitialized(false) , m_calibration(c) , m_windowName(windowName) { // Створюємо вікно з підтримкою OpenGL cv::namedWindow(windowName, cv::WINDOW_OPENGL); // Змінюємо його розміри, щоб відповідати характеристиками відео cv::resizeWindow(windowName, frameSize.width, frameSize.height); // Встановлюємо функцію зворотного виклику OpenGL: cv::setOpenGlContext(windowName); cv::setOpenGlDrawCallback(windowName, ARDrawingContextDrawCallback, this); }
Atronax
22.02.21 в 09:21
|
|
Оскільки тепер маємо окремий клас для зберігання стану візуалізації, модифікуємо виклик cv::setOpenGlDrawCallback та передаємо в якості параметра екземпляр ARDrawingContext. Змінена функція зворотного вигляду матиме вигляд: void ARDrawingContextDrawCallback(void* param) { ARDrawingContext * ctx = static_cast<ARDrawingContext*>(param); if (ctx) { ctx->draw(); } }
Atronax
22.02.21 в 09:22
|
|
ARDrawingContext повністю відповідальна за формування ефекту доповненої реальності. Візуалізація кадра починається із зображення фону з урахуванням ортографічної проєкції. Потім малюємо 3D модель з коректною перспективною проєкцією та перетвореннями. Наступний код описує фінальну версію функції: void ARDrawingContext::draw() { // Очищуємо весь екран glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // Малюємо фон drawCameraFrame(); // Доповнюємо об'єктами AR drawAugmentedScene(); }
Atronax
22.02.21 в 09:26
|
|
Очистивши екран та буфер глибини, перевіримо наявність і коректність текстури відеокадра. Якщо вона налаштована як слід, продовжуємо малювати фон, інакше створюємо нову 2D-текстуру викликом glGenTextures.
Atronax
22.02.21 в 09:28
|
|
Щоб зобразити фон, встановлюємо ортографічну проєкцію та малюємо заповнений прямокутник, що покриє весь вид екрану. Він нерозривно пов'язаний з текстурою, що відповідає вмісту об'єкта m_backgroundImage. Цей контент завантажується в пам'ять OpenGL заздалегідь. Функція аналогічна до описаної в попередньому розділі, тому не показуватимемо її код тут. Після зображення фонового зображення, перемикаємося на роботу з доповненою реальністю. У цьому разі важливо встановити коректні перспективні проєкції, що відповідають калібруванню камери.
Atronax
22.02.21 в 09:39
|
|
Подальший код пояснює, як будувати матрицю проєкції OpenGL з урахуванням калібрування камери, а також як візуалізувати сцену: void ARDrawingContext::drawAugmentedScene() { // Налаштовуємо проєкцію доповнення Matrix44 projectionMatrix; int w = m_backgroundImage.cols; int h = m_backgroundImage.rows; buildProjectionMatrix(m_calibration, w, h, projectionMatrix); glMatrixMode(GL_PROJECTION); glLoadMatrixf(projectionMatrix.data); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if (isPatternPresent) { // Встановлюємо перетворення шаблона Matrix44 glMatrix = patternPose.getMat44(); glLoadMatrixf(reinterpret_cast<const GLfloat*> (&glMatrix.data[0])); // Зображаємо модель drawCoordinateAxis(); drawCubeModel(); } }
Atronax
22.02.21 в 09:42
|
|
Функцію buildProjectionMatrix взято без змін з попереднього розділу. Після застосування перспективної проєкції встановлюємо матрицю GL_MODELVIEW для перетворення зображення. Щоб довести коректність оцінки пози, у позиції шаблона малюємо одиничну систему координат. Підготовчі процеси практично повністю виконано. Тепер формуємо алгоритм розпізнавання та оцінюємо позу знайденого шаблона у тривимірному просторі, а також будуємо систему візуалізації доповненої реальності. Поглянемо на UML-діаграму процедури обробки кадра у нашому застосунку:
Atronax
22.02.21 в 10:06
|
|
Демонстрація Даний проєкт підтримує обробку звичайних зображень, записаного відеоматеріалу та прямого ефіру з вебкамери. Створимо дві функції, що допоможуть здійснити це.
Atronax
22.02.21 в 10:09
|
|
main.cpp Функція processVideo призначена для обробки відео, а processSingleImage - одного кадру, як показано далі: void processVideo(const cv::Mat& patternImage, CameraCalibration& calibration, cv::VideoCapture& capture); void processSingleImage(const cv::Mat& patternImage, CameraCalibration& calibration, const cv::Mat& image); Виходячи з назв, перша працює з джерелом відео, а інша - з єдиним зображенням (корисна для налагодження). Обидві здійснюють подібну процедуру обробки відео, розпізнавання шаблона, візуалізації та взаємодії з користувачем. Функція processFrame описує кроки так:
Atronax
22.02.21 в 10:21
|
|
/** * Виконує повну процедуру розпізнавання на кадрі з камери .* та малює сцену з використанням контексту. * Крім цього, фукнція виводить інформацію для налагодження .* над вікном доповненої реальності. Повертає true, .* якщо цикл обробки має бути зупинений; інакше - false. */ bool processFrame(const cv::Mat& cameraFrame, ARPipeline& pipeline, ARDrawingContext& drawingCtx) { // Клонуємо зображення для фону (намалюємо // дещо на його поверхні) cv::Mat img = cameraFrame.clone();
Atronax
22.02.21 в 10:25
|
|
// Зображаємо дані: if (pipeline.m_patternDetector.enableHomographyRefinement) cv::putText(img, "Уточнення пози: увімкнено (натисніть 'h', щоб вимкнути)", cv::Point(10,15), CV_FONT_HERSHEY_PLAIN, 1, CV_RGB(0,200,0)); else cv::putText(img, "Уточнення пози: вимкнено (натисніть 'h', щоб увімкнути)", cv::Point(10,15), CV_FONT_HERSHEY_PLAIN, 1, CV_RGB(0,200,0)); cv::putText(img, "Поріг RANSAC: " + ToString(pipeline.m_patternDetector.homographyReprojectionThreshold) + "(Натисніть '-'/'+' для регулювання)", cv::Point(10, 30), CV_FONT_HERSHEY_PLAIN, 1, CV_RGB(0,200,0)); // Встановлюємо новий кадр камери: drawingCtx.updateBackground(img); // Знаходимо шаблон та оновлюємо статус розпізнавання: drawingCtx.isPatternPresent = pipeline.processFrame(cameraFrame);
Atronax
22.02.21 в 10:30
|
|
// Актуалізуємо позу шаблона: drawingCtx.patternPose = pipeline.getPatternLocation(); // Запитуємо оновлення вікна: drawingCtx.updateWindow(); // Зчитуємо ввід з клавіатури: int keyCode = cv::waitKey(5); bool shouldQuit = false; if (keyCode == '+' || keyCode == '=') { pipeline.m_patternDetector.homographyReprojectionThreshold += 0.2f;
Atronax
22.02.21 в 10:31
|
|
0.2f; pipeline.m_patternDetector.homographyReprojectionThreshold = std::min(10.0f, pipeline.m_patternDetector.homographyReprojectionThreshold); } else if (keyCode == '-') { pipeline.m_patternDetector.homographyReprojectionThreshold -= 0.2f; pipeline.m_patternDetector.homographyReprojectionThreshold = std::max(0.0f, pipeline.m_patternDetector.homographyReprojectionThreshold); } else if (keyCode == 'h') { pipeline.m_patternDetector.enableHomographyRefinement = !pipeline.m_patternDetector.enableHomographyRefinement; } else if (keyCode == 27 || keyCode == 'q') { shouldQuit = true; } return shouldQuit; }
Atronax
22.02.21 в 10:32
|
|
Налаштування ARPipeline та ARDrawingContext здійснюється або в функції processSingleImage, або в processVideo таким чином: void processSingleImage(const cv::Mat& patternImage, CameraCalibration& calibration, const cv::Mat& image) { cv::Size frameSize(image.cols, image.rows); ARPipeline pipeline(patternImage, calibration); ARDrawingContext drawingCtx("Безмаркерна AR", frameSize, calibration); bool shouldQuit = false; do { shouldQuit = processFrame(image, pipeline, drawingCtx); } while (!shouldQuit); }
Atronax
22.02.21 в 10:33
|
|
Створюємо об'єкт ARPipeline з урахуванням шаблону та аргументів калібрування. З використанням останніх також налаштовуємо ARDrawingContext. Після здійснення цих кроків матимемо готове для роботи вікно OpenGL. Потім завантажуємо зображення запиту в контекст малювання та викликаємо функцію ARPipeline.processFrame для пошуку шаблона. Якщо шаблон знайдено, копіюємо його положення в контекст для подальшої візуалізації. Якщо ж ні, візуалізуємо кадр без доповненої реальності. Демонстраційний проєкт можна запустити одним з наступних способів:
Atronax
22.02.21 в 10:46
|
|
Для обробки єдиного зображення: markerless_ar_demo pattern.png test_image.png Для обробки збереженого відеопотоку: markerless_ar_demo pattern.png test_video.avi Для обробки прямого ефіру з вебкамери: markerless_ar_demo pattern.png Результат доповнення одного кадру показаний на наступному скріншоті:
Atronax
22.02.21 в 10:47
|
|
|
|
|
У цьому розділі ви дізналися про дескриптори особливих точок та їх використання для опису шаблонів, інваріантних до змін масштабу та повороту. Цей опис можна використати для пошуку подібних фрагментів на інших зображеннях. Також пояснено сильні та слабкі сторони найбільш відомих детекторів ключових точок. У другій половині частини ми навчились об'єднувати OpenGL та OpenCV для спільної візуалізації доповненої реальності.
Atronax
22.02.21 в 10:54
|
|
Посилання Distinctive Image Features from Scale-Invariant Keypoints (http://www.cs.ubc.ca/~lowe/papers/ijcv04.pdf) SURF: Speeded Up Robust Features (http://www.vision.ee.ethz.ch/~surf/eccv06.pdf) Model-Based Object Pose in 25 Lines of Code, Dementhon and L.S Davis, International Journal of Computer Vision, edition 15, pp. 123-141, 1995 Linear N-Point Camera Pose Determination, L.Quan, IEEE Trans. on Pattern Analysis and Machine Intelligence, vol. 21, edition. 7, July 1999
Atronax
22.02.21 в 10:55
|
|
Random Sample Consensus: A Paradigm for Model Fitting with Applications to Image Analysis and Automated Cartography, M. Fischer and R. Bolles, Graphics and Image Processing, vol. 24, edition. 6, pp. 381-395, June 1981 Multiple View Geometry in Computer Vision, R. Hartley and A.Zisserman, Cambridge University Press (http://www.umiacs.umd.edu/~ramani/cmsc828d/lecture9.pdf) Camera Pose Revisited – New Linear Algorithms, M. Ameller, B.Triggs, L.Quan (http://hal.inria.fr/docs/00/54/83/06/PDF/Ameller-eccv00.pdf) Closed-form solution of absolute orientation using unit quaternions, Berthold K. P. Horn, Journal of the Optical Society A, vol. 4, 629–642
Atronax
22.02.21 в 10:55
|
|