Определить внешнюю камеру с помощью opencv для opengl с объектом мирового пространства
Я использую opencv и openframeworks (т.е. opengl) для вычисления камеры (матрицы преобразования мира и проекции) из изображения (а позже и нескольких изображений для триангуляции).
Для целей opencv «план этажа» становится объектом (т. Е. Шахматной доской) с 0,0,0 центром мира. Положение мира / пола известно, поэтому мне нужно получить информацию о проекции (коэффициенты искажения, fov и т. Д.) И внешние координаты камеры.
Я нанес на карту положения этих точек плана этажа на мое 2D-изображение в нормализованном пространстве вида ([0,0] вверху слева. [1,1] внизу справа).
Объект (план этажа / мировые точки) находится в плоскости xz, -y вверх, поэтому я конвертирую в плоскость xy (здесь не уверен, является ли z-up отрицательным или положительным ...) для opencv, так как он должен быть плоским
ofMatrix4x4 gWorldToCalibration(
1, 0, 0, 0,
0, 0, 1, 0,
0, 1, 0, 0,
0, 0, 0, 1
);
Я передаю 1,1 как ImageSize для калибровки камеры. флагиCV_CALIB_FIX_ASPECT_RATIO|V_CALIB_FIX_K4|CV_CALIB_FIX_K5
calibrateCamera
работает успешно, дает мне низкую ошибку (обычно около0.003
).
с помощьюcalibrationMatrixValues
Я получаю разумное поле зрения, обычно около 50 градусов, поэтому я почти уверен, что внутренние свойства верны.
Теперь, чтобы вычислить внешнее преобразование мира-пространства камеры ... Я не думаю, что мне нужно использоватьsolvePnP
поскольку у меня есть только один объект (хотя я пробовал все это раньше и вернулся с теми же результатами)
// rot and trans output...
cv::Mat& RotationVector = ObjectRotations[0];
cv::Mat& TranslationVector = ObjectTranslations[0];
// convert rotation to matrix
cv::Mat expandedRotationVector;
cv::Rodrigues(RotationVector, expandedRotationVector);
// merge translation and rotation into a model-view matrix
cv::Mat Rt = cv::Mat::zeros(4, 4, CV_64FC1);
for (int y = 0; y < 3; y++)
for (int x = 0; x < 3; x++)
Rt.at<double>(y, x) = expandedRotationVector.at<double>(y, x);
Rt.at<double>(0, 3) = TranslationVector.at<double>(0, 0);
Rt.at<double>(1, 3) = TranslationVector.at<double>(1, 0);
Rt.at<double>(2, 3) = TranslationVector.at<double>(2, 0);
Rt.at<double>(3, 3) = 1.0;
Теперь у меня есть матрица вращения и преобразования, но это основной столбец (я полагаю, что объект полностью перекошен, если я не транспонирую, а код выше выглядит для меня главным столбцом)
// convert to openframeworks matrix AND transpose at the same time
ofMatrix4x4 ModelView;
for ( int r=0; r<4; r++ )
for ( int c=0; c<4; c++ )
ModelView(r,c) = Rt.at<double>( c, r );
Поменяйте местами мои плоскости обратно в пространство моей координаты (y вверх), используя обратную матрицу.
// swap y & z planes so y is up
ofMatrix4x4 gCalibrationToWorld = gWorldToCalibration.getInverse();
ModelView *= gCalibrationToWorld;
Не уверен, что мне НУЖНО сделать это ... Я не отрицал самолеты, когда помещал их в калибровку ...
// invert y and z planes for -/+ differences between opencv and opengl
ofMatrix4x4 InvertHandednessMatrix(
1, 0, 0, 0,
0, -1, 0, 0,
0, 0, -1, 0,
0, 0, 0, 1
);
ModelView *= InvertHandednessMatrix;
И, наконец, вид модели - это объект-относительно камеры, и я хочу инвертировать его, чтобы он был относительно камеры-объекта (0,0,0)
ModelView = ModelView.getInverse();
Это приводит к тому, что камера находится не в том месте и вращается неправильно. Это неслишком далеко, камера - правая сторона плоскости Y, перевод не дикий, и я думаю, что это верный путь ... просто еще не правильно. Я ожидаю, что камера будет находиться в нарисованном краской синем круге.
Я прошел десятки SO-ответов, документацию десятки раз, но не нашел ничего правильного, я уверен, что охватил все, что мне нужно для преобразования пространства, но, возможно, я упустил что-то очевидное ? Или делать что-то не в том порядке?
Обновление 1 - мир космического самолета ... Я изменил свою плоскость мирового пространства на XY (Z вверх), чтобы соответствовать входу для openCV. (gWorldToCalibration теперь является единичной матрицей). Поворот по-прежнему неправильный, и результат проецирования такой же, но я думаю, что перевод сейчас правильный (это, безусловно, на правильной стороне маркеров)
Update2 - реальный размер изображения Я играю с размером изображения, идущим в калибровку камеры; видя, как я использую 1,1, который нормализован, но параметр imageSize указан в целых числах, я подумал, что это может быть значительным ... И я предполагаю, что это так (красный прямоугольник - это то, где проекционные точки пространства обзора пересекаются с z = 0 плоскость пола) Без какой-либо коррекции искажения, вот результат (Единственное, что изменилось, это размер изображения от 1,1 до 640,480. Я также умножил свои нормализованные координаты пространства ввода-просмотра на 640,480) Я собираюсь попробовать добавить коррекцию искажения, чтобы увидеть, если она выстраиваетсяв совершенстве...