icvGetRTMatrix函数详细注释

icvGetRTMatrix是opencv2.4.3中求取仿射变换系数的函数,定义在lkpyramid函数中,在此给出此函数的详细注释。

static void
icvGetRTMatrix( const CvPoint2D32f* a, const CvPoint2D32f* b,
                int count, CvMat* M, int full_affine )
{
    // CvPoint2D32f是结构体
	// a是待变换的图像中的特征点
	// b是参考图像中的特征点,M是变换矩阵
	if( full_affine )
    {
        //如果是全仿射变换,则仿射变换为:x'=m1*x+m2*y+m3,y'=m4*x+m5*y+m6,共有6个系数需求解
		double sa[36], sb[6];
        CvMat A = cvMat( 6, 6, CV_64F, sa ), B = cvMat( 6, 1, CV_64F, sb );  //CvMat是结构体(opencv2refman55页),函数cvMat给其成员赋值
        CvMat MM = cvMat( 6, 1, CV_64F, M->data.db ); // M->data.db是64位浮点型指针,指向M中的数据首地址

        int i;

        memset( sa, 0, sizeof(sa) );  //将sa对应的内存块初始化为0
        memset( sb, 0, sizeof(sb) );
		//这里要解Ax=B,即x中有6个仿射系数要求解,本来需要3对点的坐标就够了,但输入的a和b中可能不止3个点,因此需要将点坐标做一些相加和同乘\
		某系数的处理,以使得所有的点之构建出6个方程求解6个未知仿射系数
        for( i = 0; i < count; i++ )  //这段for的详解可见博文的分析
        {
            sa[0] += a[i].x*a[i].x;  
            sa[1] += a[i].y*a[i].x;
            sa[2] += a[i].x;

            sa[6] += a[i].x*a[i].y;
            sa[7] += a[i].y*a[i].y;
            sa[8] += a[i].y;

            sa[12] += a[i].x;
            sa[13] += a[i].y;
            sa[14] += 1;

            sb[0] += a[i].x*b[i].x;
            sb[1] += a[i].y*b[i].x;
            sb[2] += b[i].x;
            sb[3] += a[i].x*b[i].y;
            sb[4] += a[i].y*b[i].y;
            sb[5] += b[i].y;
        }

        sa[21] = sa[0];
        sa[22] = sa[1];
        sa[23] = sa[2];
        sa[27] = sa[6];
        sa[28] = sa[7];
        sa[29] = sa[8];
        sa[33] = sa[12];
        sa[34] = sa[13];
        sa[35] = sa[14];

        cvSolve( &A, &B, &MM, CV_SVD ); //解方程A*MM=B,A和B均已知
    }
    else  
    {
        //如果不是全仿射,则仿射变换为:x'=m1*x-m2*y+m3,y'=m1*y+m2*x+m4,共有4个系数需求解\
		关于全仿射和非全仿射区别可见opencv2refman中estimateRigidTransform函数的介绍
	double sa[16], sb[4], m[4], *om = M->data.db;
        CvMat A = cvMat( 4, 4, CV_64F, sa ), B = cvMat( 4, 1, CV_64F, sb );
        CvMat MM = cvMat( 4, 1, CV_64F, m );

        int i;

        memset( sa, 0, sizeof(sa) ); //对A初始化
        memset( sb, 0, sizeof(sb) );

        for( i = 0; i < count; i++ )
        {
            sa[0] += a[i].x*a[i].x + a[i].y*a[i].y;
            sa[1] += 0;
            sa[2] += a[i].x;
            sa[3] += a[i].y;

            sa[4] += 0;
            sa[5] += a[i].x*a[i].x + a[i].y*a[i].y;
            sa[6] += -a[i].y;
            sa[7] += a[i].x;

            sa[8] += a[i].x;
            sa[9] += -a[i].y;
            sa[10] += 1;
            sa[11] += 0;

            sa[12] += a[i].y;
            sa[13] += a[i].x;
            sa[14] += 0;
            sa[15] += 1;

            sb[0] += a[i].x*b[i].x + a[i].y*b[i].y;
            sb[1] += a[i].x*b[i].y - a[i].y*b[i].x;
            sb[2] += b[i].x;
            sb[3] += b[i].y;
        }

        cvSolve( &A, &B, &MM, CV_SVD );

        om[0] = om[4] = m[0];  //将求得的系数存进M中
        om[1] = -m[1];
        om[3] = m[1];
        om[2] = m[2];
        om[5] = m[3];
    }
}
关于上面那段for循环,其实是在用a和b中的坐标点构建AM=B中的A矩阵和B矩阵,A和B都构建出来,才能求取仿射系数组成的M矩阵,通常仿射系数有6个参数要求,所以其实只要3对点就能求出M,也就是像酱紫的:

icvGetRTMatrix函数详细注释_第1张图片

其中(x1,y1)、(x2,y2)、(x3,y3)、(x1’,y1‘)、(x2’,y2‘)、(x3,y3’)分别是3个点的坐标,但是opencv中并未采取这种做法来构建A和B,而是将A和B构建成了下面这样,那段for循环就是在构建A和B:

icvGetRTMatrix函数详细注释_第2张图片

注意到A和B中的每个元素都是所有特征点的坐标根据某种规则相加得到的,这样做的好处是不管a和b中有多少个特征点(并不一定要是3个),反正将所有点都会叠加,因此构建出的A总是6*6大小的,并且opencv构建时并不是随便构建的,拿A的第一行乘以M得到xx'为例,这是在m0*x+m1*y+m3=x'等式两边同乘了x,因此它仍然是成立的,同样第二行是在等式两边同乘y得到的,因此也成立。不过问题来了,这样得到的上面3个等式不是一样的吗?下面3个也是一样的呀?这样不是不能求出6个未知数的M吗?注意刚刚说过,这里A中的每个元素是所有点相加的结果,对于一个单独的具体的点,上面3个等式是一样的,但是所有点求和之后等式就不一样了。

对于非全仿射的系数矩阵求解,整体思路是一模一样的,只不过只要求取4个系数,而非6个。


你可能感兴趣的:(仿射变换,系数)