Подтвердить что ты не робот

Проблемы с обнаружением OpenCV: solvePnP

У меня проблема с точным обнаружением маркеров с использованием OpenCV.

Я записал видео, представляющее эту проблему: http://youtu.be/IeSSW4MdyfU

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

В начале я собираю данные из разных изображений и сохраняю углы калибровки в _imagePoints, как это показано

std::vector<cv::Point2f> corners;
_imageSize = cvSize(image->size().width, image->size().height);

bool found = cv::findChessboardCorners(*image, _patternSize, corners);

if (found) {
    cv::Mat *gray_image = new cv::Mat(image->size().height, image->size().width, CV_8UC1);
    cv::cvtColor(*image, *gray_image, CV_RGB2GRAY);

    cv::cornerSubPix(*gray_image, corners, cvSize(11, 11), cvSize(-1, -1), cvTermCriteria(CV_TERMCRIT_EPS+ CV_TERMCRIT_ITER, 30, 0.1));

    cv::drawChessboardCorners(*image, _patternSize, corners, found);
}

_imagePoints->push_back(_corners);

Затем, после сбора достаточного количества данных, я вычисляю матрицу камеры и коэффициенты с помощью этого кода:

std::vector< std::vector<cv::Point3f> > *objectPoints = new std::vector< std::vector< cv::Point3f> >();

for (unsigned long i = 0; i < _imagePoints->size(); i++) {
    std::vector<cv::Point2f> currentImagePoints = _imagePoints->at(i);
    std::vector<cv::Point3f> currentObjectPoints;

    for (int j = 0; j < currentImagePoints.size(); j++) {
        cv::Point3f newPoint = cv::Point3f(j % _patternSize.width, j / _patternSize.width, 0);

        currentObjectPoints.push_back(newPoint);
    }

    objectPoints->push_back(currentObjectPoints);
}

std::vector<cv::Mat> rvecs, tvecs;

static CGSize size = CGSizeMake(_imageSize.width, _imageSize.height);
cv::Mat cameraMatrix = [_userDefaultsManager cameraMatrixwithCurrentResolution:size]; // previously detected matrix
cv::Mat coeffs = _userDefaultsManager.distCoeffs; // previously detected coeffs
cv::calibrateCamera(*objectPoints, *_imagePoints, _imageSize, cameraMatrix, coeffs, rvecs, tvecs);

Результаты, как вы видели в видео.

Что я делаю неправильно? это проблема в коде? Сколько изображений следует использовать для выполнения калибровки (сейчас я пытаюсь получить 20-30 изображений до окончания калибровки).

Должен ли я использовать изображения, которые содержат неправильно обнаруженные углы шахматной доски, например:

photo 1

или я должен использовать только правильно обнаруженные шахматные доски, подобные этим:

photo 2photo 3

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

В случае вопросов, как я обнаруживаю маркер: я использую функцию solvepnp:

solvePnP(modelPoints, imagePoints, [_arEngine currentCameraMatrix], _userDefaultsManager.distCoeffs, rvec, tvec);

с параметрами модели, указанными следующим образом:

    markerPoints3D.push_back(cv::Point3d(-kMarkerRealSize / 2.0f, -kMarkerRealSize / 2.0f, 0));
    markerPoints3D.push_back(cv::Point3d(kMarkerRealSize / 2.0f, -kMarkerRealSize / 2.0f, 0));
    markerPoints3D.push_back(cv::Point3d(kMarkerRealSize / 2.0f, kMarkerRealSize / 2.0f, 0));
    markerPoints3D.push_back(cv::Point3d(-kMarkerRealSize / 2.0f, kMarkerRealSize / 2.0f, 0));

и imagePoints являются координатами углов маркера в обработке изображения (для этого я использую собственный алгоритм)

4b9b3361

Ответ 1

Чтобы правильно отладить вашу проблему, мне понадобится весь код: -)

Я предполагаю, что вы выполняете подход, предложенный в учебниках (calibration и pose), приведенный @kobejohn в его comment и чтобы ваш код выполнял следующие действия:

  • собирать различные изображения цели шахматной доски
  • найти углы шахматной доски в изображениях точки 1)
  • откалибруйте камеру (с помощью cv::calibrateCamera) и, таким образом, получите в результате внутренние параметры камеры (позвоните им intrinsic) и параметры искажения объектива (позвоните им distortion)
  • собирайте изображение вашей собственной целевой цели (цель видна в 0:57 в вашем видео), и это показано в следуя рисунку Axadiw's own custom target и найдите в нем некоторые соответствующие точки (позвольте называть точку, найденную вами на изображениях image_custom_target_vertices и world_custom_target_vertices соответствующими 3D-точками).
  • оцените матрицу поворота (назовем ее R) и вектором перевода (пусть называют это t) камеры с изображения вашей собственной целевой цели, которую вы видите в пункте 4), с вызовом cv::solvePnP, как этот cv::solvePnP(world_custom_target_vertices,image_custom_target_vertices,intrinsic,distortion,R,t)
  • давая 8-угольный куб в 3D (позвоните им world_cube_vertices), вы получите 8 точек 2D-изображения (позвоните им image_cube_vertices) с помощью вызова cv2::projectPoints, подобного этому cv::projectPoints(world_cube_vertices,R,t,intrinsic,distortion,image_cube_vertices)
  • нарисуйте куб со своей собственной функцией draw.

