Положение камеры в мировых координатах из cv :: resolvePnP

У меня есть откалиброванная камера (собственная матрица и коэффициенты искажения), и я хочу знать положение камеры, зная некоторые трехмерные точки и соответствующие им точки на изображении (2d точки).

Я знаю, что cv::solvePnP может мне помочь, и после прочтения this и < a href = "qaru tvec - вращение и перемещение объекта в системе координат камеры.

Поэтому мне нужно узнать вращение / перемещение камеры в мировой системе координат.

Из приведенных выше ссылок кажется, что код на python прост:

found,rvec,tvec = cv2.solvePnP(object_3d_points, object_2d_points, camera_matrix, dist_coefs)
rotM = cv2.Rodrigues(rvec)[0]
cameraPosition = -np.matrix(rotM).T * np.matrix(tvec)

Я не знаю, что такое python / numpy (я использую C ++), но для меня это не имеет большого смысла:

  • rvec, tvec выходные данные от решенияPnP - это матрица 3x1, 3-элементные векторы
  • cv2.Rodrigues (rvec) - это матрица 3x3
  • cv2.Rodrigues (rvec) [0] - это матрица 3x1, 3-элементные векторы
  • cameraPosition - это матрица умножения 3x1 * 1x3, которая является матрицей .. 3x3. как я могу использовать это в opengl с простыми вызовами glTranslatef и glRotate?

person nkint    schedule 05.09.2013    source источник


Ответы (2)


Если под «мировыми координатами» вы имеете в виду «координаты объекта», вы должны получить обратное преобразование результата, даваемого алгоритмом pnp.

Есть уловка для инвертирования матриц преобразования, которая позволяет вам сохранить операцию инверсии, которая обычно является дорогостоящей и объясняет код на Python. Учитывая преобразование [R|t], у нас есть это inv([R|t]) = [R'|-R'*t], где R' - это транспонирование R. Итак, вы можете кодировать (не проверено):

cv::Mat rvec, tvec;
solvePnP(..., rvec, tvec, ...);
// rvec is 3x1, tvec is 3x1

cv::Mat R;
cv::Rodrigues(rvec, R); // R is 3x3

R = R.t();  // rotation of inverse
tvec = -R * tvec; // translation of inverse

cv::Mat T = cv::Mat::eye(4, 4, R.type()); // T is 4x4
T( cv::Range(0,3), cv::Range(0,3) ) = R * 1; // copies R into T
T( cv::Range(0,3), cv::Range(3,4) ) = tvec * 1; // copies tvec into T

// T is a 4x4 matrix with the pose of the camera in the object frame

Обновление: Позже, чтобы использовать T с OpenGL, вы должны иметь в виду, что оси кадра камеры различаются между OpenCV и OpenGL.

OpenCV использует ссылку, обычно используемую в компьютерном зрении: X указывает вправо, Y вниз, Z вперед (как в это изображение). Рамка камеры в OpenGL: X указывает вправо, Y вверх, Z назад (как в левой части это изображение). Итак, вам нужно применить поворот вокруг оси X на 180 градусов. Формула этой матрицы вращения находится в википедии.

// T is your 4x4 matrix in the OpenCV frame
cv::Mat RotX = ...; // 4x4 matrix with a 180 deg rotation around X
cv::Mat Tgl = T * RotX; // OpenGL camera in the object frame

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

Наконец, учтите, что матрицы в OpenCV хранятся в памяти в порядке строк, а матрицы OpenGL - в порядке столбцов.

person ChronoTrigger    schedule 05.09.2013
comment
похоже, работает, я получаю углы для glRotatef с формулой, взятой из этого: euclideanspace.com/maths/geometry/rotations/conversions/, а затем обычное преобразование радиалов в градусы. Но если я вставлю эти значения в opengl, я все равно получу неправильный поворот камеры (поворот X примерно на 45 ° неправильный) и немного неправильный перевод .. - person nkint; 06.09.2013
comment
Это может быть связано с тем, что кадр камеры в OpenCV и OpenGL отличается. Проверьте мой расширенный ответ. - person ChronoTrigger; 06.09.2013
comment
да, я знаю разницу в порядке расположения матриц в памяти между opencv и opengl. И мне также нужно перевернуть оси y и z (= ›используйте opencv y как opengl z и используйте opencv z как opengl y) - person nkint; 06.09.2013
comment
почти рядом! результат согласуется с несколькими испытаниями. Мне кажется, что есть ошибка 45 ° по оси X (это может быть некоторая разница в кадре между opencv и opengl, которую я не понимаю) и 10 ° по углу y (что я не знаю, как интерпретировать) - person nkint; 06.09.2013
comment
Ты уверен, что топоры такие, как ты говоришь? Я думаю, что они такие же, как в моем примере, но могу ошибаться. Также проверьте, совпадают ли параметры проекции камеры в opencv и opengl. - person ChronoTrigger; 07.09.2013
comment
хорошо, это была ошибка где-то еще. Мне нужно rotateY (180) и rotateZ (180), я точно не понимаю, почему, но, похоже, это работает очень хорошо. Большое спасибо за исчерпывающий ответ! - person nkint; 10.09.2013
comment
(в любом случае, похоже, он работает хорошо, но я ищу способ численно протестировать его, если у вас есть предложения, добро пожаловать!) - person nkint; 10.09.2013
comment
@ChronoTrigger знаете ли вы, где центрируется рамка изображения OpenCV? В центре изображения или в верхнем левом углу? Когда вы даете 2-мерные точки изображения для решенияPnp, связаны ли эти 2-мерные координаты с центром изображения или с верхним левым углом? - person manatttta; 10.04.2015
comment
@manatttta Используйте координаты изображения, начиная с верхнего левого угла, потому что калибровка камеры уже содержит координаты оптического центра. Вы можете использовать любую систему отсчета, кроме верхнего левого угла, соответствующим образом изменив параметры калибровки. - person ChronoTrigger; 10.04.2015
comment
У меня работает не поворот камеры на 180 градусов, но я повернул всю сцену вокруг оси X и оставил матрицу положения камеры нетронутой. - person TonyB; 23.03.2020

Если вы хотите превратить его в стандартную матрицу позы 4x4, указав положение вашей камеры. Используйте rotM как верхний левый квадрат 3x3, tvec как 3 элемента справа и 0,0,0,1 как нижнюю строку

pose = [rotation   tvec(0)
        matrix     tvec(1)
        here       tvec(2)
        0  , 0, 0,  1]

затем переверните его (чтобы получить позу камеры вместо позы мира)

person Hammer    schedule 05.09.2013