Hu矩的确很神奇,它具有平移不变性、旋转不变性和缩放不变性,是图形匹配的一个不错的工具。

通过大致对Hu矩的学习,我认为对Hu矩的学习应该有一下几步,第一步要了解什么是矩;第二步再开始了解Hu矩。为了方便大家的使用,先简单介绍下Hu矩用于模板匹配的用法。

用法:

其实Hu矩用于匹配已经在opencv中的cvMatchShape函数中应用了,下面是cvMatchShape的源代码(可以跳过):

cvMatchShapes( const void* contour1, const void* contour2,
                int method, double )
 {
     CvMoments moments;
     CvHuMoments huMoments;
     double ma[7], mb[7];
     int i, sma, smb;
     double eps = 1.e-5;
     double mmm;
     double result = 0;    CV_FUNCNAME( "cvMatchShapes" );
    __BEGIN__;
    if( !contour1 || !contour2 )
         CV_ERROR( CV_StsNullPtr, "" );     CV_CALL( cvMoments( contour1, &moments ));
     CV_CALL( cvGetHuMoments( &moments, &huMoments ));
    ma[0] = huMoments.hu1;
     ma[1] = huMoments.hu2;
     ma[2] = huMoments.hu3;
     ma[3] = huMoments.hu4;
     ma[4] = huMoments.hu5;
     ma[5] = huMoments.hu6;
     ma[6] = huMoments.hu7;     CV_CALL( cvMoments( contour2, &moments ));
     CV_CALL( cvGetHuMoments( &moments, &huMoments ));
    mb[0] = huMoments.hu1;
     mb[1] = huMoments.hu2;
     mb[2] = huMoments.hu3;
     mb[3] = huMoments.hu4;
     mb[4] = huMoments.hu5;
     mb[5] = huMoments.hu6;
     mb[6] = huMoments.hu7;    switch (method)
     {
     case 1:
         {
             for( i = 0; i < 7; i++ )
             {
                 double ama = fabs( ma[i] );
                 double amb = fabs( mb[i] );                if( ma[i] > 0 )
                     sma = 1;
                 else if( ma[i] < 0 )
                     sma = -1;
                 else
                     sma = 0;
                 if( mb[i] > 0 )
                     smb = 1;
                 else if( mb[i] < 0 )
                     smb = -1;
                 else
                     smb = 0;                if( ama > eps && amb > eps )
                 {
                     ama = 1. / (sma * log10( ama ));
                     amb = 1. / (smb * log10( amb ));
                     result += fabs( -ama + amb );
                 }
             }
             break;
         }

    case 2:

{
             for( i = 0; i < 7; i++ )
             {
                 double ama = fabs( ma[i] );
                 double amb = fabs( mb[i] );                if( ma[i] > 0 )
                     sma = 1;
                 else if( ma[i] < 0 )
                     sma = -1;
                 else
                     sma = 0;
                 if( mb[i] > 0 )
                     smb = 1;
                 else if( mb[i] < 0 )
                     smb = -1;
                 else
                     smb = 0;                if( ama > eps && amb > eps )
                 {
                     ama = sma * log10( ama );
                     amb = smb * log10( amb );
                     result += fabs( -ama + amb );
                 }
             }
             break;
         }

    case 3:

{
             for( i = 0; i < 7; i++ )
             {
                 double ama = fabs( ma[i] );
                 double amb = fabs( mb[i] );                if( ma[i] > 0 )
                     sma = 1;
                 else if( ma[i] < 0 )
                     sma = -1;
                 else
                     sma = 0;
                 if( mb[i] > 0 )
                     smb = 1;
                 else if( mb[i] < 0 )
                     smb = -1;
                 else
                     smb = 0;                if( ama > eps && amb > eps )
                 {
                     ama = sma * log10( ama );
                     amb = smb * log10( amb );
                     mmm = fabs( (ama - amb) / ama );
                     if( result < mmm )
                         result = mmm;
                 }
             }
             break;
         }
     default:
         CV_ERROR_FROM_STATUS( CV_BADCOEF_ERR );
     }

    __END__;

    return result;
}

因此,我们只需要传入两个轮廓或两幅图片,再选择一种评价的方式,就可以用这个函数进行匹配了,至于hu矩,在这个函数中已经帮我们计算好了,我们只需要选择一种匹配的方法就可以的到一个评价相似性的值了,该函数提供了三种方法来评价这两幅图的相似性,值越小越相似。

如果我们要单独计算hu矩怎么办??

opencv中有一个结构体叫CvMoments,他的定义是

typedef struct CvMoments
      {
           double  m00, m10, m01, m20, m11, m02, m30, m21, m12, m03;
           double  mu20, mu11, mu02, mu30, mu21, mu12, mu03;
           double  inv_sqrt_m00;
      }
      CvMoments;

这个结构体是用来保存矩的数据的,其中第一行是普通矩,第二行是中心距。接着有一个函数叫做CVAPI(void) cvMoments( const CvArr* arr, CvMoments* moments, int binary CV_DEFAULT(0));,这个函数是用来计算矩的,其中第一个参数是传入的图像,第二个参数是之前创建的CvMoments变量,第三个参数是是否二值化(如果是0的话,那么原图像是什么值就是什么值,如果大于0,就二值化,其中阀值我也不知道怎么确定的,但是和这个参数是没有关系的)。

同样,要计算Hu矩也有一个结构体叫CvHuMoments,他的定义是

typedef struct CvHuMoments
      {
            double hu1, hu2, hu3, hu4, hu5, hu6, hu7;
      }
        CvHuMoments;

这里用于储存计算出来的七个值,同时又有一个函数叫CVAPI(void) cvGetHuMoments( CvMoments*  moments, CvHuMoments*  hu_moments );用来计算Hu矩,其中第一个参数就是上面所计算好的moments,第二个参数就是刚创建的CvHuMoments hu_moments,计算的结果就保存的这个变量中。

以上就是我认为用hu矩进行模板匹配的方法了。

接下来我按照第二段所述的步骤介绍下我对Hu矩的理解:

第一步什么是矩:个人认为,矩的理解可以从力矩上,在高中物理上力矩的定义是力乘以力臂,所以在图像中的矩也可以这么认为,它的定义参照中所述,其中Xpq中p+q的值称为矩的阶数。

第二步什么是hu矩:hu矩就是利用矩里边的1阶矩、二阶矩和三阶矩经过算术运算构造出来的具有平移、缩放和旋转不变性的七个矩,在上面的网址中也有讲到,我也是从上面的那个网址中学习过。

本人初学opencv,以上仅是本人学习Hu矩的一点心得,如有发现错误,请回复,我将尽快更改,同时也希望对和我一样的初学者有一丝丝的帮助。