findHomography, getPerspectiveTransform, & getAffineTransform
cette question Est sur les fonctions OpenCV findHomography
,getPerspectiveTransform
&getAffineTransform
Quelle est la différence entre
findHomography
etgetPerspectiveTransform
?. Ma compréhension de la documentation est quegetPerspectiveTransform
calcule la transformation en utilisant 4 correspondances (ce qui est le minimum requis pour calculer une transformation homographie/perspective) où commefindHomography
calcule la transformation même si vous fournissez plus de 4 correspondances (probablement en utilisant quelque chose comme les moindres carrés méthode?). Est-ce correct? (Dans ce cas, la seule raison pour laquelle OpenCV continue à soutenir getPerspectiveTransform devrait être legacy? )ma prochaine préoccupation est que je veux savoir s'il y a un équivalent à
findHomography
pour calculer une transformation Affine? c'est-à-dire une fonction qui utilise une méthode des moindres carrés ou une méthode robuste équivalente pour calculer et affiner la transformation. Selon la documentationgetAffineTransform
prend en seulement 3 correspondances (ce qui est le minimum requis pour calculer une transformation affine).
,
4 réponses
Q #1: à droite, la findhomographie tente de trouver la meilleure transformation entre deux ensembles de points. Il utilise quelque chose de plus intelligent que les moindres carrés, appelé RANSAC, qui a la capacité de rejeter les valeurs aberrantes - si au moins 50% + 1 de vos points de données sont OK, RANSAC fera de son mieux pour les trouver, et construire une transformation fiable.
le getPerspectiveTransform a beaucoup de raisons utiles pour rester - il est la base pour la findHomography, et il est utile dans de nombreuses situations où vous n'avez que 4 points, et vous savez que ce sont les bons. La findhomographie est habituellement utilisée avec des ensembles de points détectés automatiquement - vous pouvez trouver beaucoup d'entre eux, mais avec une faible confiance. getPerspectiveTransform est bon quand vous savez à coup sûr 4 coins-comme le marquage manuel, ou la détection automatique d'un rectangle.
Q #2
je suis d'accord avec tout ce que @vasile a écrit. Je veux juste ajouter quelques observations:
getPerspectiveTransform()
et getAffineTransform()
sont destinés à travailler sur 4 ou 3 points (respectivement), qui sont connu pour être correct correspondances. Sur des images réelles prises avec un vrai appareil photo, vous pouvez jamais obtenir des correspondances aussi précises, pas avec marquage automatique ou manuel des points correspondants.
il y a toujours les valeurs aberrantes. il suffit de regarder le cas simple de vouloir ajuster une courbe à travers des points (par exemple prendre une équation générative avec le bruit y1 = f(x) = 3.12x + gauss_noise
ou y2 = g(x) = 0.1x^2 + 3.1x + gauss_noise
): il sera beaucoup plus facile de trouver une bonne fonction quadratique pour estimer les points dans les deux cas, qu'une bonne linéaire. Quadratique peut être un surmenage, mais dans la plupart des cas ne le sera pas (après avoir enlevé les valeurs aberrantes), et si vous voulez mettre une ligne droite là-bas, vous avez intérêt à être sûr que c'est le bon modèle, sinon vous êtes allez obtenir inutilisable résultats.
cela dit, si vous sont puissamment bien sûr que la transformation affine est la bonne, voici une suggestion:
- utiliser
findHomography
, a RANSAC incorporé à la fonctionnalité, pour se débarrasser des valeurs aberrantes et obtenir une estimation initiale de la transformation de l'image - choisir 3 correspondances correctes-correspondances (qui correspondent à l'homographie trouvée), ou reprojecter 3 points de la 1ère image pour la 2ème (en utilisant l'homographie)
- utilisez ces 3 correspondances (qui sont aussi près de corriger que vous pouvez obtenir) dans
getAffineTransform()
- envelopper tout cela dans votre propre
findAffine()
si vous le souhaitez et le tour est joué!
Re Q#2, estimateRigidTransform est l'équivalent suréchantillonné de getAffineTransform. Je ne sais pas si C'était dans OCV quand ça a été posté, mais c'est disponible en 2.4.
- notez qu'en général une transformation Affine trouve une solution au système sur-déterminé D'équations linéaires Ax=B en utilisant un pseudo-inverse ou une technique similaire, donc
x = (A A t) -1 t B
de plus, ceci est traité dans l'openCV de base fonctionnalité par un simple appel à résoudre(A, B, X).
familiarisez-vous avec le code de transformation Affine dans opencv/modules/imgproc/src/imgwarp.rpc: il n'a vraiment juste deux choses:
b. ensuite, les appels solve(A, B, X);
NOTE: ignorer les commentaires de fonction dans le code openCV-ils sont confus et ne reflètent pas l'ordre réel des éléments dans le matrice. Si vous résolvez [u, v]’= Affine * [x, y, 1] le réarrangement est:
x1 y1 1 0 0 1
0 0 0 x1 y1 1
x2 y2 1 0 0 1
A = 0 0 0 x2 y2 1
x3 y3 1 0 0 1
0 0 0 x3 y3 1
X = [Affine11, Affine12, Affine13, Affine21, Affine22, Affine23]’
u1 v1
B = u2 v2
u3 v3
Tout ce que vous devez faire est d'ajouter plus de points. Pour faire fonctionner Solve(A, B, X) sur un système sur-déterminé, ajoutez le paramètre DECOMP_SVD. Pour voir les diapositives powerpoint sur le sujet, utilisez ce lien. Si vous souhaitez en savoir plus sur le pseudo-inverse dans le contexte de la vision informatique, la meilleure source est: ComputerVision, voir le chapitre 15 et l'annexe C.
si vous ne savez toujours pas comment ajouter plus de points voir mon code ci-dessous:
// extension for n points;
cv::Mat getAffineTransformOverdetermined( const Point2f src[], const Point2f dst[], int n )
{
Mat M(2, 3, CV_64F), X(6, 1, CV_64F, M.data); // output
double* a = (double*)malloc(12*n*sizeof(double));
double* b = (double*)malloc(2*n*sizeof(double));
Mat A(2*n, 6, CV_64F, a), B(2*n, 1, CV_64F, b); // input
for( int i = 0; i < n; i++ )
{
int j = i*12; // 2 equations (in x, y) with 6 members: skip 12 elements
int k = i*12+6; // second equation: skip extra 6 elements
a[j] = a[k+3] = src[i].x;
a[j+1] = a[k+4] = src[i].y;
a[j+2] = a[k+5] = 1;
a[j+3] = a[j+4] = a[j+5] = 0;
a[k] = a[k+1] = a[k+2] = 0;
b[i*2] = dst[i].x;
b[i*2+1] = dst[i].y;
}
solve( A, B, X, DECOMP_SVD );
delete a;
delete b;
return M;
}
// call original transform
vector<Point2f> src(3);
vector<Point2f> dst(3);
src[0] = Point2f(0.0, 0.0);src[1] = Point2f(1.0, 0.0);src[2] = Point2f(0.0, 1.0);
dst[0] = Point2f(0.0, 0.0);dst[1] = Point2f(1.0, 0.0);dst[2] = Point2f(0.0, 1.0);
Mat M = getAffineTransform(Mat(src), Mat(dst));
cout<<M<<endl;
// call new transform
src.resize(4); src[3] = Point2f(22, 2);
dst.resize(4); dst[3] = Point2f(22, 2);
Mat M2 = getAffineTransformOverdetermined(src.data(), dst.data(), src.size());
cout<<M2<<endl;