Как правильно использовать cv::triangulatePoints()

Я пытаюсь триангулировать некоторые моменты с OpenCV, и я нашел это cv::triangulatePoints() функция. Проблема в том, что там почти нет документации или примеров.

У меня есть некоторые сомнения по этому поводу.

  1. Какой метод он использует? Я провел небольшое исследование о триангуляции, и есть несколько методов (Линейный, Линейный LS, Собственный, итерационный LS, Итерационный Собственный,...), но я не могу найти, какой из них он использует в OpenCV.

  2. Как я должен использовать это? Похоже, что в качестве входных данных ему нужна матрица проекции и 3xN однородных 2D точек. Я их определил как std::vector<cv::Point3d> pnts, но в качестве выхода ему нужны массивы 4xN и, очевидно, я не могу создать std::vector<cv::Point4d> потому что он не существует, так как мне определить выходной вектор?

На второй вопрос я попробовал: cv::Mat pnts3D(4,N,CV_64F); а также cv::Mat pnts3d;, ни один, кажется, не работает (это исключение).

6 ответов

Решение

1.- Используется метод наименьших квадратов. Есть более сложные алгоритмы, чем этот. Тем не менее, он является наиболее распространенным, так как другие методы в некоторых случаях могут потерпеть неудачу (т. Е. Некоторые другие потерпят неудачу, если точки находятся на плоскости или на бесконечности).

Этот метод может быть найден в " Геометрии множественного обзора в компьютерном зрении " Ричарда Хартли и Эндрю Циссермана (p312)

2.-Использование:

cv::Mat pnts3D(1,N,CV_64FC4);
cv::Mat cam0pnts(1,N,CV_64FC2);
cv::Mat cam1pnts(1,N,CV_64FC2);

Заполните 2 точечные матрицы каналов с точками на изображениях.

cam0 а также cam1 являются Mat3x4 матрицы камеры (внутренние и внешние параметры). Вы можете построить их, умножив A*RT, где A - матрица внутренних параметров, а RT - матрица поз 3x4 с поворотом.

cv::triangulatePoints(cam0,cam1,cam0pnts,cam1pnts,pnts3D);

ПРИМЕЧАНИЕ: pnts3D НУЖНО быть 4-х канальным 1xN cv::Mat если определено, выдает исключение, если нет, но результатом является cv::Mat(4,N,cv_64FC1) матрица. Действительно сбивает с толку, но это единственный способ, которым я не получил исключение.


ОБНОВЛЕНИЕ: Начиная с версии 3.0 или, возможно, ранее, это больше не так, и pnts3D также может быть типа Mat(4,N,CV_64FC1) или может быть оставлен полностью пустым (как обычно, он создается внутри функции).

Небольшое дополнение к ответу Андер Бигури. Вы должны получить ваши очки изображения наundistortредактировать изображение и вызывать undistortPoints() на cam0pnts а также cam1pnts, так как cv::triangulatePoints ожидает 2D точки в нормализованных координатах (независимо от камеры) и cam0 а также cam1 должно быть только [R|t^T] матриц, вам не нужно умножать его на A.

Спасибо Андеру Бигури! Его ответ мне очень помог. Но я всегда предпочитаю альтернативу с std::vector, я отредактировал его решение так:

std::vector<cv::Point2d> cam0pnts;
std::vector<cv::Point2d> cam1pnts;
// You fill them, both with the same size...

// You can pick any of the following 2 (your choice)
// cv::Mat pnts3D(1,cam0pnts.size(),CV_64FC4);
cv::Mat pnts3D(4,cam0pnts.size(),CV_64F);

cv::triangulatePoints(cam0,cam1,cam0pnts,cam1pnts,pnts3D);

Так что вам просто нужно сделать emplace_back в точках. Основное преимущество: вам не нужно знать размер N прежде чем начинать их заполнять. К сожалению, нет cv::Point4f, поэтому pnts3D должен быть cv::Mat...

Я пробовал cv::triangulatePoints, но каким-то образом он вычисляет мусор. Я был вынужден реализовать метод линейной триангуляции вручную, который возвращает матрицу 4x1 для триангулированной трехмерной точки:

Mat triangulate_Linear_LS(Mat mat_P_l, Mat mat_P_r, Mat warped_back_l, Mat warped_back_r)
{
    Mat A(4,3,CV_64FC1), b(4,1,CV_64FC1), X(3,1,CV_64FC1), X_homogeneous(4,1,CV_64FC1), W(1,1,CV_64FC1);
    W.at<double>(0,0) = 1.0;
    A.at<double>(0,0) = (warped_back_l.at<double>(0,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,0) - mat_P_l.at<double>(0,0);
    A.at<double>(0,1) = (warped_back_l.at<double>(0,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,1) - mat_P_l.at<double>(0,1);
    A.at<double>(0,2) = (warped_back_l.at<double>(0,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,2) - mat_P_l.at<double>(0,2);
    A.at<double>(1,0) = (warped_back_l.at<double>(1,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,0) - mat_P_l.at<double>(1,0);
    A.at<double>(1,1) = (warped_back_l.at<double>(1,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,1) - mat_P_l.at<double>(1,1);
    A.at<double>(1,2) = (warped_back_l.at<double>(1,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,2) - mat_P_l.at<double>(1,2);
    A.at<double>(2,0) = (warped_back_r.at<double>(0,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,0) - mat_P_r.at<double>(0,0);
    A.at<double>(2,1) = (warped_back_r.at<double>(0,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,1) - mat_P_r.at<double>(0,1);
    A.at<double>(2,2) = (warped_back_r.at<double>(0,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,2) - mat_P_r.at<double>(0,2);
    A.at<double>(3,0) = (warped_back_r.at<double>(1,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,0) - mat_P_r.at<double>(1,0);
    A.at<double>(3,1) = (warped_back_r.at<double>(1,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,1) - mat_P_r.at<double>(1,1);
    A.at<double>(3,2) = (warped_back_r.at<double>(1,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,2) - mat_P_r.at<double>(1,2);
    b.at<double>(0,0) = -((warped_back_l.at<double>(0,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,3) - mat_P_l.at<double>(0,3));
    b.at<double>(1,0) = -((warped_back_l.at<double>(1,0)/warped_back_l.at<double>(2,0))*mat_P_l.at<double>(2,3) - mat_P_l.at<double>(1,3));
    b.at<double>(2,0) = -((warped_back_r.at<double>(0,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,3) - mat_P_r.at<double>(0,3));
    b.at<double>(3,0) = -((warped_back_r.at<double>(1,0)/warped_back_r.at<double>(2,0))*mat_P_r.at<double>(2,3) - mat_P_r.at<double>(1,3));
    solve(A,b,X,DECOMP_SVD);
    vconcat(X,W,X_homogeneous);
    return X_homogeneous;
}

входными параметрами являются две матрицы проекции камеры 3x4 и соответствующая пара левого / правого пикселей (x,y,w).

В качестве альтернативы вы можете использовать метод Хартли и Циссермана, реализованный здесь: http://www.morethantechnical.com/2012/01/04/simple-triangulation-with-opencv-from-harley-zisserman-w-code/

В дополнение к комментариям Хинес Идальго,

если бы вы сделали стереокалибровку и могли бы точно оценить Фундаментальную матрицу оттуда, которая была рассчитана на основе шахматной доски.

Используйте функцию correctMatches для уточнения обнаруженных ключевых точек

      std::vector<cv::Point2f> pt_set1_pt_c, pt_set2_pt_c;
cv::correctMatches(F,pt_set1_pt,pt_set2_pt,pt_set1_pt_c,pt_set2_pt_c)
Другие вопросы по тегам