OpenCV: странные матрицы вращения и перевода от decomposeHomographyMat

Я пытаюсь решить проблему проекции plane2plane, используя функцию findHomography, найденную в библиотеке OpenCV. В качестве игрушечного примера у меня есть набор точек в R2, P, и второй набор точек в R2, Q, где Qx = Px+50, Qy = Py. Это означает, что я сместил x-координаты на 50. Теперь я запускаю следующий код:

Mat projectionMatrix = findHomography(Q, P);
vector<Point2f> projectedPoints(objectCoordinates.size());
perspectiveTransform(Q, projectedPoints, projectionMatrix);

И это дает мне P, и это здорово. Однако сейчас хотелось бы матрицы поворота и перевода, R&T, вот тут-то я и запутался. В OpenCV 3 есть функция decomposeHomographyMat, которая возвращает до 4 решений для R и T (также возвращает нормали, но я их не сохраняю). Я запускаю его так:

vector<Mat> Rs;
vector<Mat> Ts;
decomposeHomographyMat(projectionMatrix, cameraMatrix, Rs, Ts, noArray());

cameraMatrix, который я использую, испытан на основе предыдущих экспериментов. Итак, я получаю свои четыре результата. Глядя на них, я замечаю, что в результате получаю единичную матрицу для всех R, и это здорово. Однако все векторы трансляции равны [0,0,0]T, тогда как я ожидаю, что хотя бы один из них будет [-50,0,0]T. Есть ли что-то, что мне нужно сделать с результатом decomposeHomographyMat, чтобы получить ожидаемое поведение?

Спасибо


person IlliteratePhD    schedule 11.03.2016    source источник
comment
Вы проверили, что вычисленная гомография не равна нулю?   -  person SpamBot    schedule 11.03.2016
comment
@SpamBot Да, вычисленная гомография (с небольшим округлением): [ 1 0 -50 ; 0 1 0 ; 0 0 1].   -  person IlliteratePhD    schedule 11.03.2016
comment
Это всего лишь предположение, может быть, decomposeHomographyMat не обрабатывает частный случай аффинной гомографии?   -  person SpamBot    schedule 11.03.2016


Ответы (2)


Оказывается, я был не прав в некоторых моментах, поэтому я решил переписать этот ответ.

Короче говоря, вы получаете странные результаты из-за неправильной матрицы внутренних параметров.

Используя терминологию из статьи «Малис, Э. и Варгас, М., «Углубленное понимание гомографической декомпозиции для управления на основе зрения» (на которой основана гомографическая декомпозиция в OpenCV), перспективное преобразование обозначается H и называется евклидовой матрицей гомографии, а результат ее нормализации G = K^-1 * H< /strong> * K (где K — калибровочная матрица камеры) называется матрицей гомографии

И cv::findHomography(), и cv::decomposeHomographyMat() работают с евклидовой матрицей гомографии. Но чтобы разложить его на перемещение и вращение, cv::decomposeHomographyMat() нормализует евклидову матрицу гомографии, чтобы получить матрицу гомографии. Он использует K, предоставленный пользователем, для выполнения этой нормализации.

Что касается оценки K, я думаю, что это выходит за рамки данного вопроса. Эта проблема называется автокалибровкой камеры. Вот соответствующая цитата из этой вики-статьи:

Таким образом, три просмотра — это минимум, необходимый для полной калибровки с фиксированными внутренними параметрами между просмотрами. Качественные современные датчики изображения и оптика могут также обеспечивать дополнительные предварительные ограничения на калибровку, такие как нулевой перекос (ортогональная пиксельная сетка) и единичное соотношение сторон (квадратные пиксели). Интеграция этих априорных значений уменьшит минимальное количество необходимых изображений до двух.

Кажется, что вы можете извлечь K из соответствий изображений по двум кадрам с одной и той же камеры при нулевом перекосе и предположениях о квадратных пикселях. Но я не знаком с этой темой, поэтому не могу дать вам больше предложений.

