Определить внешнюю камеру с помощью 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)Я попытаюсь добавить исправление искажения, чтобы увидеть, идеально ли оно выстраивается...
3 ответа
Пока, по крайней мере, я рассматриваю свой Edit 2 (ImageSize должен быть больше чем 1,1) как исправление, так как оно дает результаты, намного больше, чем я ожидал.
У меня сейчас могут быть проблемы с ног на голову, но это дает довольно хорошие результаты.
Я думаю, что вы не должны принимать обратное значение gWorldToCalibration
ofMatrix4x4 gCalibrationToWorld = gWorldToCalibration.getInverse();
Здесь я разместил код, который делает более или менее то, что вы хотите от OpenCV до OpenGL COS. Это на C, но должно быть похоже на C++.
Первое, что нужно проверить, - это убедиться, что маркеры правильно проецируются на изображение с учетом предполагаемой внутренней и внешней матриц камеры. Затем вы можете найти положение камеры в глобальном кадре и посмотреть, соответствует ли оно позициям маркеров. (Используйте систему координат, как в OpenCV.) Когда это сделано, не так много вещей, которые могут пойти не так. Поскольку вы хотите, чтобы точки лежали на плоскости xz, вам нужно всего лишь одно преобразование координат. Как я вижу, вы делаете это с помощью матрицы gWorldToCalibration. Затем примените преобразование как к маркерам, так и к положению камеры и убедитесь, что маркеры находятся в нужном месте. Тогда положение камеры также будет правильным (если что-то пошло не так с системой координат, но может быть легко исправлено).