I am using estimateRigidTransform with about two vectors of 100 points and is working FINE. But somehow getAffineTransform doesn't work.
I know that findHomography finds the best matrix using RANSAC and getPerspectiveTransform needs only 4 points.
My question is what happens if I give more inputs in estimateRigidTransform or getAffineTransform?
Does it take only 4 points from input matrix? Or do some kind of RANSAC?
warpAffine() that applies an affine transformation to an image. You just need to provide the transformation matrix (M). The basic syntax for the function is given below. Below is a sample code where the image is translated by (100, 50).
The affine transforms scale, rotate and shear are actually linear transforms and can be represented by a matrix multiplication of a point represented as a vector, [x y ] = [ax + by dx + ey ] = [a b d e ][x y ] , or x = Mx, where M is the matrix.
In Affine transformation, all parallel lines in the original image will still be parallel in the output image. To find the transformation matrix, we need three points from input image and their corresponding locations in the output image.
The functions you mentioned can be split into 3 different types:
Type 1: getAffineTransform and getPerspectiveTransform. Given 3 points on one plane and 3 matching points on another you can calculate affine transform between those planes. And given 4 points you can find perspective transform. This is all what getAffineTransform and getPerspectiveTransform can do: they require 3 and 4 pairs of points, no more no less, and calculate relevant transform. The one and the only.
Type 2: estimateRigidTransform. If you can't get your points with absolute precision (which is normally the case when you get them from an image) then you need more than 3 pairs of points to reduce the error. The more the merrier (i.e. better accuracy). There more than one way to define the error you want to reduce, as well as what approach you use to find the minimal error. estimateRigidTransform is minimizing least square error (I think the most popular error definition). It does this by solving system of equations. If you provide 3 point exactly then the result, of course, will be the same as result of getAffineTransform. You can ask why we need getAffineTransform in OpenCV if estimateRigidTransform can do its work. Alas, this is not the only redundancy in OpenCV.
Type 3: findHomography. This one is more advanced. Not only it can deal with errors in locations of points, but it can also deal with existence of the outliers. If there some wrong matches between points then using them for least square error estimation will lead to very bad accuracy. It can use RANSAC or LMeDs to test possible matches and eliminate those outliers. It works like multiple iterations of estimateRigidTransform: find least square match for different sub-sets of points. If you know that no outliers are present then you can set the 'method' argument to 0, and it will work like estimateRigidTransform - by trying to minimize least square error created from match of all points.
Edit. Thank to Micka for his comment.
I guess my memory is playing tricks on me. I remembered that estimateRigidTransform was implemented through system of equations in OpenCV, but now I checked it and saw that Micka is right. It does uses some hard coded RANSAC in it... Sorry for misguiding you.
For those who are still interested in having closed form solution instead of RANSAC, here it is:
// find affine transformation between two pointsets (use least square matching)
static bool computeAffine(const vector<Point2d> &srcPoints, const vector<Point2d> &dstPoints, Mat &transf)
{
// sanity check
if ((srcPoints.size() < 3) || (srcPoints.size() != dstPoints.size()))
return false;
// container for output
transf.create(2, 3, CV_64F);
// fill the matrices
const int n = (int)srcPoints.size(), m = 3;
Mat A(n,m,CV_64F), xc(n,1,CV_64F), yc(n,1,CV_64F);
for(int i=0; i<n; i++)
{
double x = srcPoints[i].x, y = srcPoints[i].y;
double rowI[m] = {x, y, 1};
Mat(1,m,CV_64F,rowI).copyTo(A.row(i));
xc.at<double>(i,0) = dstPoints[i].x;
yc.at<double>(i,0) = dstPoints[i].y;
}
// solve linear equations (for x and for y)
Mat aTa, resX, resY;
mulTransposed(A, aTa, true);
solve(aTa, A.t()*xc, resX, DECOMP_CHOLESKY);
solve(aTa, A.t()*yc, resY, DECOMP_CHOLESKY);
// store result
memcpy(transf.ptr<double>(0), resX.data, m*sizeof(double));
memcpy(transf.ptr<double>(1), resY.data, m*sizeof(double));
return true;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With