Как я могу оценить позу камеры с 3D-2D-точечными соответствиями (используя opencv)

Здравствуйте, моя цель состоит в том, чтобы разработать функцию отслеживания головы, которая будет использоваться в кабине самолета (симулятора), чтобы обеспечить AR для поддержки гражданских пилотов при посадке и полете в плохих визуальных условиях.

Мой подход заключается в том, чтобы обнаружить характерные точки (в темных светодиодах симулятора), для которых мне известны трехмерные координаты, а затем вычислить приблизительную (R = t) позу (с надетой головой) (вращение, соединенное с переводом).

Проблема, которая у меня есть, заключается в том, что предполагаемая поза кажется неправильной, и проекция моих трехмерных точек (которую я также использовал для оценки позы) не перекрывается точками 2D-изображения (или не видна).

Обнаружение светодиода работает, но оценка позы и 3D-проекция не

Мои вопросы:

Как я могу оценить позу камеры с заданным набором точечных соответствий 2D-3D.

Почему это не работает, как я это пробую и где могут быть источники ошибок?

Насколько точными должны быть измерения (3D и 2D точек и матрицы камеры), чтобы теоретическое решение работало в реальных условиях?

Будет ли подход работать для копланарных точек (ось x,y изменилась) в теории?

Аппаратное обеспечение, которое я использую, - Epson BT-200.

