对于超定方程组的仿射变换问题,有一个简单的解决方案。
- 需要注意的是,通常情况下,通过使用伪逆或类似技术,仿射变换可以找到线性方程组Ax=B的解,因此
x = (A At )-1 At B
此外,在核心openCV功能中,只需调用solve(A, B, X)即可处理此问题。
熟悉opencv/modules/imgproc/src/imgwarp.cpp中的仿射变换代码,它实际上只执行两个操作:
a. 重新排列输入以创建Ax=B系统;
b. 然后调用solve(A, B, X)。
注意:忽略openCV代码中的函数注释-它们会让人感到困惑,并且不反映矩阵中元素的实际顺序。如果您正在解决[u,v]’= Affine * [x,y,1],则重新排列如下:
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
您只需添加更多的点即可。想要使Solve(A, B, X)适用于过度确定的系统,请添加DECOMP_SVD参数。如果您想查看有关此主题的幻灯片,请使用此
链接。如果您想在计算机视觉背景下了解伪逆的更多信息,最好的来源是:
ComputerVision,请参见第15章和附录C。
如果您仍然不确定如何添加更多的点,请参考以下代码:
cv::Mat getAffineTransformOverdetermined( const Point2f src[], const Point2f dst[], int n )
{
Mat M(2, 3, CV_64F), X(6, 1, CV_64F, M.data);
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);
for( int i = 0; i < n; i++ )
{
int j = i*12;
int k = i*12+6;
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;
}
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;
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;