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

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

Поскольку обе камеры имеют разное разрешение, я их калибровал отдельно, получал RMS-ошибки 0,34 и 0,43, затем использовал stereoCalibrate() для получения большего количества матриц, получил RMS 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 | расстояние

Угол обзора:

Угол ABC

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

Ответ 1

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

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

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