RinOS 16 Жалоба Опубликовано February 10, 2011 Всем привет! Решил заняться стерео зрением. Нашел в сети такой проект на OpenCV: http://code.google.com/p/opencvstereovision/ даже удалось скомпилить его) exe во вложении. Поигрался с программой пытался калибровать шахматной доской но результат мягко говоря неочень... Раньше ни когда этим не занимался сразу возникло несколько вопросов. Какое расстояние должно быть между камерами? Как они должны быть направлены? (какой угол примерно) (ехе требует библиотеки OpenCV 1.1 и библиотеки Qt если в сети не найдете выложу )OpenCV-Qt-StereoCalibration.rar 2 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
Nuzhny 243 Жалоба Опубликовано February 10, 2011 Присоединяюсь к вопросу. 1 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
RinOS 16 Жалоба Опубликовано February 25, 2011 Читая статьи на эту тему и форумы выяснил что 1. Оси камер должны быть параллельны 2. Расстояние между камерами 10-15 см. 3. Калибровка очень, и очень важна (правда ни чего не нашел о том как правильно калибровать...) К примеру беру шахматную доску, пытаюсь калибровать... и каждый раз разный результат... Кто знает как правильно калибровать)? Доска для калибровки во вложении.ChessBoard Stereo Calibration.pdf 1 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
RinOS 16 Жалоба Опубликовано March 4, 2011 Допустим как то я как то скалибровал и получил disparityImage, карта глубины конечно не ахти, впрочем сойдет... Карта глубины как я понял это не реальные числа глубины, а разности между слоями... (так ли)? И что бы получить расстояние до объекта, надо преобразовать координаты. Если я правильно понял то для этого используется функция cvReprojectImageTo3D() (так ли)? В которой: первый параметр disparityImage который нам создал cvFindStereoCorrespondenceBM() второй параметр IplImage (size, IPL_DEPTH_32F, 3) третий параметр самый интересный... матрица СvMat(4,4, CV_64F), в доке написано что: (You’re going to need the reprojection matrix (named Q) from the cvStereoRectify method.) Дело в том что в этой программе, используется cvStereoRectifyUncalibrated, а там ни какого Q, нет... откуда бы взять эту матрицу Q? И вообще правильный ли ход мыслей, для получения реальных координат, из disparityImage? Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
executor 5 Жалоба Опубликовано March 4, 2011 Здравствуйте, RinOS. Рекомендую все-таки использовать cvStereoRectify() т.к. результат его работы более точен, а cvStereoRectifyUncalibrated() лучше использовать когда существуют значительные непараллельности в оптических осях камер, насколько я понимаю. Критерием правильности калибровки стерео может служить правильное число в векторе T (который означает расстояние между оптическими осями по оси x, y и z) камер. У меня, при расстоянии между камерами 12 см, и 29 калибровочных пар изображений 640х480 в оттенках серого (изображения я сохраняю предварительно в bmp, чтобы каждый раз не мучаться с их показом камерам) величина составляет: цитирую xml содержимое <data>-1.1886876922892217e-001 -7.8263643755714435e-004 -4.6620003758508491e-003</data>, (все величины в метрах - первая величина - это сдвиг по оси X, то есть расстояние между камерами). То есть 1.6 %, что может быть точнее измерянного мною расстояния. Чем шире расстояние между камерами, тем лучше будет восприятие на более дальних расстояниях, и тем хуже будет поле зрения камеры, при обзоре близких предметов. Для того чтобы величина вектора T содержала метрические величины, необходимо, чтобы вы правильно (в метрических единицах) указали размер клеточки при калибровке. В книге learning OpenCV, в примере стерео (стр 445), есть константа squareSize, у меня в коде примерно так (размер клетки 3 см): float chesbSquareSize = 0.030f; // 30 mm is a Square size for (size_t pair_idx = 0; pair_idx < boards_count; pair_idx++) { for (size_t i=pair_idx*board_n,j = 0;j < board_n; ++i,++j) { // Chessboard points CV_MAT_ELEM(*image_pointsL,float,i,0) = vCalibData[IMG_LEFT][pair_idx].vPoints[j].x; CV_MAT_ELEM(*image_pointsL,float,i,1) = vCalibData[IMG_LEFT][pair_idx].vPoints[j].y; CV_MAT_ELEM(*image_pointsR,float,i,0) = vCalibData[IMG_RIGHT][pair_idx].vPoints[j].x; CV_MAT_ELEM(*image_pointsR,float,i,1) = vCalibData[IMG_RIGHT][pair_idx].vPoints[j].y; // Linear space remapping points CV_MAT_ELEM(*object_points,float,i,0) = (float)(j/w) * chesbSquareSize; CV_MAT_ELEM(*object_points,float,i,1) = (float)(j%w) * chesbSquareSize; CV_MAT_ELEM(*object_points,float,i,2) = 0; }; CV_MAT_ELEM(*point_counts,UINT,pair_idx,0) = (UINT)board_n; }; Откалиброванные матрицы удобно сохранять в xml. Для того, чтобы калибровка была корректной, рекомендуют использовать "шахматную доску" с не одинаковым кол-вом клеток по ширине и высоте (я заметил вы такую и использовали). Для улучшения качества BlockMatcher'а, попробуйте BMState->uniquenessRatio = 0; Привожу мои рабочие параметры: BMState->preFilterSize = 17; BMState->preFilterCap = 31; BMState->SADWindowSize = 11; BMState->minDisparity = 13; BMState->numberOfDisparities = 256; BMState->textureThreshold = 35; BMState->uniquenessRatio = 0; Искомую матрицу репроекции Q вы можете заполнить вручную по формуле Q= стр 435 Learn OpenCV, используя ранее вычисленные матрицы: CameraMatrix и T, например.. но мне кажется это сложный путь, и ... некоторые величины (f, n) придется выдумывать. Вот набросал матрицу, которую вы можете использовать, подставив лишь расстояние между объективами камер: Но я не уверен, что она даст для вас точный и качественный результат. f можно уменьшить вовсе до 5 метров. Если вы вызовете cvStereoRectify() то наверняка ничего не потеряете, по книге инных вариантов не приводится. Матрицу Q нельзя извлечь из cvStereoRectifyUncalibrated() поскольку (стр 431): The advantage of Hartley’s algorithm is that online stereo calibration can be performed simply by observing points in the scene. The disadvantage is that we have no sense of image scale. For example, if we used a chessboard for generating point matches then we would not be able to tell if the chessboard were 100 meters on each side and far away or 100 centimeters on each side and nearby. Neither do we explicitly learn the intrinsic camera matrix, without which the cameras might have dif erent focal lengths, skewed pixels, dif erent centers of projection, and/or dif erent principal points. As a result, we can determine 3D object reconstruction only up to a projective transform. What this means is that dif erent scales or projections of an object can appear the same to us (i.e., the feature points have the same 2D coordinates even though the 3D objects differ). То есть, теоретически, если вы при калибровке, задавали реальные метрические координаты (с помощью размера) ваших клеточек, то на выходе ReprojectTo3D проекции получите реальные размеры и расстояния до объекта в метрах. Насколько я понимаю, disparityImage должен быть Float(существует быстрый вариант с Int). Подавать нужно disparityImage не нормализованный. Репроекцией еще сам не занимался, поэтому грабли раскрыть в этой теме пока не могу (может на этих выходных, тогда я бы отписался здесь). Прикрепляю свою шахматную доску (visio), может пригодится. 3 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
executor 5 Жалоба Опубликовано March 6, 2011 При подаче в функцию cvInitUndistortRectifyMap (вы же ректифицируете изображение?), четвертый параметр - матрица NewCameraMatrix для правой камеры, т.е. "M2". Она содержит все недостающие величины. Я так понимаю, в вашем примере, эта матрица тоже рассчитывается подобно тому как в книге (в коде на примере) стр 450 внизу (после "OR ELSE HARTLEY'S METHOD, ... , compute the rectification transformation directly ... "). Вот как выглядит матрица Q у меня. (моя предыдущая теоретическая имела C1 и C2 равные 0). У меня f = 5.84*10^2, C1 = 3.11*10^2, C2 = 2.41*10^2, Tx = 0.118 Вот написал функцию, которая принимает матрицу 4 параметра cvInitUndistortRectifyMap и расстояние между камерами в метрах, и возвращает double_1channel матрицу Q (возвращаемая ею матрица у меня полностью совпала с откалиброванной Q из StereoCalibrate() по выводу в xml). // vM2_right - та же что и 4 параметр cvInitUndistortRectifyMap для правой камеры // т.е. new_camera_matrix правой камеры CvMat * CreateArtificialMatQ(CvMat *vM2_right, double Tx) { // находим нужные величины из матрицы правой камеры vM2 double f = CV_MAT_ELEM(*vM2_right,double,0,0); double C1 = CV_MAT_ELEM(*vM2_right,double,0,2); double C2 = CV_MAT_ELEM(*vM2_right,double,1,2); // заполняем правильно элементы матрицы CvMat *MatQ_artificial = cvCreateMat(4,4,CV_64FC1); cvZero(MatQ_artificial); CV_MAT_ELEM(*MatQ_artificial,double,0,0) = 1; CV_MAT_ELEM(*MatQ_artificial,double,1,1) = 1; CV_MAT_ELEM(*MatQ_artificial,double,0,3) = -C1; CV_MAT_ELEM(*MatQ_artificial,double,1,3) = -C2; CV_MAT_ELEM(*MatQ_artificial,double,2,3) = f; CV_MAT_ELEM(*MatQ_artificial,double,3,2) = -1.0/Tx; return MatQ_artificial; } Но я не могу пока-что проверить будет ли она правильно работать на cvStereoRectifyUncalibrated. 1 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
RinOS 16 Жалоба Опубликовано March 9, 2011 Рекомендую все-таки использовать cvStereoRectify() т.к. результат его работы более точен, а cvStereoRectifyUncalibrated() лучше использовать когда существуют значительные непараллельности в оптических осях камер, насколько я понимаю. Смотрел пример stereo_calib.cpp, там показаны оба метода cvStereoRectify и cvStereoRectifyUncalibrated, мне показалось что cvStereoRectifyUncalibrated, работает лучше... по крайней мере карта высот выглядит правдоподобнее. Искомую матрицу репроекции Q вы можете заполнить вручную по формуле Q= стр 435 Learn OpenCV, используя ранее вычисленные матрицы: CameraMatrix и T, например.. но мне кажется это сложный путь, и ... некоторые величины (f, n) придется выдумывать. У меня есть модуль (см. вложение) который вроде как строит Q, по некоторым параметрам: * \brief Constructor that takes camera parameters and parameterizes the object. * The object is ready to be used after constructed by this constructor. * @param Fx - focal length in x direction of the rectified image in pixels. * @param Fy - focal length in y direction of the rectified image in pixels. * @param Tx - Translation in x direction from the left camera to the right camera. * @param Clx - x coordinate of the optical center of the left camera * @param Crx - x coordinate of the optical center of the right camera * @param Cy - y coordinate of the optical center of both left and right camera * @param dispUnitScale - the unit of the value in a disparity map. e.g. 1/4 of a pixel. */ Правда не известно откуда брать точные значения этих параметров... При подаче в функцию cvInitUndistortRectifyMap (вы же ректифицируете изображение?) Да, вы можете посмотреть исходник с которым я работаю, ссылка в первом посте. Вот написал функцию, которая принимает матрицу 4 параметра cvInitUndistortRectifyMap Я попробовал использовать ее вот что получилось: Q возвращенное функцией cvStereoRectify: [ 1., 0., 0., -179.1385765075683600, 0., 1., 0., -120.1025462150573700, 0., 0., 0., 350.7473711626223000, 0., 0., -24.8326591292256540, -538.7927019658170600 ] Q полученное с помощью cvStereoRectifyUncalibrated и вашей функции: [ 1., 0., 0., -150.7913341775544300, 0., 1., 0., -102.6185674775258400, 0., 0., 0., 352.8618670197555500, 0., 0., -25.9740261147689360, 0. ] Пытался много раз калибровать, Q иногда больше похожи иногда меньше в среднем как-то так.stereo_utils.rar 2 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
RinOS 16 Жалоба Опубликовано March 14, 2011 Вызывал я функцию cvReprojectImageTo3D с разным Q, и пришел к выводу что она все-таки работает (карта высот довольно реалистичная в MatLab-e). Функция cvReprojectImageTo3D, возвращает 3х канальное изображение 3 канал, глубина - расстояние до объекта. Так я получаю расстояние cvReprojectImageTo3D(imageDepth, 3dImage, Q); cvSetImageCOI(3dImage, 3); cvCopy(3dImage, Zimg, 0); dist = cvGetReal2D(Zimg, 160, 120); Но вот в чем прикол... Если объект дальше то и значение dist должно быть больше, а происходит ровно наоборот... Чем ближе объект, тем больше значение dist. К примеру замерял расстояние от камеры до себя, вот что получил: расстояние 60 см, dist равен 0,117 расстояние 70 см, dist равен 0,093 расстояние 80 см, dist равен 0,079 расстояние 90 см, dist равен 0,661 расстояние 100 см, dist равен 0,580 Где то не вяжется... Или я чего то не понимаю... Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
RinOS 16 Жалоба Опубликовано April 8, 2011 На одном китайском сайте, нашел пример вывода в 3d, disparity image Для лучшего эффекта предварительно нормализуем изображение imageDepthNormalized = cvCreateMat( imageSize.height,imageSize.width, CV_8U ); cvScale(imageDepth, imageDepthNormalized, 1, 256); Программа, исходник и картинка во вложении, картинку берет из "c:\z.bmp"glut3d.rar 2 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
quosego 5 Жалоба Опубликовано May 15, 2011 Если объект дальше то и значение dist должно быть больше, а происходит ровно наоборот...Чем ближе объект, тем больше значение dist. Тоже не могу получить реальные координаты. Модуль значения disp растет в соответствии с дистанцией, но не адекватно. Z=fT/d, T-расстояние между камерами, d - диспаритет, значит ошибка в f? 1 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
quosego 5 Жалоба Опубликовано June 4, 2011 Ввел в формулу поправочный коэффициент a, который вычислил экспериментально: z=afT/d. Теперь дистанция определяется верно, но в чем проблема так и не понял. Скрепя сердцем оставил пока этот костыль. Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
ssovec 1 Жалоба Опубликовано November 1, 2012 Подниму старую тему но все же, пытаюсь реализовать код представленный в посте конкретно: cvReprojectImageTo3D(imageDepth, 3dImage, Q); cvSetImageCOI(3dImage, 3); cvCopy(3dImage, Zimg, 0); dist = cvGetReal2D(Zimg, 160, 120); opencv ругается что GetReal пожддерживает только однокональные массивы, а теперь вопрос если 3dImage трехканальное то как оно использованно в CvGetReal? и еще не сочтите за наглость откуда взяты данные для графиков представленных в посте? Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
zloibobig 4 Жалоба Опубликовано November 2, 2012 Подниму старую тему но все же, пытаюсь реализовать код представленный в посте конкретно: cvReprojectImageTo3D(imageDepth, 3dImage, Q); cvSetImageCOI(3dImage, 3); cvCopy(3dImage, Zimg, 0); dist = cvGetReal2D(Zimg, 160, 120); opencv ругается что GetReal пожддерживает только однокональные массивы, а теперь вопрос если 3dImage трехканальное то как оно использованно в CvGetReal? и еще не сочтите за наглость откуда взяты данные для графиков представленных в посте? Никогда не пользовался С-интерфейсом, но приведу часть кода на С++ из моего проекта pcl::PointCloud<pcl::PointXYZRGB>::Ptr BaseStereoMatcher::pointCloud(const cv::Mat &disp, const cv::Mat &image, const CalibrationParams &calib_params) { Q_ASSERT((image.rows != 0) && (image.cols != 0) && (image.rows == disp.rows) && (image.cols == disp.cols) && (image.type() == CV_8UC3)); pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>); cv::Matx44f Q = calib_params.DisparityToDepthMatrix; cv::Matx41f reproj_point; for (int v = 0; v < disp.rows; v++) { for (int u = 0; u < disp.cols; u++) { float d; if (disp.type() == CV_16S) d = disp.at<short>(v, u) / 16.0f; if (disp.type() == CV_32F) d = disp.at<float>(v, u); if (d <= FLT_EPSILON) { // point.x = point.y = point.z = bad_point; continue; } pcl::PointXYZRGB point; cv::Matx41f uv_point(u,v,d,1); //диспарантность в однородных координатах reproj_point = Q * uv_point; //так осуществляется проекция в однородные 3д координаты float w = reproj_point.val[3]; point.x = -reproj_point.val[0] / w; point.y = -reproj_point.val[1] / w; point.z = reproj_point.val[2] / w; // point - готовая точка в 3д cv::Vec3b pix = image.at<cv::Vec3b>(v, u); uint8_t r = pix[2], g = pix[1], b = pix[0]; uint32_t rgb = (static_cast<uint32_t>(r) << 16 | static_cast<uint32_t>(g) << 8 | static_cast<uint32_t>(); point.rgb = *reinterpret_cast<float*>(&rgb); cloud->push_back(point); } } return cloud; } [/code] Тоже самое выполняется в функции ReprojectTo3D (откомментированные строчки), так что можешь врунчую заполнить матрицу с 3д координатами. И, кстати, в матрице диспарантности все ее величины умножены на 16, поэтому прежде чем ее использовать, нужно значение диспарантности поделить на 16, выше в коде ты можешь это увидеть. 2 Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах
ssovec 1 Жалоба Опубликовано November 2, 2012 Спасибо большое! Поделиться сообщением Ссылка на сообщение Поделиться на других сайтах