Постановка компьютерной камеры с матрицей гомографии на основе 4 компланарных точек
У меня есть 4 компланарных точки в видео (или изображении), представляющих квадрат (не обязательно квадрат или прямоугольник), и я хотел бы иметь возможность отображать виртуальный куб поверх них, где расположены углы куба точно на углах видеокарты.
Так как точки копланарны, я могу вычислить гомографию между углами единичного квадрата (т.е. [0,0] [0,1] [1,0] [1,1]) и видеокоды квадранта,
Из этой гомографии я должен иметь возможность вычислить правильную позу камеры, т.е. [R | t], где R является матрицей вращения 3x3, а t является вектором трансляции 3x1, так что виртуальный куб лежит на видеоцикле.
Я прочитал много решений (некоторые из них на SO) и попытался их реализовать, но они, похоже, работают только в некоторых "простых" случаях (например, когда квадрат видео является квадратом), но не работают в большинстве случаев.
Вот методы, которые я пробовал (большинство из них основаны на одних и тех же принципах, только вычисление перевода несколько отличается). Пусть K - внутренняя матрица из камеры, H - гомография. Мы вычисляем:
A = K-1 * H
Пусть a1, a2, a3 - столбчатые векторы A и r1, r2, r3 векторы столбцов матрицы вращения R.
r1 = a1 / ||a1||
r2 = a2 / ||a2||
r3 = r1 x r2
t = a3 / sqrt(||a1||*||a2||)
Проблема в том, что это не работает в большинстве случаев. Чтобы проверить мои результаты, я сравнил R и t с результатами, полученными методом OpenCV solvePnP (используя следующие трехмерные точки [0,0,0] [0,1,0] [1,0,0] [1,1, 0]).
Так как я отображаю куб таким же образом, я заметил, что в каждом случае solvePnP дает правильные результаты, а поза, полученная из гомографии, в основном неправильна.
В теории, поскольку мои точки копланарны, можно вычислить позу из гомографии, но я не смог найти правильный способ вычислить позу из H.
Любые идеи о том, что я делаю неправильно?
Изменить после попытки использования метода @Jav_Rock
Привет, Jav_Rock, большое спасибо за ваш ответ, я пробовал ваш подход (и многие другие), который кажется более или менее ОК.
Тем не менее, я все еще испытываю некоторые проблемы при вычислении позы на основе 4 компланарных точек. Чтобы проверить результаты, я сравниваю их с результатами solvePnP (что будет намного лучше из-за подхода минимизации ошибки повторной оценки).
Вот пример:
![cube]()
- Желтый куб: Решите PNP
- Технология Black Cube: Jav_Rock
- Cyan (и Purple) куб (ы): некоторые другие методы с учетом тех же самых результатов
Как вы можете видеть, черный куб более или менее нормальный, но, похоже, не пропорционален, хотя векторы кажутся ортонормированными.
EDIT2: Я нормализовал v3 после его вычисления (для обеспечения ортонормированности), и, похоже, он также решает некоторые проблемы.
Ответы
Ответ 1
Если у вас есть ваша Гомография, вы можете рассчитать положение камеры с чем-то вроде этого:
void cameraPoseFromHomography(const Mat& H, Mat& pose)
{
pose = Mat::eye(3, 4, CV_32FC1); // 3x4 matrix, the camera pose
float norm1 = (float)norm(H.col(0));
float norm2 = (float)norm(H.col(1));
float tnorm = (norm1 + norm2) / 2.0f; // Normalization value
Mat p1 = H.col(0); // Pointer to first column of H
Mat p2 = pose.col(0); // Pointer to first column of pose (empty)
cv::normalize(p1, p2); // Normalize the rotation, and copies the column to pose
p1 = H.col(1); // Pointer to second column of H
p2 = pose.col(1); // Pointer to second column of pose (empty)
cv::normalize(p1, p2); // Normalize the rotation and copies the column to pose
p1 = pose.col(0);
p2 = pose.col(1);
Mat p3 = p1.cross(p2); // Computes the cross-product of p1 and p2
Mat c2 = pose.col(2); // Pointer to third column of pose
p3.copyTo(c2); // Third column is the crossproduct of columns one and two
pose.col(3) = H.col(2) / tnorm; //vector t [R|t] is the last column of pose
}
Этот метод работает от меня. Удачи.
Ответ 2
Ответ, предложенный Jav_Rock, не дает правильного решения для позы камеры в трехмерном пространстве.
Для оценки древомерного преобразования и вращения, вызванного гомографией, существуют множественные подходы. Один из них предоставляет закрытые формулы для разложения гомографии, но они очень сложны. Кроме того, решения никогда не являются уникальными.
К счастью, OpenCV 3 уже реализует это разложение (decomposeHomographyMat). Учитывая гомографию и правильно масштабированную матрицу внутренних функций, функция предоставляет набор из четырех возможных поворотов и переводов.
Ответ 3
Вычисление [R | T] из матрицы гомографии немного сложнее, чем ответ Jav_Rock.
В OpenCV 3.0 существует метод cv:: decposeHomographyMat, который возвращает четыре потенциальных решения, один из которых правильный. Однако OpenCV не предоставил метод для выбора правильного.
Теперь я работаю над этим и, возможно, опубликую мои коды здесь позже в этом месяце.
Ответ 4
На всякий случай кому-то понадобится портирование на Python функции, написанной @Jav_Rock:
def cameraPoseFromHomography(H):
H1 = H[:, 0]
H2 = H[:, 1]
H3 = np.cross(H1, H2)
norm1 = np.linalg.norm(H1)
norm2 = np.linalg.norm(H2)
tnorm = (norm1 + norm2) / 2.0;
T = H[:, 2] / tnorm
return np.mat([H1, H2, H3, T])
Хорошо работает в моих задачах.
Ответ 5
Плоскость, содержащая ваш квадрат на изображении, сбрасывает агенты полос вашей камеры.
Уравнение этой линии равно Ax + By + C = 0.
Нормальная ваша плоскость (A, B, C)!
Пусть p00, p01, p10, p11 являются координатами точки после применения внутренних параметров камеры и в гомогенной форме, например, p00 = (x00, y00,1)
Исчезающая линия может быть рассчитана как:
- down = p00 cross p01;
- left = p00 cross p10;
- right = p01 cross p11;
- up = p10 cross p11;
- v1 = левый крест справа;
- v2 = свернуть вниз;
- vanish_line = v1 cross v2;
Где крест в стандартном векторном поперечном продукте
Ответ 6
Здесь версия python, основанная на том, что был представлен Дмитрием Волошиным, который нормализует матрицу вращения и переносит результат на 3x4.
def cameraPoseFromHomography(H):
norm1 = np.linalg.norm(H[:, 0])
norm2 = np.linalg.norm(H[:, 1])
tnorm = (norm1 + norm2) / 2.0;
H1 = H[:, 0] / norm1
H2 = H[:, 1] / norm2
H3 = np.cross(H1, H2)
T = H[:, 2] / tnorm
return np.array([H1, H2, H3, T]).transpose()