Mastering OpenCV with Practical Computer Vision Projects / Опановуюємо OpenCV через практичні проекти комп'ютерного зору: Розділ 3. Безмаркерна доповнена реальність

← →
Английский оригинал Перевод на украинский

Сожалеем, но текст оригинала доступен только зарегистрированным пользователям.

Або, якщо ви потребуєте графічний інтерфейс, використайте CMake-GUI для більш зручної конфігурації проєкта:
Створивши робочий простір OpenCV для обраного IDE, відкрийте проєкт та виберіть "install target" для побудови бібліотеки. Коли процес завершиться, можна налаштувати приклад з використанням новозбудованої OpenCV.

Atronax 21.02.21 в 11:13

0

Створюємо вікно OpenGL в середовищі OpenCV

Atronax 22.02.21 в 08:39

0

Встановивши бібліотеку OpenCV з підтримкою OpenGL, можемо створити й перше вікно. Налаштування починається з виклику функції для створення вікна з відповідними параметрами - назвою та відміткою OpenGL:
cv::namedWindow(ARWindowName, cv::WINDOW_OPENGL);

Atronax 22.02.21 в 08:44

0

ARWindowName - це константна стрічка, що позначає назву вікна. У нашому випадку використаємо безмаркерну доповнену реальність. Виклик створить відповідне вікно. Відмітка cv::WINDOW_OPENGL вказує на те, що воно призначене для OpenGL. Потім встановлюємо бажаний розмір вікна:
cv::resizeWindow(ARWindowName, 640, 480);
Після цього налаштовуємо контекст малювання:
cv::setOpenGlContext(ARWindowName);

Atronax 22.02.21 в 08:50

0

Тепер вікно готове для використання. Щоб на ньому зобразити будь-що, маємо зареєструвати функцію зворотного виклику таким чином:
cv::setOpenGlDrawCallback(ARWindowName, drawAR, NULL);
У цьому разу drawAR викликатиметься як відповідь на кожну подію відображення вікна. Перший аргумент встановлює назву, другий - функцію зворотного виклику, а третій - опціональний аргумент останньої.

Atronax 22.02.21 в 09:04

0

Функція drawAR повинна мати такий вигляд:
void drawAR(void* param)
{
// Малюємо щось з використанням OpenGL
}
Щоб повідомити системі про необхідність перемалювати вікно, використовуємо функцію cv::updateWindow:
cv::updateWindow(ARWindowName);

Atronax 22.02.21 в 09:05

0

Захоплюємо відео за допомогою OpenCV

Atronax 22.02.21 в 09:05

0

Бібліотека OpenCV дозволяє легко отримувати кадри з більшості вебкамер та відеофайлів. Щоб захопити відео, можна скористатися класом cv::VideoCapture як показано в частині "Отримуємо доступ до вебкамери" у розділі 1 "Ефект мультиплікація та зміна кольору шкіри для Android".

Atronax 22.02.21 в 09:07

0

Візуалізуємо доповнену реальність

Atronax 22.02.21 в 09:07

0

Покажемо структуру ARDrawingContext, що зберігатиме всі важливі дані для візуалізації:
Останнє зображення, отримане з камери
Матриця калібрування камери
Поза шаблона в 3D (за наявності)
Внутрішні дані, пов'язані з OpenGL (ідентифікатор текстури і так далі)

Atronax 22.02.21 в 09:09

0

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

0

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

0

Оскільки тепер маємо окремий клас для зберігання стану візуалізації, модифікуємо виклик cv::setOpenGlDrawCallback та передаємо в якості параметра екземпляр ARDrawingContext.
Змінена функція зворотного вигляду матиме вигляд:
void ARDrawingContextDrawCallback(void* param)
{
ARDrawingContext * ctx = static_cast<ARDrawingContext*>(param);
if (ctx)
{
ctx->draw();
}
}

Atronax 22.02.21 в 09:22

0

ARDrawingContext повністю відповідальна за формування ефекту доповненої реальності. Візуалізація кадра починається із зображення фону з урахуванням ортографічної проєкції. Потім малюємо 3D модель з коректною перспективною проєкцією та перетвореннями. Наступний код описує фінальну версію функції:
void ARDrawingContext::draw()
{
// Очищуємо весь екран
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
// Малюємо фон
drawCameraFrame();
// Доповнюємо об'єктами AR
drawAugmentedScene();
}