В самолете я определил фиксированную ординату, к которой я рассчитываю относительные переводы и повороты как результат моей программы. Программа определяет координаты изображения (уникальных) светодиодов и сопоставляет их с соответствующей трехмерной координатой. С помощью матрицы камеры, которую я получил с помощью образца Android-кода open-cv ( https://github.com/Itseez/opencv/tree/master/samples/android/camera-calibration), я пытаюсь оценить позу с помощью solvePnP.

Матрица моей камеры и искажения меняются незначительно. Вот некоторые значения, которые я получил от процедуры. Я удостоверился, что круговое расстояние моего распечатанного шаблона круга совпадает с записанным в исходном коде (измеряется в метрах).

Вот несколько примеров и как я создаю его OpenCV Mat.

//  protected final double[] DISTORTION_MATRIX_VALUES = new double[]{
//          /*This matrix should have 5 values*/
//          0.04569467373955304,
//          0.1402980385369059,
//          0,
//          0,
//          -0.2982135315849994
//  };

//  protected final double[] DISTORTION_MATRIX_VALUES = new double[]{
//          /*This matrix should have 5 values*/
//          0.08245931646421553,
//          -0.9893762277047577,
//          0,
//          0,
//          3.23553287438898
//  };

//  protected final double[] DISTORTION_MATRIX_VALUES = new double[]{
//          /*This matrix should have 5 values*/
//          0.07444480392067945,
//          -0.7817175834131075,
//          0,
//          0,
//          2.65433773093283
//  };
    protected final double[] DISTORTION_MATRIX_VALUES = new double[]{
            /*This matrix should have 5 values*/
            0.08909941096327206,
            -0.9537960457721699,
            0,
            0,
            3.449728790843752
    };

    protected final double[][] CAMERA_MATRIX_VALUES = new double[][]{
            /*This matrix should have 3x3 values*/
//          {748.6595405553738, 0, 319.5},
//          {0, 748.6595405553738, 239.5},
//          {0, 0, 1}
//          {698.1744297982436, 0, 320},
//          {0, 698.1744297982436, 240},
//          {0, 0, 1}
//          {707.1226937511951, 0, 319.5},
//          {0, 707.1226937511951, 239.5},
//          {0, 0, 1}
            {702.1458656346429, 0, 319.5},
            {0, 702.1458656346429, 239.5},
            {0, 0, 1}
    };

    private void initDestortionMatrix(){
        distortionMatrix = new MatOfDouble();
        distortionMatrix.fromArray(DISTORTION_MATRIX_VALUES);
    }

    private void initCameraMatrix(){
        cameraMatrix = new Mat(new Size(3,3), CvType.CV_64F);
        for(int i=0;i<CAMERA_MATRIX_VALUES.length; i++){
            cameraMatrix.put(i, 0, CAMERA_MATRIX_VALUES[i]);
        }
    }

Чтобы оценить позу камеры, я использую solvePnP (и solvePnPRansac), как описано в нескольких местах ( 1, 2, 3, 4). Результат convertPnP я использую в качестве входных данных для проекции ( Calib3d.projectPoints). Обратный результат конкатинированного результата [R|t] я использую в качестве оценочной позы.

Поскольку мои результаты в продуктивной среде были слишком плохими, я создал среду тестирования. В этой среде я помещаю камеру (которая из-за ее 3D-формы (это стекло) немного повернута вниз по краю стола. Этот край я использую как ординату мировой системы координат. Я искал, как open-cv Система координат может быть ориентирована и найти разные ответы (один на стеке потока и один на официальном YouTube-разговоре об opencv). В любом случае я проверил, правильно ли я получил систему координат по проекциям трехмерных точек (описанных в этой системе координат) на изображение и проверяется, остается ли данная форма мира постоянной.

Итак, я подошел с z, указывающим вперед, y вниз и x вправо. Изображение показывает, что 3D-изображение проецируется правильно. Только поза не оценена, поэтому точки не пересекаются

Чтобы приблизиться к своему решению, я оценил позу в моей среде тестирования. Вывод вектора трансляции и вывод ангела Эйлера относится к обратному [R|t]. Ангелы Эйлера могут отображаться неправильно (они могут быть поменяны местами или неверно, если мы примем во внимание порядок), потому что я вычисляю их с помощью конвективных (я полагаю, ссылаясь на систему координат самолета), используя систему координат open-cv. (Вычисление происходит в классе Pose, который я прикреплю). Но в любом случае даже вектор перевода (обратного) представляется неверным (в моем простом тесте).

В одном тесте с этим изображением у меня был крен (который мог бы быть шагом в координатах самолета) 30° и перемещение вверх на 50 см. Это кажется более разумным. Поэтому я предположил, что, поскольку мои очки копланарны, я могу получить неоднозначные результаты. Таким образом, я реализовал другой тест с точкой, которая изменилась в оси Z. Но с этим тестом даже проекция не удалась.

Для solvePnP я попробовал все разные флаги алгоритма решения и разные параметры для алгоритма Ransac.

Может быть, вы можете как-то помочь мне найти мою ошибку или показать мне хороший путь для решения моей первоначальной проблемы. Я собираюсь приложить также мой исходный код для отладки со многими операторами println и изображениями отладки. Этот код содержит мои точечные измерения.

Заранее благодарны за Вашу помощь.

Класс Main.java: Класс Pose.java: 0.png

1.png

РЕДАКТИРОВАТЬ 22.03.2015: Наконец-то я смог найти ошибки, которые я сделал.

  1. Я изменил объект Mat в цикле for, потому что OpenCV много работает с вызовом по ссылке, и здесь я не был достаточно осторожен. Так что tvec и rvec для перепроецирования были не правы.
  2. Одна из моих точек в среде тестирования (в координатах изображения) была помечена неправильно из-за путаницы в направлении оси.

Так что мой подход в целом был правильным. Я не получаю хотя бы (часто) действительных репроекций в моем наборе тестовых данных.

К сожалению, алгоритмы OpenCV PnP: "ITERATIVE, P3P, EPNP" возвращают различные результаты, и даже при использовании очень неточной, но близкой внутренней догадки результаты только иногда верны. Предполагается, что алгоритм P3P обеспечивает 3 решения, но OpenCV предоставляет только одно. Предполагается, что EPNP даст хорошие результаты, но с EPNP OpenCV возвращает худшие результаты, полученные из моего человеческого наблюдения.

Теперь проблема в том, как отфильтровать неточные значения или убедиться, что функция OpenCV возвращает действительные. (Может быть, я должен изменить нативный код, чтобы получить 3 решения для PnP).

Сжатые изображения здесь (37 МБ) действительно показывают мои текущие результаты (с ITERATIVE PnP-Solver) с внутренним предположением о нулевом вращении и 75 см вверх. Распечатка имеет ось x вперед, ось y влево и z, а также соответствующие углы крена, тангажа и рыскания.

1 ответ

Одна вещь, которую я узнал, пытаясь внедрить мою систему слежения за головой, состоит в том, что вы должны начать с простой задачи, а затем перейти к более сложной. Ваш вопрос довольно актуален, и, к сожалению, у меня нет времени анализировать его и искать ошибку или логическую ошибку в вашем коде, поэтому, по крайней мере, я постараюсь дать вам несколько советов и рабочих примеров.

Вот учебник OpenCV для поиска перемещения и поворота объекта. Это написано на Python, если это проблема здесь, часть моего старого проекта C++.
Мой проект выполняет ту же задачу, используя функцию solvePnP или solvePnPRansac (вы можете изменить режим). Обратите внимание, что мой код является частью какого-то старого проекта "игровой площадки", поэтому даже после очистки, которую я выполнил, он довольно грязный. Когда вы запустите его, покажите печатную шахматную доску камере, нажмите "p", чтобы начать оценку положения и вращения, "m", чтобы изменить режим (0-ransac, 1-pnp, 2-posit, который, кажется, не работает...) или 'd' для включения / выключения с использованием коэффициентов искажения.
Оба проекта основаны на поиске шахматной доски, но их будет легко модифицировать для использования других объектов.

Калибровка камеры - пока я работал над своей системой отслеживания головы, мне никогда не удавалось откалибровать камеру дважды с одинаковыми результатами... Поэтому я решил использовать какой-то файл калибровки, который я нашел на github, и он работал хорошо - Здесь вы можете найти более подробную информацию о том, что ссылка на этот файл.

редактировать:

Попробуйте начать с как можно более простого решения, которое дает хорошие результаты в некоторых (даже простых) ситуациях. На мой взгляд, неплохо бы начать с замены листа бумаги из вашей тестовой среды печатной шахматной доской из учебника ( этой) и заставить ее работать. Переход от этого к вашей проблеме будет намного проще, чем начинать с вашей проблемы. Попробуйте создать любое рабочее решение на любом языке программирования - подумайте об использовании версии OpenCV на Python или C++ - там гораздо больше уроков / примеров, чем для версии Java, и сравнение результатов из вашего кода с результатами из некоторого рабочего кода сделает это намного проще. Когда у вас будет какое-то рабочее решение, попробуйте изменить его для работы с вашей средой тестирования. Есть много вещей, которые могут привести к тому, что он сейчас не работает - недостаточно очков, ошибки в вашем коде или даже в Java-оболочке OpenCV, плохая интерпретация результатов и т. Д...

edit2:

Используя очки из вашего кода, мне удалось получить следующие результаты:

rvec = [[-158.56293283], [1.46777938], [-17.32569125]]
tvec = [[-36.23910413], [-82.83704819], [266.03157578]]

К сожалению, для меня трудно сказать, хорошие результаты или нет... Единственное, что может быть неправильным для меня, это то, что 2 угла отличаются от 0 (или 180). Но если вы измените последний ряд points2d от (355,37), (353,72), (353,101) в

(355,37), (355, 72), (355, 101)

(я думаю, это ваша ошибка, а не правильный результат) вы получите:

rvec = [[-159.34101842], [1.04951033], [-11.43731376]]
tvec = [[-25,74308282], [ -82,58461674], [ 268.12321097]]

что может быть намного ближе к правильному результату. Изменение матрицы камеры приводит к значительным изменениям результатов, поэтому рассмотрим тестирование значений из этого поста.

Обратите внимание, что все значения rvec умножаются на 180.0/3.14 - в C++ и в Python вектор rvec, возвращаемый solvePnPRansac, содержит углы в радианах.

Другие вопросы по тегам