- PVSM.RU - https://www.pvsm.ru -
![Дополненная реальность / [Из песочницы] 2D >3D in Augmented reality image](http://www.pvsm.ru/images/a92ae7660de8019281482d1afe3a5c9f.jpg)
В данной статье я расскажу как в приложениях Augmented reality [1] по найденому расположению объекта в сцене построить 3D-пространство. Для этого необходимо получить две матрицы – проекционную (GL_PROJECTION) и модельную (GL_MODELVIEW) для работы, например, в OpenGL. Делать это мы будем средствами библиотеки OpenCV.
Недавно приходилось решать эту задачу, но ресурса, где просто поэтапно объяснялось как это сделать я не нашел (может плохо искал), а подводных камней в данной проблеме хватает. В любом случае, статья на хабре описывающая эту задачу не повредит.
<a rel="nofollow" name="habracut">
Сам я — iOS программист, в свободное время занимаюсь разработкой собственного движка Augmented Reality. За базу взял OpenCV [2] – библиотека с открытым кодом для компьютерного зрения.
Вообще, наиболее интересным предложением для мобильных устройств (iOS/Android) в данной области являются разработки компании Qualcomm.
Сравнительно недавно, они выпустили собственный AR SDK под именем Vuforia [3]. При этом, пользование SDK является бесплатным как для разработки, так и для выкладывании приложения в магазин (AppStore, AndroidMarket), о чем гордо заявляет абзац Licensing. В то же время, они пишут что вы должны предупредить конечного пользователя, о том, что данное SDK может собирать некоторую анонимную информацию, и отправлять на сервера Qualcomm. Найти данный раздел можно по этой ссылке [4] выбрав в правом меню Getting Started SDK -> Step 3: Compiling & Running… -> Publish Your Application. И плюс к этому, можете считать меня параноиком, но я на 90% уверен, что когда их SDK наберет определенный процент популярности, они скажут “Все, халява закончилась, платите бабки”.
Поэтому, считаю разработку собственного движка не пустой тратой времени.
Собственно, к делу!
Считаем, что к этому моменту вы внедрили OpenCV в ваш проект (как? [5]), и уже написали метод распознавания объекта в кадре поступающем с камеры. То есть, примерно такая картинка у вас есть:
![Дополненная реальность / [Из песочницы] 2D >3D in Augmented reality image](http://www.pvsm.ru/images/51d586ba3452934ea13c8297264998d2.jpg)
Теорию по данному вопросу можно найти во многих источниках. Основные ссылки я привел внизу. Стартовым ресурсом является страничка документации OpenCV [6], хотя вопросов по прочтению останется много.
В двух словах, для построения 3D пространства по найденой 2D гомографии, нам нужно знать 2 матрицы:
,
На практике, для того чтобы OpenGL отрендерил 3D модельку поверх нашего объекта, нам нужно ему задать:
Примечание: В iOS вы можете использовать 2 версии OpenGLES – 1.1 и 2.0. Основное отличие – наличие шейдеров во второй версии. В обоих случаях, мы должны задать 2 матрицы, только в первом случае они задаются конструкцией типа:
glMatrixMode(GL_PROJECTION); glLoadMatrixf(projectionMatrix); glMatrixMode(GL_MODELVIEW); glLoadMatrixf(modelViewMatrix);
А во втором, вы передаете их на вход шейдерам.
Далее, обусловимся что размер кадра, который вы получаете с камеры cameraSize = (width, height). В моем случае cameraSize=(640, 480).
Разберемся как строить каждую матрицу.
Данная матрица строится на основе матрицы камеры. Как было показано выше, последняя состоит из определенных параметров камеры. В теории, можно высчитать эти параметры основываясь на технических характеристиках камеры, но на практике этого никто не делает.
Процесс нахождения параметров камеры называется ее калибрацией. В OpenCV написаны все необходимые функции для выполнения калибрации. Также, есть пример [7], который позволяет просто в «онлайне» откалибровать свою веб-камеру. Но нам же нужно откалибровать камеру на устройстве – iPhone/iPad/iPod. И здесь я пошел по пути описанном тут [8].
Калибровать камеру мы будем «оффлайн». Это означает, что мы сделаем снимки калибровачного шаблона (шахматной доски) камерой нашего устройства, перенесем фотографии на компьютер, и посчитаем параметры с этих фоток. Несколько моментов:
Xcode проект программы для калибрации вы можете скачать тут [10]. При этом у вас должен быть скомпилирован OpenCV. Либо же вы можете скачать скомпилированный фреймворк отсюда [11].
Сами фотографии, нужно положить в папку со скомпилированным бинарником, и переименовать по порядку.
Если все будет ок, вы получите 2 файла на выходе программы:
Если на каких-то снимках шаблон не будет найден – попробуйте заменить эти снимки на другие, с лучшим освещением, возможно под менее острым углом к шаблону. OpenCV должен легко находить все внутренние точки шаблона.
Имея числа с данных файлов, мы можем построить матрицу проекции для OpenGL.
float cameraMatrix[9] = {6.24860291e+02, 0., cameraSize.width*0.5f, 0., 6.24860291e+02, cameraSize.height*0.5f, 0., 0., 1.}; - (void)buildProjectionMatrix { // Camera parameters double f_x = cameraMatrix[0]; // Focal length in x axis double f_y = cameraMatrix[4]; // Focal length in y axis (usually the same?) double c_x = cameraMatrix[2]; // Camera primary point x double c_y = cameraMatrix[5]; // Camera primary point y double screen_width = cameraSize.width; // In pixels double screen_height = cameraSize.height; // In pixels double near = 0.1; // Near clipping distance double far = 1000; // Far clipping distance projectionMatrix[0] = 2.0 * f_x / screen_width; projectionMatrix[1] = 0.0; projectionMatrix[2] = 0.0; projectionMatrix[3] = 0.0; projectionMatrix[4] = 0.0; projectionMatrix[5] = 2.0 * f_y / screen_height; projectionMatrix[6] = 0.0; projectionMatrix[7] = 0.0; projectionMatrix[8] = 2.0 * c_x / screen_width - 1.0; projectionMatrix[9] = 2.0 * c_y / screen_height - 1.0; projectionMatrix[10] = -( far+near ) / ( far - near ); projectionMatrix[11] = -1.0; projectionMatrix[12] = 0.0; projectionMatrix[13] = 0.0; projectionMatrix[14] = -2.0 * far * near / ( far - near ); projectionMatrix[15] = 0.0; }
Несколько замечаний:
При построении данной матрицы мне помог вот этот [13] вопрос в StackOverflow.
Благо, в OpenCV необходимые функции уже рализованы.
Итак, код:
float cameraMatrix[9] = {6.24860291e+02, 0., cameraSize.width*0.5f, 0., 6.24860291e+02, cameraSize.height*0.5f, 0., 0., 1.}; float distCoeff[5] = {1.61426172e-01, -5.95113218e-01, 7.10574386e-04, -1.91498715e-02, 1.66041708e+00}; - (void)buildModelViewMatrixUseOld:(BOOL)useOld { clock_t timer; startTimer(&timer); CvMat cvCameraMatrix = cvMat( 3, 3, CV_32FC1, (void*)cameraMatrix ); CvMat cvDistortionMatrix = cvMat( 1, 5, CV_32FC1, (void*)distCoeff ); CvMat* objectPoints = cvCreateMat( 4, 3, CV_32FC1 ); CvMat* imagePoints = cvCreateMat( 4, 2, CV_32FC1 ); // Defining object points and image points int minDimension = MIN(detector->modelWidth, detector->modelHeight)*0.5f; for (int i=0; i<4; i++) { float objectX = (detector->x_corner[i] - detector->modelWidth/2.0f)/minDimension; float objectY = (detector->y_corner[i] - detector->modelHeight/2.0f)/minDimension; cvmSet(objectPoints, i, 0, objectX); cvmSet(objectPoints, i, 1, objectY); cvmSet(objectPoints, i, 2, 0.0f); cvmSet(imagePoints, i, 0, detector->detected_x_corner[i]); cvmSet(imagePoints, i, 1, detector->detected_y_corner[i]); } CvMat* rvec = cvCreateMat(1, 3, CV_32FC1); CvMat* tvec = cvCreateMat(1, 3, CV_32FC1); CvMat* rotMat = cvCreateMat(3, 3, CV_32FC1); cvFindExtrinsicCameraParams2(objectPoints, imagePoints, &cvCameraMatrix, &cvDistortionMatrix, rvec, tvec); // Convert it CV_MAT_ELEM(*rvec, float, 0, 1) *= -1.0; CV_MAT_ELEM(*rvec, float, 0, 2) *= -1.0; cvRodrigues2(rvec, rotMat); GLfloat RTMat[16] = {cvmGet(rotMat, 0, 0), cvmGet(rotMat, 1, 0), cvmGet(rotMat, 2, 0), 0.0f, cvmGet(rotMat, 0, 1), cvmGet(rotMat, 1, 1), cvmGet(rotMat, 2, 1), 0.0f, cvmGet(rotMat, 0, 2), cvmGet(rotMat, 1, 2), cvmGet(rotMat, 2, 2), 0.0f, cvmGet(tvec, 0, 0) , -cvmGet(tvec, 0, 1), -cvmGet(tvec, 0, 2), 1.0f}; cvReleaseMat(&objectPoints); cvReleaseMat(&imagePoints); cvReleaseMat(&rvec); cvReleaseMat(&tvec); cvReleaseMat(&rotMat); printTimerWithPrefix((char*)"ModelView matrix computation", timer); }
Для начала нам нужно определить 4 пары точек объекта и соответствующего положения в кадре.
Точки положения в кадре – это вершины четырехугольника, описывающего (ограничивающего) объект в кадре. Получить данные точки, имея гомографию преобразования H, можно просто подействовав на крайние точки шаблона данной гомографией:
![Дополненная реальность / [Из песочницы] 2D >3D in Augmented reality image](http://www.pvsm.ru/images/20505776e02a522b1733c4a1e27567bf.jpg)
Относительно точек самого объекта есть пару моментов:
![Дополненная реальность / [Из песочницы] 2D >3D in Augmented reality image](http://www.pvsm.ru/images/7a83a075a183f7af1952a3176b7364b6.jpg)
z = 1.0
![Дополненная реальность / [Из песочницы] 2D >3D in Augmented reality image](http://www.pvsm.ru/images/06d75a642e700b74086018273b25ff59.jpg)
z = 0.0
Сконструированные матрицы передаются функции cvFindExtrinsicCameraParams2. Она построит нам вектор поворота, и вектор переноса. Из вектора поворота нам нужно получить матрицу поворота. Это делается с помощью функции cvRodrigues2, предварительно немного преобразовав вектор поворота умножением второго и третьего элементов на -1. Далее, нам остается только сохранить полученные данные в модельную матрицу для OpenGL. При этом, матрица OpenGL должна быть транспонирована.
Все, удаляем временные объекты, и матрица модели получена.
Имея процедуру построения двух матриц мы можем смело создавать GLView, и рисовать там модельки. Замечу, что функция нахождения матрицы модели выполняется не более 10 миллисекунд на iPhone 4, то есть ее использование не понизит FPS вашего распознавания значительно.
Спасибо за внимание.
1. http://old.uvr.gist.ac.kr/wlee/web/techReports/ar/Camera%20Models.html [14]
2. http://www.hitl.washington.edu/artoolkit/mail-archive/message-thread-00653-Re--Questions-concering-.html [15]
3. http://sightations.wordpress.com/2010/08/03/simulating-calibrated-cameras-in-opengl/ [16]
4. http://www.songho.ca/opengl/gl_projectionmatrix.html [17]
Автор: yltastep
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/opengl/3039
Ссылки в тексте:
[1] Augmented reality: http://habrahabr.ru/blogs/augmented_reality/
[2] OpenCV: http://opencv.willowgarage.com/
[3] Vuforia: https://developer.qualcomm.com/develop/mobile-technologies/augmented-reality
[4] этой ссылке: https://ar.qualcomm.at/qdevnet/sdk/ios
[5] как?: http://habrahabr.ru/blogs/image_processing/135244/
[6] документации OpenCV: http://opencv.willowgarage.com/documentation/cpp/camera_calibration_and_3d_reconstruction.html
[7] пример: http://dasl.mem.drexel.edu/~noahKuntz/openCVTut10.html
[8] тут: http://urbanar.blogspot.com/2011/04/offline-camera-calibration-for.html
[9] Википедии: http://en.wikipedia.org/wiki/Distortion_(optics)
[10] тут: http://dl.dropbox.com/u/13584616/CameraCalibration.zip
[11] отсюда: http://www.ient.rwth-aachen.de/cms/software/opencv/
[12] отсюда: http://opencv.willowgarage.com/wiki/Posit
[13] этот: http://stackoverflow.com/questions/3712049/how-to-use-an-opencv-rotation-and-translation-vector-with-opengl-es-in-android
[14] http://old.uvr.gist.ac.kr/wlee/web/techReports/ar/Camera%20Models.html: http://old.uvr.gist.ac.kr/wlee/web/techReports/ar/Camera%20Models.html
[15] http://www.hitl.washington.edu/artoolkit/mail-archive/message-thread-00653-Re--Questions-concering-.html: http://www.hitl.washington.edu/artoolkit/mail-archive/message-thread-00653-Re--Questions-concering-.html
[16] http://sightations.wordpress.com/2010/08/03/simulating-calibrated-cameras-in-opengl/: http://sightations.wordpress.com/2010/08/03/simulating-calibrated-cameras-in-opengl/
[17] http://www.songho.ca/opengl/gl_projectionmatrix.html: http://www.songho.ca/opengl/gl_projectionmatrix.html
Нажмите здесь для печати.