Atronax 22.02.21 в 09:26

0

Очистивши екран та буфер глибини, перевіримо наявність і коректність текстури відеокадра. Якщо вона налаштована як слід, продовжуємо малювати фон, інакше створюємо нову 2D-текстуру викликом glGenTextures.

Atronax 22.02.21 в 09:28

0

Щоб зобразити фон, встановлюємо ортографічну проєкцію та малюємо заповнений прямокутник, що покриє весь вид екрану. Він нерозривно пов'язаний з текстурою, що відповідає вмісту об'єкта m_backgroundImage. Цей контент завантажується в пам'ять OpenGL заздалегідь. Функція аналогічна до описаної в попередньому розділі, тому не показуватимемо її код тут.
Після зображення фонового зображення, перемикаємося на роботу з доповненою реальністю. У цьому разі важливо встановити коректні перспективні проєкції, що відповідають калібруванню камери.

Atronax 22.02.21 в 09:39

0

Подальший код пояснює, як будувати матрицю проєкції 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

0

Функцію buildProjectionMatrix взято без змін з попереднього розділу. Після застосування перспективної проєкції встановлюємо матрицю GL_MODELVIEW для перетворення зображення. Щоб довести коректність оцінки пози, у позиції шаблона малюємо одиничну систему координат. Підготовчі процеси практично повністю виконано. Тепер формуємо алгоритм розпізнавання та оцінюємо позу знайденого шаблона у тривимірному просторі, а також будуємо систему візуалізації доповненої реальності. Поглянемо на UML-діаграму процедури обробки кадра у нашому застосунку:

Atronax 22.02.21 в 10:06

0

Демонстрація
Даний проєкт підтримує обробку звичайних зображень, записаного відеоматеріалу та прямого ефіру з вебкамери. Створимо дві функції, що допоможуть здійснити це.

Atronax 22.02.21 в 10:09

0

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

0

/**
* Виконує повну процедуру розпізнавання на кадрі з камери
.* та малює сцену з використанням контексту.
* Крім цього, фукнція виводить інформацію для налагодження
.* над вікном доповненої реальності. Повертає true,
.* якщо цикл обробки має бути зупинений; інакше - false.
*/
bool processFrame(const cv::Mat& cameraFrame, ARPipeline& pipeline,
ARDrawingContext& drawingCtx)
{
// Клонуємо зображення для фону (намалюємо
// дещо на його поверхні)
cv::Mat img = cameraFrame.clone();

Atronax 22.02.21 в 10:25

0

// Зображаємо дані:
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

0

// Актуалізуємо позу шаблона:
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

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

0

Налаштування 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

0

Створюємо об'єкт ARPipeline з урахуванням шаблону та аргументів калібрування. З використанням останніх також налаштовуємо ARDrawingContext. Після здійснення цих кроків матимемо готове для роботи вікно OpenGL. Потім завантажуємо зображення запиту в контекст малювання та викликаємо функцію ARPipeline.processFrame для пошуку шаблона. Якщо шаблон знайдено, копіюємо його положення в контекст для подальшої візуалізації. Якщо ж ні, візуалізуємо кадр без доповненої реальності. Демонстраційний проєкт можна запустити одним з наступних способів:

Atronax 22.02.21 в 10:46

0

Для обробки єдиного зображення:
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

0

Висновки

Atronax 22.02.21 в 10:47

0

У цьому розділі ви дізналися про дескриптори особливих точок та їх використання для опису шаблонів, інваріантних до змін масштабу та повороту. Цей опис можна використати для пошуку подібних фрагментів на інших зображеннях. Також пояснено сильні та слабкі сторони найбільш відомих детекторів ключових точок. У другій половині частини ми навчились об'єднувати OpenGL та OpenCV для спільної візуалізації доповненої реальності.

Atronax 22.02.21 в 10:54

0

Посилання
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

0

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

0
Минутку...

← Предыдущая страница