Я использую opencv и openframeworks (т.е. opengl) для вычисления камеры (мировые матрицы преобразования и проекции) из изображения (а позже несколько изображений для триангуляции).
Для целей opencv «план этажа» становится объектом (то есть шахматной доской) с 0,0,0 центром мира. Позиции мира/пола известны, поэтому мне нужно получить информацию о проекции (коэффициенты искажения, поле зрения и т. д.) и внешние координаты камеры.
Я сопоставил позиции просмотра этих точек плана этажа с моим 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 );
Поменяйте местами мои плоскости обратно в мое координатное пространство (у вверх), используя обратную матрицу ранее.
// 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) Я попытаюсь добавить коррекцию искажений, чтобы посмотреть, идеально ли оно выстраивается...