Итак, чтобы проверить правильность моей интерпретации, я сделал небольшой пример, который проецирует некоторые точки на плоскости в 3D на 2 виртуальные камеры, находит гомографию, разлагает ее и позволяет сравнить это разложение с векторами вращения и переноса наземной истины. . Это лучше, чем реальные входные данные, потому что таким образом мы точно знаем K и можем отделить ошибку в его оценке от ошибки в R и t. . Для входных данных, которые я проверил, он смог правильно оценить векторы поворота и смещения, хотя по какой-то причине перевод всегда меньше, чем земная правда в 10 раз. Может быть, это разложение определено только до масштаба (сейчас я не уверен), но интересно, что оно связано с наземным значением истинности с фиксированным коэффициентом.

Вот источник:

#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>


int main() {
  // set up a virtual camera
  float f = 100, w = 640, h = 480;

  cv::Mat1f K = (cv::Mat1f(3, 3) <<
      f, 0, w/2,
      0, f, h/2,
      0, 0,   1);

  // set transformation from 1st to 2nd camera (assume K is unchanged)
  cv::Mat1f rvecDeg = (cv::Mat1f(3, 1) << 45, 12, 66);
  cv::Mat1f t = (cv::Mat1f(3, 1) << 100, 200, 300);

  std::cout << "-------------------------------------------\n";
  std::cout << "Ground truth:\n";

  std::cout << "K = \n" << K << std::endl << std::endl;
  std::cout << "rvec = \n" << rvecDeg << std::endl << std::endl;
  std::cout << "t = \n" << t << std::endl << std::endl;

  // set up points on a plane
  std::vector<cv::Point3f> p3d{{0, 0, 10},
                               {100, 0, 10},
                               {0, 100, 10},
                               {100, 100, 10}};

  // project on both cameras
  std::vector<cv::Point2f> Q, P, S;

  cv::projectPoints(p3d,
                    cv::Mat1d::zeros(3, 1),
                    cv::Mat1d::zeros(3, 1),
                    K,
                    cv::Mat(),
                    Q);

  cv::projectPoints(p3d,
                    rvecDeg*CV_PI/180,
                    t,
                    K,
                    cv::Mat(),
                    P);

  // find homography
  cv::Mat H = cv::findHomography(Q, P);

  std::cout << "-------------------------------------------\n";
  std::cout << "Estimated H = \n" << H << std::endl << std::endl;


  // check by reprojection
  std::vector<cv::Point2f> P_(P.size());
  cv::perspectiveTransform(Q, P_, H);

  float sumError = 0;

  for (size_t i = 0; i < P.size(); i++) {
    sumError += cv::norm(P[i] - P_[i]);
  }

  std::cout << "-------------------------------------------\n";
  std::cout << "Average reprojection error = "
      << sumError/P.size() << std::endl << std::endl;


  // decompose using identity as internal parameters matrix
  std::vector<cv::Mat> Rs, Ts;
  cv::decomposeHomographyMat(H,
                             K,
                             Rs, Ts,
                             cv::noArray());

  std::cout << "-------------------------------------------\n";
  std::cout << "Estimated decomposition:\n\n";
  std::cout << "rvec = " << std::endl;
  for (auto R_ : Rs) {
    cv::Mat1d rvec;
    cv::Rodrigues(R_, rvec);
    std::cout << rvec*180/CV_PI << std::endl << std::endl;
  }

  std::cout << std::endl;

  std::cout << "t = " << std::endl;
  for (auto t_ : Ts) {
    std::cout << t_ << std::endl << std::endl;
  }

  return 0;
}

И вот вывод на моей машине:

-------------------------------------------
Ground truth:
K =
[100, 0, 320;
0, 100, 240;
0, 0, 1]

rvec =
[45;
12;
66]

t =
[100;
200;
300]

-------------------------------------------
Estimated H =
[0.04136041220427821, 0.04748763375951008, 358.5557917287962;
0.05074854454707714, 0.06137211243830468, 297.4585754092336;
8.294458769850147e-05, 0.0002294875562580223, 1]

-------------------------------------------
Average reprojection error = 0

-------------------------------------------
Estimated decomposition:

rvec =
[-73.21470385654712;
56.64668212487194;
82.09114210289061]

