OpenCV undistortPoints и triangulatePoint дают странные результаты (стерео)

Я пытаюсь получить 3D-координаты нескольких точек в пространстве, но получаю странные результаты как от undistortPoints(), так и от triangulatePoints().

Поскольку обе камеры имеют разное разрешение, я откалибровал их отдельно, получил среднеквадратичные ошибки 0,34 и 0,43, затем использовал stereoCalibrate() для получения дополнительных матриц, получил среднеквадратичное значение 0,708, а затем использовал stereoRectify() для получения оставшихся матриц. Имея это в руках, я начал работу по сбору координат, но получил странные результаты.

Например, ввод: (935, 262), а вывод undistortPoints()(1228.709125, 342.79841) для одной точки, а для другой — (934, 176) и (1227.9016, 292.4686) соответственно. Что странно, ведь обе эти точки находятся очень близко к середине кадра, где искажения минимальны. Я не ожидал, что это переместит их на 300 пикселей.

При переходе к traingulatePoints() результаты становятся еще более странными - я измерил расстояние между тремя точками в реальной жизни (линейкой) и вычислил расстояние между пикселями на каждой картинке. Поскольку на этот раз точки находились на довольно плоской плоскости, эти две длины (пиксельная и реальная) совпадали, как в |AB|/|BC| в обоих случаях было около 4/9. Тем не менее, triangulatePoints() дает мне результаты с рельсов, с |AB|/|BC| быть 3/2 или 4/2.

Это мой код:

double pointsBok[2] = { bokList[j].toFloat()+xBok/2, bokList[j+1].toFloat()+yBok/2 };
cv::Mat imgPointsBokProper = cv::Mat(1,1, CV_64FC2, pointsBok);

double pointsTyl[2] = { tylList[j].toFloat()+xTyl/2, tylList[j+1].toFloat()+yTyl/2 };
//cv::Mat imgPointsTyl = cv::Mat(2,1, CV_64FC1, pointsTyl);
cv::Mat imgPointsTylProper = cv::Mat(1,1, CV_64FC2, pointsTyl);

cv::undistortPoints(imgPointsBokProper, imgPointsBokProper, 
      intrinsicOne, distCoeffsOne, R1, P1);
cv::undistortPoints(imgPointsTylProper, imgPointsTylProper, 
      intrinsicTwo, distCoeffsTwo, R2, P2);

cv::triangulatePoints(P1, P2, imgWutBok, imgWutTyl, point4D);

double wResult = point4D.at<double>(3,0);
double realX = point4D.at<double>(0,0)/wResult;
double realY = point4D.at<double>(1,0)/wResult;
double realZ = point4D.at<double>(2,0)/wResult;

Углы между точками вроде как хороши, но обычно это не так:

`7,16816    168,389 4,44275` vs `5,85232    170,422 3,72561` (degrees)
`8,44743    166,835 4,71715` vs `12,4064    158,132 9,46158`
`9,34182    165,388 5,26994` vs `19,0785    150,883 10,0389`

Я пытался использовать undistort() для всего кадра, но результаты были такими же странными. Расстояние между точками B и C должно практически не меняться все время, и все же это то, что я получаю:

7502,42     
4876,46 
3230,13 
2740,67 
2239,95 

Покадровый.

Расстояние в пикселях (внизу) и реальное расстояние (вверху) — должно быть очень похоже: |BC| расстояние

Угол:

Угол АВС

Кроме того, разве undistortPoints() и undistort() не должны давать одинаковые результаты (другой набор видео здесь)?
введите здесь описание изображения


person Petersaber    schedule 26.08.2015    source источник
comment
Не могли бы вы включить код, который вы использовали для калибровки, и несколько образцов изображений для части триангуляции?   -  person BConic    schedule 07.09.2015
comment
Это может быть сложно... тут много всего. Завтра постараюсь выкладывать только нужные части   -  person Petersaber    schedule 08.09.2015
comment
Это похоже на испорченную калибровку — если вы используете undistort на изображении только с одной камеры (игнорируя стереокалибровку), это сработает? Вы должны иметь возможность откалибровать внутренние параметры каждой камеры по отдельности и убедиться, что они в порядке, а затем передать эти значения в стереокалибровку в качестве начального предположения, используя флаг CV_CALIB_USE_INTRINSIC_GUESS.   -  person abarry    schedule 11.09.2015
comment
@abarry Что касается калибровки внутренних параметров, я делаю именно это. Я калибрую каждую камеру индивидуально (потому что они имеют разное разрешение и соотношение сторон), а затем я калибрую стереосистему с тем, что получаю от предыдущих калибровок. После калибровки я сразу же применяю undistort() к обоим изображениям, и изображение становится прямым, как стрелка. Я попробовал undistortPoints() и undistort() на одном и том же наборе видео, графики приведены выше.   -  person Petersaber    schedule 12.09.2015
comment
Есть ли обновления по этой проблеме? Была ли это просто проблема с калибровкой или проблема с OpenCV или что-то еще?   -  person ancajic    schedule 16.10.2015
comment
@ancajic Я так и не узнал. Конечный пользователь (исследователи) все еще заканчивает свой предыдущий проект, и все это занимает слишком много времени, чтобы настроить и записать, чтобы я мог попробовать самостоятельно, и мне нужен еще один человек.   -  person Petersaber    schedule 16.10.2015
comment
Вы должны исправить свои изображения (используя stereoRectify) до триангуляции (и после устранения искажений), чтобы соответствующие точки имели одинаковую высоту, иначе карта несоответствий будет неточной.   -  person Azad    schedule 30.10.2015
comment
Вы упомянули stereoRectify, но я не вижу его между undistortPoints и triangulatePoints   -  person Azad    schedule 30.10.2015
comment
@Azad Азад Я никогда не видел, чтобы stereoRectify использовалось между undistortPoints и triangulatePoints? Мне нужно было что-то из него раньше, поэтому я использовал его намного раньше. Я калибрую камеры и сохраняю координаты отдельно   -  person Petersaber    schedule 30.10.2015
comment
@Petersaber После устранения искажений они больше не исправляются, поэтому результаты будут неверными. Для примеров и дальнейшего объяснения вы можете взглянуть на главу 12 «Изучения OpenCV» Гэри Брэдски и Адриана Келера.   -  person Azad    schedule 30.10.2015
comment
@Азад, ага. я должен изучить это   -  person Petersaber    schedule 30.10.2015


Ответы (1)


Функция cv::undistort выполняет неискажение и репроекцию за один раз. Он выполняет следующий перечень операций:

  1. отменить проекцию камеры (умножение на обратную матрицу камеры)
  2. применить модель искажения, чтобы отменить искажение
  3. повернуть на предоставленную матрицу вращения R1/R2
  4. проект указывает на изображение, используя предоставленную матрицу проекции P1/P2

Если вы передаете матрицы R1, P1 соотв. R2, P2 из cv::stereoCalibrate(), входные точки не будут искажены и исправлены. Исправление означает, что изображения преобразуются таким образом, что соответствующие точки имеют одинаковую координату y. Не существует уникального решения для ректификации изображения, так как вы можете применить любое перемещение или масштабирование к обоим изображениям, не изменяя выравнивание соответствующих точек. При этом cv::stereoCalibrate() может немного сместить центр проекции (например, на 300 пикселей). Если вам нужно чистое отсутствие искажений, вы можете передать матрицу идентичности (вместо R1) и исходную матрицу камеры K (вместо P1). Это должно привести к координатам пикселей, похожим на исходные.

person Tobias    schedule 11.11.2016