OpenCV: problèmes de détection de solvePnP
j'ai un problème avec la détection précise des marqueurs en utilisant OpenCV.
j'ai enregistré une vidéo présentant ce numéro: http://youtu.be/IeSSW4MdyfU
comme vous le voyez, je détecte des marqueurs légèrement déplacés à certains angles de caméra. J'ai lu sur le web que c'est peut-être un problème de calibrage de caméra, donc je vais vous dire comment je calibre la caméra, et peut-être que vous pourriez me dire ce que je fais de mal?
au début, je collecte des données à partir de diverses images, et je stocke les coins de calibration dans _imagePoints
vecteur comme celui-ci
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);
que, après avoir recueilli suffisamment de données, je calcule la matrice de la caméra et les coefficients avec ce code:
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);
résultats sont comme vous avez vu dans la vidéo.
Qu'est-ce que je fais de mal? est-ce un problème dans le code? Combien d'images dois-je utiliser pour effectuer calibrage (en ce moment, j'essaie d'obtenir 20-30 images avant la fin du calibrage).
est-ce que je devrais utiliser des images qui containg mal détecté coins de l'échiquier, comme ceci:
ou devrais-je utiliser uniquement des échiquiers correctement détectés comme ceux-ci:
j'ai expérimenté la grille des cercles au lieu des échiquiers, mais les résultats étaient bien pires que maintenant.
dans le cas de questions comment je détecte marqueur: j'utilise solvepnp
fonction:
solvePnP(modelPoints, imagePoints, [_arEngine currentCameraMatrix], _userDefaultsManager.distCoeffs, rvec, tvec);
avec les points de modèle spécifiés comme suit:
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));
et imagePoints
sont des coordonnées de coins de marqueur dans le traitement de l'image (j'utilise un algorithme personnalisé pour ce faire)
3 réponses
pour déboguer correctement votre problème j'aurais besoin de tout le code: -)
je suppose que vous suivez l'approche suggérée dans les tutoriels ( calibrage et pose ) cité par @kobejohn dans son commentaire et de sorte que votre code suit ces étapes:
- recueillir les différentes images de l'échiquier cible
- trouver des coins de planches dans les images du point 1)
- calibrer la caméra (avec
cv::calibrateCamera
) et ainsi obtenir les paramètres intrinsèques de la caméra (appelons-lesintrinsic
) et les paramètres de distorsion de la lentille (appelons-lesdistortion
) - collectez une image de votre propre cible personnalisée (la cible est vue à 0: 57 dans votre vidéo ) et il est montré dans la figure suivante et trouver quelques points pertinents dans elle (appelons le point que vous avez trouvé dans l'image
image_custom_target_vertices
etworld_custom_target_vertices
les points 3D correspondants). - estimez la matrice de rotation (appelons-le
R
) et le vecteur de traduction (appelons-let
) de la caméra à partir de l'image de votre propre cible personnalisée que vous obtenez en point 4, avec un appel àcv::solvePnP
comme celui-cicv::solvePnP(world_custom_target_vertices,image_custom_target_vertices,intrinsic,distortion,R,t)
- donnant les 8 coins cube en 3D (appelons-les
world_cube_vertices
) vous obtenez les 8 points image 2D (appelons-lesimage_cube_vertices
) au moyen d'un appel àcv2::projectPoints
comme celui-cicv::projectPoints(world_cube_vertices,R,t,intrinsic,distortion,image_cube_vertices)
- dessinez le cube avec votre propre fonction
draw
.
Maintenant, le résultat final de la procédure de tirage dépend de toutes les données calculées et nous devons trouver où se situe le problème:
Étalonnage : comme vous observé dans votre réponse , en 3) vous devriez jeter les images où les coins ne sont pas correctement détectés. Vous avez besoin d'un seuil pour l'erreur de reprojection afin de rejeter les "mauvaises" images de cible d'échiquier. Extrait du tutoriel d'étalonnage :
erreur de re-projection
Re-projection d'erreur donne une bonne estimation de la juste façon exacte est la trouvé paramètres. Ce devrait être aussi proche de zéro que possible. Donné les matrices intrinsèques, de distorsion, de rotation et de translation, nous transformez le point de l'objet en point de l'image en utilisant cv2.projectPoints (). Ensuite, nous calculons la norme absolue entre ce que nous avons eu avec notre la transformation et la coin de l'algorithme de recherche. Pour trouver la moyenne erreur nous calculons la moyenne arithmétique des erreurs calculées pour toutes les images de calibrage.
habituellement, vous trouverez un seuil approprié avec quelques expériences. Avec cette étape supplémentaire, vous obtiendrez de meilleures valeurs pour intrinsic
et distortion
.
recherche personnalisée cible : il ne me semble pas que vous m'expliquiez comment vous trouver votre propre cible de l'étape, j'ai étiqueté comme point 4). Vous avez le image_custom_target_vertices
attendu ? Rejetez-vous les images dont les résultats sont "mauvais"?
Pose de la caméra : I pensez que dans 5) vous utilisez intrinsic
trouvé dans 3), êtes-vous sûr que rien n'est changé dans la caméra dans l'intervalle? Se référant à la deuxième règle de Callari D'étalonnage de la caméra :
deuxième règle de calibrage de L'appareil photo: "tu ne dois pas toucher l'objectif après l'étalonnage". En particulier, vous ne pouvez pas recentrer ni modifier l' f-stop, parce que la focalisation et l'iris affectent la lentille non linéaire la distorsion et (mais moins, selon l'objectif) le champ de vue. Bien sûr, vous êtes complètement libre de changer le temps d'exposition, comme il n'affecte pas la géométrie de l'Objectif du tout.
et puis il peut y avoir des problèmes dans la fonction draw
.
donc, j'ai fait beaucoup d'expériences avec mon code, et je n'ai toujours pas corrigé le problème principal (objets déplacés), mais j'ai réussi à répondre à certaines des questions de calibrage que j'ai posées.
tout D'abord - pour obtenir de bons résultats d'étalonnage vous devez utiliser des images avec des éléments de grille correctement détectés/positions de cercles! . L'utilisation de toutes les images capturées dans le processus de calibrage (même ceux qui ne sont pas correctement détectés) résultera mauvais étalonnage.
j'ai expérimenté divers modèles de calibration:
- Asymmetric circles pattern (
CALIB_CB_ASYMMETRIC_GRID
), donnent des résultats bien pires que tout autre pattern. Par mauvais résultats, je veux dire qu'il produit beaucoup de coins mal détectés comme ceux-ci:
j'ai expérimenté avec CALIB_CB_CLUSTERING
et il n'a pas aidé beaucoup - dans certains cas (environnement lumineux différent) il est devenu meilleur, mais pas beaucoup.
- "Symmetric circles pattern (
CALIB_CB_SYMMETRIC_GRID
) - de meilleurs résultats que la grille asymétrique, mais j'ai quand même des résultats bien pires que la grille standard (échiquier). Il produit souvent des erreurs comme celles-ci:
- échiquier (trouvé en utilisant la fonction
findChessboardCorners
) - cette méthode produit les meilleurs résultats possibles - elle ne produit pas des coins mal alignés très souvent, et presque chaque étalonnage produit des résultats similaires aux meilleurs résultats possibles de grille de cercles symétriques "
pour chaque calibration j'ai utilisé 20-30 images qui venaient de différents angles. J'ai essayé même avec plus de 100 images mais il n'a pas produit un changement notable des résultats d'étalonnage par rapport à une plus petite quantité d'images. Il est intéressant de noter qu'un plus grand nombre d'images de test augmente le temps nécessaire pour calculer les paramètres de la caméra de manière non linéaire (100 images de test en 480x360 résolution calculent 25 minutes en iPad4, contre 4 minutes avec ~50 images)
j'ai aussi expérimenté les paramètres solvePNP
- mais je n'ai pas non plus obtenu de résultats acceptables: j'ai essayé les 3 méthodes de détection ( ITERATIVE
, EPNP
et P3P
), mais je n'ai pas vu de changement notable.
aussi j'ai essayé avec useExtrinsicGuess
mis à true
, et j'ai utilisé rvec
et tvec
de la détection précédente, mais celui-ci a abouti à la disparition complète de cube détecté.
je suis à court d'idées - quoi d'autre pourrait affecter ces problèmes changeants?
pour ceux qui sont encore intéressés: c'est une vieille question, mais je pense que votre problème n'est pas de la mauvaise calibration. J'ai développé une application AR pour iOS, en utilisant OpenCV et SceneKit, et j'ai eu votre même problème.
je pense que votre problème est la mauvaise position du rendu du cube: Le solvePnP d'OpenCV renvoie les coordonnées X, Y, Z du centre du marqueur, mais vous voulez rendre le cube au-dessus du marqueur, à une distance spécifique le long de l'axe Z du marqueur, exactement à la moitié de la taille du côté cube. Vous devez donc améliorer la coordonnée Z du vecteur de translation du marqueur de cette distance.
en fait, quand vous voyez votre cube du haut, le cube est rendu correctement. J'ai fait une image pour expliquer le problème, mais ma réputation empêcher de la publier.