Проблемы с обнаружением 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 2]()
![photo 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
являются координатами углов маркера в обработке изображения (для этого я использую собственный алгоритм)
Ответы
Ответ 1
Чтобы правильно отладить вашу проблему, мне понадобится весь код: -)
Я предполагаю, что вы выполняете подход, предложенный в учебниках (calibration и pose), приведенный @kobejohn в его comment и чтобы ваш код выполнял следующие действия:
- собирать различные изображения цели шахматной доски
- найти углы шахматной доски в изображениях точки 1)
- откалибруйте камеру (с помощью
cv::calibrateCamera
) и, таким образом, получите в результате внутренние параметры камеры (позвоните им intrinsic
) и параметры искажения объектива (позвоните им distortion
)
- собирайте изображение вашей собственной целевой цели (цель видна в 0:57 в вашем видео), и это показано в следуя рисунку
и найдите в нем некоторые соответствующие точки (позвольте называть точку, найденную вами на изображениях 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 1]()
![photo 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 вектора перевода маркера на этом расстоянии.
Фактически, когда вы видите свой куб сверху, куб отображается правильно.
Я сделал изображение, чтобы объяснить проблему, но моя репутация не позволяет публиковать ее.