我使用的是带有两个100点向量的estimateRigidTransform,并且工作正常。 但不知怎的,getAffineTransform不起作用。
我知道findHomography使用RANSAC找到最佳矩阵,getPerspectiveTransform只需要4个点。
我的问题是如果我在estimateRigidTransform或getAffineTransform中提供更多输入会发生什么?
输入矩阵只需要4个点吗?或者做某种RANSAC?
答案 0 :(得分:18)
您提到的功能可以分为3种不同的类型:
类型1:getAffineTransform和getPerspectiveTransform。给定一个平面上的3个点和另一个平面上的3个匹配点,您可以计算这些平面之间的仿射变换。给出4分,你可以找到透视变换。这就是getAffineTransform和getPerspectiveTransform可以做的事情:它们需要3对和4对点,不多也不少,并计算相关变换。唯一的。
类型2:estimateRigidTransform。如果你不能以绝对精度获得你的点(通常是从图像中获取它们的情况),那么你需要超过3对点来减少误差。越多越好(即更好的准确性)。有多种方法可以定义要减少的错误,以及用于查找最小错误的方法。 estimateRigidTransform正在最小化最小平方误差(我认为最流行的错误定义)。它通过求解方程组来实现。如果你提供3点,那么结果当然与getAffineTransform的结果相同。如果estimateRigidTransform可以完成其工作,您可以问我们为什么需要在OpenCV中使用getAffineTransform。唉,这不是OpenCV中唯一的冗余。
类型3:findHomography。这个更先进。它不仅可以处理点位置的错误,而且还可以处理异常值的存在。如果点之间存在一些错误的匹配,那么使用它们进行最小平方误差估计将导致非常差的精度。它可以使用RANSAC或LMeD来测试可能的匹配并消除这些异常值。它的工作方式类似于estimateRigidTransform的多次迭代:找到不同子点集的最小二乘匹配。如果您知道没有异常值,那么您可以设置'方法'参数为0,它将像estimateRigidTransform一样工作 - 通过尝试最小化从所有点的匹配创建的最小平方误差。
修改。感谢Micka的评论。
我想我的记忆在欺骗我。我记得estimateRigidTransform是通过OpenCV中的方程系统实现的,但现在我检查了它,看到Micka是对的。它确实使用了一些硬编码的RANSAC ...抱歉误导了你。
对于那些仍然有兴趣使用封闭式解决方案代替RANSAC的人来说,这里是:
// 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;
}