Перейти к содержимому
Compvision.ru
himik

Расстояние до объекта

Recommended Posts

Здравствуйте! Недавно начал разбираться с 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;

} 

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Почитайте здесь:

http://www.compvision.ru/forum/index.php?showtopic=47

И здесь пример в самом конце:

http://www.compvision.ru/forum/index.php?showtopic=251&st=20

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ага, на счет калибровки понятно.

А вот на счет построения карты глубины не очень, если использовать 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;

}

post-4489-0-24957800-1328270114_thumb.jp

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Такое ощущение, что у Вас с нормированием результата не все хорошо.

Почему так?

cvConvertScale( disparity_left, disparity_left_visual, -16 );

Максимальное и минимальное значения, как я понял, хранятся в переменной state.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Попробовал как вы советовали.

cvConvertScale( disparity_left, disparity_left_visual,state->numberOfDisparities);

дает не очень хороший результат.

post-4489-0-25692900-1328347882_thumb.jp

Лучше всего получается когда cvConvertScale( disparity_left, disparity_left_visual,1);

post-4489-0-31262000-1328348026_thumb.jp

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Я просто подумал что Вы раньше такое делали.

Надо так (для 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));
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Спасибо. Попробовал сделать все тоже самое с камерами в принципе работает но опять же достаточно плохо


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;

}

А вот такой получается результат:

post-4489-0-41896200-1328368573_thumb.jp

Подумал может кому будет интересно, и сфотографировал своего если можно так сказать робота,

пока он гоняется за зелеными шариками но планируется что он будет их собирать для этого и нужно определять расстояние до объекта.

post-4489-0-31431400-1328369132_thumb.jp

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Метод cvFindStereoCorrespondenceBM требует достаточно кропотливой настройки, чтобы был более менее приемлемый результат, на этом сайте:

http://www.directrss.co.il/TextPage_EN.aspx?ID=8523259

есть ссылка на StereoBMTuner-1.0, программа для настройки параметров cvFindStereoCorrespondenceBM, правда она судя по всему, под линукс.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ага как я понял проблема в не параллельности камер то есть надо как то их калибровать.

Все программы которые там выложены для Linux.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Так, отлично камеры мне откалибровать удалось на выходе: 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 получается вот такой:

post-4489-0-72780000-1328438561_thumb.jp

а карта глубины вот такая:

post-4489-0-56075100-1328438692_thumb.jp

как видите разница небольшая.


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;

}

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Вожусь с настройкой cvFindStereoCorrespondenceBM уже несколько дней пока мало что получилось.

Вопросы:

1)Какое оптимальное расстояние должно быть между камерами, при 7см у меня работает неплохо но при отдалении от камер больше чем на 2 метра начинает показывать всякую чущь

post-4489-0-06607800-1328710531_thumb.jp

2) Имеет ли значения какая камера правая а какая левая, при калибровке я указываю правой ту камеру которая будет находится справа для меня если я буду смотреть в объективы камерам,

а при непосредственно построении карты глубины я считаю правой ту камеру которая находится слева от меня (если смотреть камерам в объектив) как ни странно так результаты получаются лучше

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ага вроде бы строить карту глубины более менее получается. Для того что бы определять расстояния до точки как я понимаю нужно использовать cvReprojectImageTo3D

на выходе будет массив точек, каждая из которых задаётся координатами x, y, z, но что то я не пойму как получить z для конкретного пикселя на изображении.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Можно создать изображение, и распихать пиксели по своим местам. А потом брать их оттуда.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ну так я сначала создаю изображение:

IplImage *_3dImage=cvCreateImage( cvSize(imageLeft->width,imageLeft->height), 32, 3);
Затем уже делаю
cvReprojectImageTo3D(disparity_left_visual,_3dImage,Q);

А как из изображения получить z?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Так там вроде; B -> X ; G -> Y ; R -> Z;

Или я не правильно понял?

И еще, x,y экранные не обязательно равны x,y в возвращенном 3D.

Поэтому:

1) выясняем максимальное и минимальное значения координат x,y полученных из первых двух каналов 3D изображения.

2) делаем одноканальное изображение соответствующего размера.

3) переносим туда (в координаты x,y из первых двух каналов) а качестве значения пикселей значение третьего канала, то есть z.

ЗЫ: масштабирование и смещение координат по вкусу :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Честно говоря не понял из того что вы написали почти нечего

можете подсказать в каком из пунктов какие функции использовать.

Вот нашел тут на все том же 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;

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Я имел ввиду вот что:

_3dImage - трехканальное изображение.

координаты каждого пикселя x,y соответствуют той точке, которую в этих координатах мы видим на экране.

этой точке на экране, соответствует точка X,Y,Z в пространстве.

Так вот, как я понял из документации, эти X,Y,Z хранятся на местах где должны храниться компоненты R,G,B этой точки.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

То есть что бы узнать Z нужно у точки на изображении (x,y) узнать параметры B, G, R ну вернее только R.

Но как это сделать?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Вроде вот так, переделал из 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 Вам тоже нужно из изображения брать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Большое сапасибо. Делаю вот так:



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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Не пойму, как правильно калибровать камеры каждый раз пли калибровании получаются разные результаты.

Расстояния всегда получаются разными, лучшее чего удалось достигнуть это погрешность 6см при чем с внесением поправок.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

пожалуй спрошу тут вопрос

как если у нас есть видео получить точки в 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?)

еще впринципе можно из видео ведь получать панораму, но как её правильно потом распрямлять\проэцировать не понятно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Я думаю, что первый пункт нужен для третьего. Так как если на изображении существенные нелинейные искажения, то последующее их сопоставление затруднительно и даст большую погрешность.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

да я вообще то первые 3 пункта понимаю зачем и как их делать)

да даже эти матрицы получить не сложно, только потом что с ними делать?

там еще такой метод рассматривается.

cv::stereoRectify to get a disparity map

and then using cv::reprojectImageTo3D.

disparity map это как я понимаю, то что автор темы пытается получить, но тут вопрос, что если у нас 2 кадра с видеопоследовательности? т.е. маленький угол, можно конечно брать не 2 соседних, но тогда не гарантируется, что там одни и те же объекты, хотя это и так не гарантируется, но тогда наверно можно сцену разбить на несколько просто.

2 himik

какие результаты наблюдаются, если начать из точки чтобы камеры стояли вместе, а потом их начать разводить?

где лучший результат для disparity map?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

расстояние зависит от угла на котором объект находится относительно камер и расстоянию между камерами.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте учётную запись или войдите для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать учётную запись

Зарегистрируйтесь для создания учётной записи. Это просто!

Зарегистрировать учётную запись

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×