Теперь конечный результат процедуры рисования зависит от всех предыдущих вычисленных данных, и мы должны найти, где лежит проблема:

Калибровка: как вы заметили в ответе , в 3) вы должны отбросить изображения, где углы не обнаружены должным образом. Вам нужно пороговое значение для ошибки повторения, чтобы отбросить "плохие" целевые изображения шахматной доски. Цитирование из руководство по калибровке:

Ошибка повторной проекции

Ошибка перепроекции дает хорошую оценку того, насколько точна найденные параметры. Это должно быть как можно ближе к нулю. Данный матрицы внутреннего, искажения, вращения и трансляции, сначала преобразуйте точку объекта в точку изображения с помощью cv2.projectPoints(). Затем мы вычисляем абсолютную норму между тем, что мы получили с нашим преобразования и алгоритма поиска углов. Найти средний мы вычисляем среднее арифметическое вычислений ошибок для все калибровочные изображения.

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

Поиск собственной пользовательской цели: мне не кажется, что вы объясняете, как вы находите свою собственную целевую аудиторию на шаге I, обозначенном как точка 4). Получаете ли вы ожидаемый image_custom_target_vertices? Вы отбрасываете изображения, где эти результаты являются "плохими"?

Поза камеры: я думаю, что в 5) вы используете intrinsic, найденный в 3), вы уверены, что в то же время ничего не изменилось в камере? Ссылаясь на Callari Второе правило калибровки камеры:

Второе правило калибровки камеры: "Не прикасайтесь к объективу после калибровки". В частности, вы не можете переориентировать или изменить f-stop, так как фокусировка и диафрагма влияют на нелинейную линзу искажения и (хотя и менее того, в зависимости от объектива) поле Посмотреть. Конечно, вы можете свободно менять время экспозиции, так как он вообще не влияет на геометрию объектива.

И тогда могут быть некоторые проблемы в функции draw.

Ответ 2

Итак, я много экспериментировал с моим кодом, и я все еще не исправил основную проблему (смещенные объекты), но мне удалось ответить на некоторые вопросы калибровки, которые я задал.

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

Я экспериментировал с различными шаблонами калибровки:

  • Шаблон асимметричных кругов (CALIB_CB_ASYMMETRIC_GRID) дает гораздо худшие результаты, чем любой другой шаблон. К худшим результатам я имею в виду, что он создает много ошибочно обнаруженных углов, подобных этим:

photo 1photo 2

Я экспериментировал с CALIB_CB_CLUSTERING, и он не очень помог - в некоторых случаях (в другой светлой среде) он улучшился, но не сильно.

  • Шаблон симметричных кругов (CALIB_CB_SYMMETRIC_GRID) - лучшие результаты, чем асимметричная сетка, но все же у меня есть намного худшие результаты, чем стандартная сетка (шахматная доска). Он часто вызывает такие ошибки:

photo 3

  • Шахматная доска (найдена с использованием функции findChessboardCorners) - этот метод дает наилучшие возможные результаты - он не производит неправильных углов очень часто, и почти каждая калибровка производит аналогичные результаты для наилучшего возможного результаты симметричной сетки кругов

Для каждой калибровки я использовал 20-30 изображений, которые исходили из разных ракурсов. Я пробовал даже со 100 + изображениями, но он не произвел заметных изменений в результатах калибровки, чем меньшее количество изображений. Стоит отметить, что большее количество тестовых изображений увеличивает время, необходимое для вычисления параметров камеры нелинейным способом (100 тестовых изображений в разрешении 480x360 вычисляют 25 минут в iPad4 по сравнению с 4 минутами с ~ 50 изображениями)

Я также экспериментировал с параметрами solvePNP, но также не дал мне никаких приемлемых результатов: я пробовал все 3 метода обнаружения (ITERATIVE, EPNP и P3P), но я не видели заметных изменений.

Также я попытался с useExtrinsicGuess установить на true, и я использовал rvec и tvec из предыдущего обнаружения, но это привело к полному исчезновению обнаруженного куба.

У меня закончились идеи - что еще может повлиять на эти проблемы?

Ответ 3

Для тех, кто еще заинтересован: это старый вопрос, но я думаю, что ваша проблема - не плохая калибровка. Я разработал приложение AR для iOS, используя OpenCV и SceneKit, и у меня была такая же проблема.

Я думаю, что ваша проблема - неправильная позиция рендера куба: OpenCV solvePnP возвращает координаты X, Y, Z центра маркеров, но вы хотите сделать куб над маркером на определенном расстоянии вдоль оси Z маркера ровно на половину размера стороны куба. Поэтому вам нужно улучшить координату Z вектора перевода маркера на этом расстоянии.

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