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矩的一点心得,如有发现错误,请回复,我将尽快更改,同时也希望对和我一样的初学者有一丝丝的帮助。