himik 2 Жалоба Опубликовано February 2, 2012 Здравствуйте! Недавно начал разбираться с OpenCV а конкретно c определением расстояния до какого либо объекта, и возникло несколько вопросов: 1)Как я понимаю первое что надо сделать это построить карту глубины, ее постройка для статичных картинок проблем не вызывает а вот при использовании камер, cvFindStereoCorrespondenceGC работает слишком медленно а cvFindStereoCorrespondenceBM дает слишком плохой результат. 2)Ладно допустим карта глубины построена следующий шаг это нахождение реального расстояния до объекта для этого как я понимаю нужно использовать ReprojectImageTo3D, первый параметр disparityImage который нам создал cvFindStereoCorrespondenceGC() второй параметр IplImage (size, IPL_DEPTH_32F, 3) третий параметр самый интересный... матрица СvMat(4,4, CV_64F), но где ее взять, как я понимаю для этого и нужно калибровать камеры, но как? IplImage *image_ = 0; IplImage *image1_ = 0; int main(array<System::String ^> ^args) { IplImage *image_ = 0; IplImage *image1_ = 0; image_ = cvLoadImage("left.jpg", 1 ); image1_ = cvLoadImage("right.jpg", 1 ); IplImage *image_g = cvCreateImage( cvSize(image_->width,image_->height), 8,1); IplImage *image1_g = cvCreateImage( cvSize(image_->width,image_->height), 8,1); CvSize size = cvGetSize(image_); CvMat* disparity_left = cvCreateMat( size.height, size.width, CV_16S ); CvMat* disparity_right = cvCreateMat( size.height, size.width, CV_16S ); cvCvtColor( image_, image_g, CV_BGR2GRAY ); cvCvtColor( image1_, image1_g, CV_BGR2GRAY ); CvStereoGCState* state = cvCreateStereoGCState( 16, 2); cvFindStereoCorrespondenceGC( image_g, image1_g, disparity_left, disparity_right, state, 0 ); cvReleaseStereoGCState( &state ); CvMat* disparity_left_visual = cvCreateMat( size.height, size.width, CV_8U ); cvConvertScale( disparity_left, disparity_left_visual, -16 ); cvSave( "disparity.png", disparity_left_visual ); cvSaveImage( "disparity.jpg", disparity_left_visual ); return 0; } 1 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано February 3, 2012 Почитайте здесь: http://www.compvision.ru/forum/index.php?showtopic=47 И здесь пример в самом конце: http://www.compvision.ru/forum/index.php?showtopic=251&st=20 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
himik 2 Жалоба Опубликовано February 3, 2012 Ага, на счет калибровки понятно. А вот на счет построения карты глубины не очень, если использовать FindStereoCorrespondenceBM то получается примерно так как на картинке: IplImage *image_ = 0; IplImage *image1_ = 0; image_ = cvLoadImage("left.jpg", 1 ); image1_ = cvLoadImage("right.jpg", 1 ); IplImage *image_g = cvCreateImage( cvSize(image_->width,image_->height), 8,1); IplImage *image1_g = cvCreateImage( cvSize(image_->width,image_->height), 8,1); CvSize size = cvGetSize(image_); CvMat* disparity_left = cvCreateMat( size.height, size.width, CV_16S ); CvMat* disparity_right = cvCreateMat( size.height, size.width, CV_16S ); cvCvtColor( image_, image_g, CV_BGR2GRAY ); cvCvtColor( image1_, image1_g, CV_BGR2GRAY ); CvStereoBMState* state = cvCreateStereoBMState( 16, 0); cvFindStereoCorrespondenceBM( image_g, image1_g, disparity_left, state ); cvReleaseStereoBMState( &state ); CvMat* disparity_left_visual = cvCreateMat( size.height, size.width, CV_8U ); cvConvertScale( disparity_left, disparity_left_visual, -16 ); cvSave( "disparity.png", disparity_left_visual ); cvSaveImage( "disparity.jpg", disparity_left_visual ); return 0; } Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано February 3, 2012 Такое ощущение, что у Вас с нормированием результата не все хорошо. Почему так? cvConvertScale( disparity_left, disparity_left_visual, -16 ); Максимальное и минимальное значения, как я понял, хранятся в переменной state. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
himik 2 Жалоба Опубликовано February 4, 2012 Попробовал как вы советовали. cvConvertScale( disparity_left, disparity_left_visual,state->numberOfDisparities); дает не очень хороший результат. Лучше всего получается когда cvConvertScale( disparity_left, disparity_left_visual,1); Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано February 4, 2012 Я просто подумал что Вы раньше такое делали. Надо так (для Mat): double M=0,m=0; //---------------------------------------------------- // Приводим к диапазону 0-1, чтобы было видно картинки //---------------------------------------------------- minMaxLoc(aResultMag,&m,&M); if((M-m)>0) {aResultMag=aResultMag*(1.0/(M-m))-m/(M-m);} [/code] Это для изображений типа CV_32FC1, если для CV_8U, то там нужно вместо единицы 255 поставить. или так для IplImage [code] double min_val,max_val; cvMinMaxLoc( Img, &min_val, &max_val); if(min_val!=max_val) { cvConvertScale( Img, result_img,255.0/(max_val-min_val),-min_val/(max_val-min_val)); } Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
himik 2 Жалоба Опубликовано February 4, 2012 Спасибо. Попробовал сделать все тоже самое с камерами в принципе работает но опять же достаточно плохо int main(array<System::String ^> ^args) { IplImage *image_ = 0; IplImage *image1_ = 0; CvCapture* capture1 = cvCreateCameraCapture(1); CvCapture* capture2 = cvCreateCameraCapture(2); cvSetCaptureProperty(capture1, CV_CAP_PROP_FRAME_WIDTH, 640); cvSetCaptureProperty(capture1, CV_CAP_PROP_FRAME_HEIGHT, 480); cvSetCaptureProperty(capture2, CV_CAP_PROP_FRAME_WIDTH, 640); cvSetCaptureProperty(capture2, CV_CAP_PROP_FRAME_HEIGHT, 480); while(true){ image1_= cvQueryFrame( capture2 ); image_= cvQueryFrame( capture1 ); CvSize size = cvGetSize(image_); IplImage *image_g = cvCreateImage(size, 8,1); IplImage *image1_g = cvCreateImage( size, 8,1); CvMat* disparity_left = cvCreateMat( size.height, size.width, CV_16S ); CvMat* disparity_right = cvCreateMat( size.height, size.width, CV_16S ); cvCvtColor( image_, image_g, CV_BGR2GRAY ); cvCvtColor( image1_, image1_g, CV_BGR2GRAY ); CvStereoBMState* state = cvCreateStereoBMState( 16, 0); cvFindStereoCorrespondenceBM( image_g, image1_g, disparity_left, state ); CvMat* disparity_left_visual = cvCreateMat( size.height, size.width,CV_8U ); cvMinMaxLoc(disparity_left, &min_val, &max_val); if(min_val!=max_val) { cvConvertScale(disparity_left, disparity_left_visual,255.0/(max_val-min_val),-min_val/(max_val-min_val)); } cvShowImage("capture1",image_); cvShowImage("capture2",image1_); cvShowImage("Output",disparity_left_visual); cvReleaseStereoBMState( &state ); cvWaitKey(33); } return 0; } А вот такой получается результат: Подумал может кому будет интересно, и сфотографировал своего если можно так сказать робота, пока он гоняется за зелеными шариками но планируется что он будет их собирать для этого и нужно определять расстояние до объекта. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано February 4, 2012 Метод cvFindStereoCorrespondenceBM требует достаточно кропотливой настройки, чтобы был более менее приемлемый результат, на этом сайте: http://www.directrss.co.il/TextPage_EN.aspx?ID=8523259 есть ссылка на StereoBMTuner-1.0, программа для настройки параметров cvFindStereoCorrespondenceBM, правда она судя по всему, под линукс. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
himik 2 Жалоба Опубликовано February 4, 2012 Ага как я понял проблема в не параллельности камер то есть надо как то их калибровать. Все программы которые там выложены для Linux. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
himik 2 Жалоба Опубликовано February 5, 2012 Так, отлично камеры мне откалибровать удалось на выходе: D1.xml, D2.xml, M1.xml, M2.xml, mx1.xml, mx2.xml, my1.xml, my2.xml, Q.xml. http://blog.martinperis.com/2011/01/opencv-stereo-camera-calibration.html здесь написано что надо использовать cvRemap для того что бы cvFindStereoCorrespondenceBM работал нормально. Результат cvRemap получается вот такой: а карта глубины вот такая: как видите разница небольшая. CvMat *Q = (CvMat *)cvLoad("Q.xml",NULL,NULL,NULL); CvMat *mx1 = (CvMat *)cvLoad("mx1.xml",NULL,NULL,NULL); CvMat *my1 = (CvMat *)cvLoad("my1.xml",NULL,NULL,NULL); CvMat *mx2 = (CvMat *)cvLoad("mx2.xml",NULL,NULL,NULL); CvMat *my2 = (CvMat *)cvLoad("my2.xml",NULL,NULL,NULL); IplImage *image_ = 0; IplImage *image1_ = 0; CvCapture* capture1 = cvCreateCameraCapture(2); CvCapture* capture2 = cvCreateCameraCapture(1); cvSetCaptureProperty(capture1, CV_CAP_PROP_FRAME_WIDTH, 640); cvSetCaptureProperty(capture1, CV_CAP_PROP_FRAME_HEIGHT, 480); cvSetCaptureProperty(capture2, CV_CAP_PROP_FRAME_WIDTH, 640); cvSetCaptureProperty(capture2, CV_CAP_PROP_FRAME_HEIGHT, 480); while(true){ imageLeft= cvQueryFrame( capture2 ); imageRight= cvQueryFrame( capture1 ); CvSize size = cvGetSize(imageRight); IplImage *image_gLeft = cvCreateImage(size, 8,1); IplImage *image_gRight = cvCreateImage( size, 8,1); IplImage *imageTransL= cvCreateImage(size, 8,1); IplImage *imageTransR = cvCreateImage( size, 8,1); IplImage *dstL = cvCreateImage(cvSize(imageLeft->width, imageLeft->height), imageLeft->depth, imageLeft->nChannels); IplImage *dstR = cvCreateImage(cvSize(imageRight->width, imageRight->height), imageRight->depth, imageRight->nChannels); CvMat* disparity_left = cvCreateMat( size.height, size.width, CV_16S ); CvMat* disparity_right = cvCreateMat( size.height, size.width, CV_16S ); cvRemap( imageLeft, dstL, mx1, my1); cvRemap( imageRight, dstR, mx2, my2); cvShowImage("captureRD",dstR); cvShowImage("captureLD",dstL); cvCvtColor( dstL, image_gLeft, CV_BGR2GRAY ); cvCvtColor( dstR, image_gRight, CV_BGR2GRAY ); CvStereoBMState* state = cvCreateStereoBMState( 16, 0); cvFindStereoCorrespondenceBM( image_gRight, image_gLeft, disparity_left, state ); CvMat* disparity_left_visual = cvCreateMat( size.height, size.width,CV_8U ); cvMinMaxLoc(disparity_left, &min_val, &max_val); if(min_val!=max_val) { cvConvertScale(disparity_left, disparity_left_visual,255.0/(max_val-min_val),-min_val/(max_val-min_val)); } cvShowImage("captureL", imageLeft); cvShowImage("captureR",imageRight); cvShowImage("Output",disparity_left_visual); cvReleaseStereoBMState( &state ); } return 0; } 1 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
himik 2 Жалоба Опубликовано February 8, 2012 Вожусь с настройкой cvFindStereoCorrespondenceBM уже несколько дней пока мало что получилось. Вопросы: 1)Какое оптимальное расстояние должно быть между камерами, при 7см у меня работает неплохо но при отдалении от камер больше чем на 2 метра начинает показывать всякую чущь 2) Имеет ли значения какая камера правая а какая левая, при калибровке я указываю правой ту камеру которая будет находится справа для меня если я буду смотреть в объективы камерам, а при непосредственно построении карты глубины я считаю правой ту камеру которая находится слева от меня (если смотреть камерам в объектив) как ни странно так результаты получаются лучше Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
himik 2 Жалоба Опубликовано February 12, 2012 Ага вроде бы строить карту глубины более менее получается. Для того что бы определять расстояния до точки как я понимаю нужно использовать cvReprojectImageTo3D на выходе будет массив точек, каждая из которых задаётся координатами x, y, z, но что то я не пойму как получить z для конкретного пикселя на изображении. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано February 12, 2012 Можно создать изображение, и распихать пиксели по своим местам. А потом брать их оттуда. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
himik 2 Жалоба Опубликовано February 12, 2012 Ну так я сначала создаю изображение: IplImage *_3dImage=cvCreateImage( cvSize(imageLeft->width,imageLeft->height), 32, 3); Затем уже делаю cvReprojectImageTo3D(disparity_left_visual,_3dImage,Q); А как из изображения получить z? Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано February 12, 2012 Так там вроде; B -> X ; G -> Y ; R -> Z; Или я не правильно понял? И еще, x,y экранные не обязательно равны x,y в возвращенном 3D. Поэтому: 1) выясняем максимальное и минимальное значения координат x,y полученных из первых двух каналов 3D изображения. 2) делаем одноканальное изображение соответствующего размера. 3) переносим туда (в координаты x,y из первых двух каналов) а качестве значения пикселей значение третьего канала, то есть z. ЗЫ: масштабирование и смещение координат по вкусу Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
himik 2 Жалоба Опубликовано February 12, 2012 Честно говоря не понял из того что вы написали почти нечего можете подсказать в каком из пунктов какие функции использовать. Вот нашел тут на все том же http://blog.martinperis.com вот такой код: d = pointRightImage.X - pointLeftImage.X; X = pointLeftImage.X * Q[0, 0] + Q[0, 3]; Y = pointLeftImage.Y * Q[1, 1] + Q[1, 3]; Z = Q[2, 3]; W = d * Q[3, 2] + Q[3, 3]; X = X / W; Y = Y / W; Z = Z / W; Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано February 12, 2012 Я имел ввиду вот что: _3dImage - трехканальное изображение. координаты каждого пикселя x,y соответствуют той точке, которую в этих координатах мы видим на экране. этой точке на экране, соответствует точка X,Y,Z в пространстве. Так вот, как я понял из документации, эти X,Y,Z хранятся на местах где должны храниться компоненты R,G,B этой точки. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
himik 2 Жалоба Опубликовано February 12, 2012 То есть что бы узнать Z нужно у точки на изображении (x,y) узнать параметры B, G, R ну вернее только R. Но как это сделать? Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано February 12, 2012 Вроде вот так, переделал из uchar в float, мог где то опечататься. Проход по всем пискелям. float r,g,b; for (int y = 0; y < cvFrame->height; y++) { float *ptr = (float*) (cvFrame_->imageData + y*cvFrame_->widthStep); for (int x = 0; x < cvFrame_->width; x++) { r = ptr[3*x]; g = ptr[3*x + 1]; b = ptr[3*x + 2]; } }[/code] Может быть еще y*cvFrame_->widthStep надо умножить на sizeof(float) Еще вариант: [code]CvScalar cvGet2D(const CvArr*, int row, int col); ЗЫ: только х и y Вам тоже нужно из изображения брать. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
himik 2 Жалоба Опубликовано February 13, 2012 Большое сапасибо. Делаю вот так: float *ptr = (float*) (_3dImage->imageData + 240*_3dImage->widthStep); r =- ptr[3*320]; printf(" %f\n", r-1); Как я понимаю это середина изображения. После нескольких калибровок получились приемлемые значения, но есть несколько непонятных для меня фактов 1) Значение получается отрицательным приходится исправлять 2) Значение на 1 больше действительного то есть если расстояние до объекта 60 см то он покажет 160см, поэтому printf(" %f\n", r-1) 3) Значения выводятся в метрах хотя при калибровке размер квадрата шахматной доски вводился в см, то есть если расстояние до объекта 60см пишет 0.6000 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
himik 2 Жалоба Опубликовано February 14, 2012 Не пойму, как правильно калибровать камеры каждый раз пли калибровании получаются разные результаты. Расстояния всегда получаются разными, лучшее чего удалось достигнуть это погрешность 6см при чем с внесением поправок. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
mrgloom 242 Жалоба Опубликовано February 14, 2012 пожалуй спрошу тут вопрос как если у нас есть видео получить точки в 3D имея 2 соседних кадра? нашел примерно что то такое, но последние 3 пункта непонятны, тем более возможно какие то есть ограничения на фотографии? или надо иметь доп. данные? не очень понятно что в литературе называют "calibrated cameras" если камера одна? это означает что нет зума и т.д. и настройки камеры постоянны? или надо просто знать какие то дополнительные связывающие параметры между изображениями? 1. Calibrate Camera (cv::calibrateCamera) 2. Extract local features (SIFT, SURF) 3. Undistort local feature points with intrinsic camera calibration data (cv::undistortPoints) 4. Find fundamental matrix based on undistorted feature points (cv::findFundamentalMat) 5. Get essential matrix from fundamental matrix 6. Triangulate (cv::cvTriangulatePoints, not documented - where to get parameters?) еще впринципе можно из видео ведь получать панораму, но как её правильно потом распрямлять\проэцировать не понятно. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Smorodov 579 Жалоба Опубликовано February 14, 2012 Я думаю, что первый пункт нужен для третьего. Так как если на изображении существенные нелинейные искажения, то последующее их сопоставление затруднительно и даст большую погрешность. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
mrgloom 242 Жалоба Опубликовано February 15, 2012 да я вообще то первые 3 пункта понимаю зачем и как их делать) да даже эти матрицы получить не сложно, только потом что с ними делать? там еще такой метод рассматривается. cv::stereoRectify to get a disparity map and then using cv::reprojectImageTo3D. disparity map это как я понимаю, то что автор темы пытается получить, но тут вопрос, что если у нас 2 кадра с видеопоследовательности? т.е. маленький угол, можно конечно брать не 2 соседних, но тогда не гарантируется, что там одни и те же объекты, хотя это и так не гарантируется, но тогда наверно можно сцену разбить на несколько просто. 2 himik какие результаты наблюдаются, если начать из точки чтобы камеры стояли вместе, а потом их начать разводить? где лучший результат для disparity map? Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
mrgloom 242 Жалоба Опубликовано February 15, 2012 http://www.google.ru/url?sa=t&rct=j&q=distance%20estimation%20algorithm%20for%20stereo%20pairimages&source=web&cd=1&ved=0CCsQFjAA&url=http%3A%2F%2Fdocs.lib.purdue.edu%2Fcgi%2Fviewcontent.cgi%3Farticle%3D1064%26context%3Decetr&ei=FU07T-OTJ-OI4gShwP34Cg&usg=AFQjCNGnU62kDJ4hLPr3whEore1ybslB6Q&sig2=y5tQOXDn_5N-QQDH2MYQgw по этому документу Distance Estimation Algorithm for Stereo Pair Images расстояние зависит от угла на котором объект находится относительно камер и расстоянию между камерами. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах