Определить внешнюю камеру с помощью opencv для opengl с космическим объектом мира

Я использую opencv и openframeworks (т.е. opengl) для вычисления камеры (мировые матрицы преобразования и проекции) из изображения (а позже несколько изображений для триангуляции).

Для целей opencv «план этажа» становится объектом (то есть шахматной доской) с 0,0,0 центром мира. Позиции мира/пола известны, поэтому мне нужно получить информацию о проекции (коэффициенты искажения, поле зрения и т. д.) и внешние координаты камеры.

Координаты ввода 2D

Я сопоставил позиции просмотра этих точек плана этажа с моим 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();

выходной 3D-вид

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

Я просмотрел множество ответов SO, документацию десятки раз, но не нашел ничего правильного, я почти уверен, что рассмотрел все, что мне нужно для преобразования пространства, но, возможно, я пропустил что-то очевидное ? Или делать что-то не в том порядке?

Обновление 1 — плоскость мирового пространства... Я изменил плоскость пола мирового пространства на XY (Z вверх), чтобы соответствовать вводу для openCV. (gWorldToCalibration теперь является единичной матрицей). Поворот по-прежнему неправильный, и вывод проекции такой же, но я думаю, что перевод теперь правильный (он определенно находится на правильной стороне маркеров) 3D вид в плоскости XY

Update2 — Реальный размер изображения. Я играю с размером изображения при калибровке камеры; учитывая, что я использую 1,1, который нормализован, но параметр imageSize находится в целых числах, я подумал, что это может быть важно... И я предполагаю, что это так (красный прямоугольник - это место, где точки проецируемого пространства обзора пересекаются с z = 0 плоскость пола) Без какой-либо коррекции искажений вот результат (Единственное, что изменилось, это размер изображения с 1,1 до 640 480. Я также умножаю свои нормализованные координаты пространства ввода на 640 480) введите описание изображения здесь Я попытаюсь добавить коррекцию искажений, чтобы посмотреть, идеально ли оно выстраивается...


person Soylent Graham    schedule 20.03.2013    source источник
comment
Вы уверены, что визуализируете положение камеры, а не поступательную составляющую внешней матрицы камеры?   -  person fdermishin    schedule 21.03.2013
comment
Я не уверен, что неправильно прочитал ваш комментарий, но окончательная матрица просмотра модели (которая, как я полагаю, является/должна быть cam pos) настроена на глобальное преобразование ofCamera. Визуализация выглядит следующим образом; зеленый +Y, синий +z, красный +x.   -  person Soylent Graham    schedule 21.03.2013
comment
Сетка галопирует вокруг 0,0,0. Возможно, я ошибся во внешнем преобразовании и преобразовании камеры? Но для этого и нужен инвертор.   -  person Soylent Graham    schedule 21.03.2013
comment
Извините, я пропустил шаг инверсии. Тогда это, кажется, нормально.   -  person fdermishin    schedule 21.03.2013


Ответы (3)


Первое, что нужно проверить, это убедиться, что маркеры правильно перепроецируются на изображение с учетом предполагаемых внутренних и внешних матриц камеры. Затем вы можете найти положение камеры в глобальном кадре и посмотреть, совпадает ли оно с положением маркеров. (Используйте систему координат, как в OpenCV.) Как только это будет сделано, не так много вещей, которые могут пойти не так. Поскольку вы хотите, чтобы точки лежали на плоскости xz, вам нужно всего одно преобразование координат. Как я вижу, вы делаете это с матрицей gWorldToCalibration. Затем примените преобразование как к маркерам, так и к положению камеры и убедитесь, что маркеры находятся в правильном месте. Тогда и положение камеры будет правильным (если только что-то не так с хиральностью системы координат, но это легко исправить).

person fdermishin    schedule 20.03.2013
comment
Маркеры перепроецируются из камеры в лучи (синие линии), которые явно находятся не в том месте. Зеленый многоугольник — это проекция луча на N метров, а красный — место, где I попадает в плоскость y = 0 (в данном случае это бессмысленно). Но я думаю, вы имеете в виду репроецирование в пространство просмотра/изображения, я сделаю это утром! А также, возможно, преобразовать все так, чтобы z=up удалить один шаг... - person Soylent Graham; 21.03.2013
comment
Да, я имею в виду перепроецирование на изображение, сразу после калибровки. Это можно сделать путем перемножения внутренних и внешних матриц, полученных на координаты маркеров (в системе координат, используемой для калибровки). - person fdermishin; 21.03.2013
comment
Мои результаты не очень хорошие... Я делаю внутреннюю матрицу 4x4 (0,0,0,1 для дополнительного столбца/строки) и копирую остальное из cameraMatrix из calibrateCamera. Я использую rT в качестве внешней матрицы (правильно ли это?) и умножаю на мои нормализованные координаты ввода-вида. Каждая перепроецированная мировая позиция отличается на одинаковую величину... (4.0,1.7,5.5, 4.3,1.6,6.0, 4.7, 1.5, 6.0, 4.8,1.6,5.5 ) Мой внешний перевод - 1.4, 4.3, 6.3. Я не могу не думать, что эти значения связаны... Правильно ли я делаю повторное проецирование? - person Soylent Graham; 21.03.2013

На данный момент, по крайней мере, я рассматриваю свой Edit 2 (ImageSize должен быть больше, чем 1,1) как исправление, поскольку оно дало результаты, намного более похожие на то, что я ожидал.

Возможно, в данный момент у меня все с ног на голову, но это дает довольно хорошие результаты.

person Soylent Graham    schedule 26.03.2013
comment
Привет! У меня похожая проблема, но.. из правок не очень понятно.. в конце концов, какова была пошаговая процедура? - person nkint; 04.09.2013
comment
Решение проблемы состояло в том, чтобы НЕ нормализовать координаты моего изображения, поэтому вместо поплавков и размера изображения 1,1 я использовал координаты пикселей и 1920,1080. Не думаю, что мне нужно было что-то еще менять. - person Soylent Graham; 06.09.2013

Я думаю, что вам не следует не принимать обратное значение gWorldToCalibration.

ofMatrix4x4 gCalibrationToWorld = gWorldToCalibration.getInverse();

Здесь я разместил код, который делает более или менее то, что вы хотите OpenCV-to OpenGL COS. Это на C, но должно быть похоже на C++.

person Adrian Schneider    schedule 04.12.2013