[-73.21470385654712;
56.64668212487194;
82.09114210289061]

[45.00005330430893;
12.00000697952995;
65.99998380038915]

[45.00005330430893;
12.00000697952995;
65.99998380038915]


t =
[10.76993852870029;
18.60689642878277;
30.62344129378435]

[-10.76993852870029;
-18.60689642878277;
-30.62344129378435]

[10.00001378255982;
20.00002581449634;
30.0000336510648]

[-10.00001378255982;
-20.00002581449634;
-30.0000336510648]

Как видите, среди гипотез есть правильная оценка вектора вращения, а в масштабе есть правильная оценка перевода.

person alexisrozhkov    schedule 11.03.2016
comment
Спасибо за обстоятельный и информативный ответ! Я прочитал некоторые из предоставленных вами ссылок и думаю, что следую общим понятиям. Я до сих пор не уверен в роли K, поскольку она, кажется, полностью игнорируется. Однако, когда я применяю описанный выше рабочий процесс к двум изображениям с поворотом камеры на 30 градусов и отмеченными точками, мои результаты плохие. Опять же, перспективное преобразование с использованием предполагаемого H дает отличный результат. Но разложение дает плохие результаты. Я предполагаю, что мне не хватает какой-то нормализации моих точек при применении R и t, но я не могу понять, что именно. - person IlliteratePhD; 15.03.2016
comment
Я немного уточню. Я вручную разместил 12 маркеров в перекрывающихся частях двух сцен, где камера была повернута на 30 градусов вокруг оси Y. Поскольку камера вращалась вокруг одной оси, я предполагал, что получу R, демонстрирующий вращение, и t = 0. Однако я вижу очень большие перемещения и просто бессмысленные вращения. - person IlliteratePhD; 15.03.2016
comment
Были ли совпадения точек равномерно распределены по области изображения? Потому что когда вы наблюдаете маленькую часть вращающегося объекта - ее можно локально описать поступательным движением. - person alexisrozhkov; 15.03.2016
comment
Кроме того, теперь я не уверен в том, что матрица K должна быть идентичной, когда вы используете cv::findHomography() для оценки преобразования. Координаты действительно нормализуются «внутри», но нормализация сохраняется в матричной форме, и ее инверсия повторно применяется к оценочному преобразованию перед возвратом результата. Итак, кажется, что cv::findHomography() возвращает евклидову матрицу гомографии. Но, к сожалению, я не могу понять, как найти соответствующую матрицу K в этом случае. - person alexisrozhkov; 15.03.2016
comment
Я просмотрел этот документ: ais.informatik.uni-freiburg.de/teaching/ws11/robotics2/pdfs/ Кажется, что вы можете получить K из евклидовой гомографической матрицы, используя факторизацию Холецкого. Если будет время вечером - попробую и выложу фрагмент. - person alexisrozhkov; 15.03.2016
comment
Я попытался распределить свои точки по всему перекрывающемуся участку, так что это не должно быть проблемой. Если это поможет, у меня есть калибровочное значение для K из других экспериментов, поэтому мне действительно не нужно извлекать K из H. Я просто не могу понять, почему мои результаты неверны. Я опубликую свои данные ниже. - person IlliteratePhD; 15.03.2016
comment
Я отредактировал ответ, и хотя он не полностью решает вашу проблему, я надеюсь, что он все еще может быть полезен. - person alexisrozhkov; 03.04.2016
comment
Большое спасибо за то, что поделились этим информативным ответом. есть масштабная правильная оценка перевода, хотелось бы спросить, можно ли получить реальную шкалу трансформации? - person Humam Helfawi; 01.11.2017
comment
могу ли я попросить вас уточнить кадры координат камеры в вашем примере кода? Я не понимаю, чему соответствует 45, 12, 66? это повороты камеры по x (вправо), y (вниз), z (вперед) соответственно? кроме того, эта виртуальная камера следует правилу правой или левой руки? - person Färid Alijani; 05.06.2019

