icvGetRTMatrix函数详细注释

来源:互联网 发布:java过滤器的应用场景 编辑:程序博客网 时间:2024/05/16 03:09

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

static voidicvGetRTMatrix( 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,也就是像酱紫的:


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


注意到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个。


1 0
原创粉丝点击