Вместо этого вы должны использовать solvePnP на каждой плоскости, а затем получить относительное перемещение и вращение из двух матриц камеры (при условии, что у вас есть как минимум 4 точки). Проблема с decomposeHomographyMat заключается в том, что вы можете получить до 4 решений... Вы можете удалить два, которые будут выходить за пределы FOV изображения, но это все еще проблема.

  • @alexisrozhkov- я нашел ошибку в вашем коде- и статья, и функция предполагают, что плоскость Z равна 1 (а не z=10, как у вас). Ниже добавлена ​​​​правильная повторная имплантация вашего вышеуказанного кода (исправлена ​​​​и в python):
import cv2
import numpy as np


# set up a virtual camera
f = 100
w = 640
h = 480

K = np.array([[f, 0, w/2],
              [0, f, h/2],
              [0, 0,   1]])
dist_coffs = np.zeros(5)
# set transformation from 1st to 2nd camera (assume K is unchanged)
rvecDeg = np.array([[45, 12, 66]]).T
t = np.array([[100.0, 200, 300]]).T

print("-------------------------------------------\n")
print("Ground truth:\n")

print("K = \n" + str(K))
print("rvec = \n" + str(rvecDeg))
print("t = \n" + str(t))

# set up points on a plane
p3d = np.array([[0, 0, 1],
                [100, 0, 1],
                [0, 100, 1],
                [100, 100, 1]], dtype=np.float64)

# project on both cameras

Q, _ = cv2.projectPoints(p3d,
                         np.zeros((3, 1)),
                         np.zeros((3, 1)),
                         K,
                         dist_coffs)

P, _ = cv2.projectPoints(p3d,
                         rvecDeg*np.pi/180,
                         t,
                         K,
                         dist_coffs)

# find homography
H, _ = cv2.findHomography(Q, P)

print("-------------------------------------------\n")
print("Estimated H = \n" + str(H))


# check by reprojection
P_ = cv2.perspectiveTransform(Q, H)

sumError = 0

for i in range(P.shape[0]):
    sumError += np.linalg.norm(P[i] - P_[i])


print("-------------------------------------------\n")
print("Average reprojection error = "+str(sumError/P.shape[0]))


# decompose using identity as internal parameters matrix
num_res, Rs, ts, n = cv2.decomposeHomographyMat(H, K)

print("-------------------------------------------\n")
print("Estimated decomposition:\n\n")
for i, Rt in enumerate(zip(Rs, ts)):
    R, t = Rt
    print("option " + str(i+1))
    print("rvec = ")
    rvec, _ = cv2.Rodrigues(R)
    print(rvec*180/np.pi)
    print("t = ")
    print(t)

и это результат (вариант 3 вышел как правильный результат):

-------------------------------------------

Ground truth:

K = 
[[100.   0. 320.]
 [  0. 100. 240.]
 [  0.   0.   1.]]
rvec = 
[[45]
 [12]
 [66]]
t = 
[[100.]
 [200.]
 [300.]]
-------------------------------------------

Estimated H = 
[[3.93678513e-03 4.51998690e-03 3.53830416e+02]
 [4.83037187e-03 5.84154353e-03 3.05790229e+02]
 [7.89487379e-06 2.18431675e-05 1.00000000e+00]]
-------------------------------------------

Average reprojection error = 1.1324020050061362e-05
-------------------------------------------

Estimated decomposition:


option 1
rvec = 
[[-78.28555877]
 [ 58.01301837]
 [ 81.41634175]]
t = 
[[100.79816988]
 [198.59277542]
 [300.6675498 ]]
option 2
rvec = 
[[-78.28555877]
 [ 58.01301837]
 [ 81.41634175]]
t = 
[[-100.79816988]
 [-198.59277542]
 [-300.6675498 ]]
option 3
rvec = 
[[45.0000205 ]
 [12.00005488]
 [65.99999505]]
t = 
[[100.00010826]
 [200.00026791]
 [300.00034698]]
option 4
rvec = 
[[45.0000205 ]
 [12.00005488]
 [65.99999505]]
t = 
[[-100.00010826]
 [-200.00026791]
 [-300.00034698]]
person YoniChechik    schedule 12.04.2020