轮廓匹配、识别

OpenCv轮廓高级应用(轮廓匹配,几何直方图)

    最近再次用到了opencv轮廓,在这里结合作者冰山一角的博客(http://www.cnblogs.com/slysky/)以及自己的体会在此稍加说明。其程序主要参见冰山一角的Blog,遗憾的是代码是OpenCV1.0写的,等有时间再用2.4.2改写一篇。

    对于轮廓的相关数据结构表示和几本操作(查找轮廓,画轮廓),可参见前面两片关于轮廓的例程,在这里不多讲。

   对于查找轮廓我们一般要对图像Canny检测。但是对于很特殊的场合其实我们还可以直接对二值化的图像进行轮廓的提取,找出的轮廓其实就是Blob(这个可能就是为什么OpenCV高版本里面把blob分析抛弃的原因吧,我猜的话),画上外截矩形就是一个ROI,是不是觉得很有用?下面介绍罗阔的高级应用。

   轮廓的特性:

1.轮廓的多边形逼近
    轮廓的多边形逼近指的是:使用多边形来近似表示一个轮廓。
    多边形逼近的目的是为了减少轮廓的顶点数目。
    多边形逼近的结果依然是一个轮廓,只是这个轮廓相对要粗旷一些。
   可以使用方法cvApproxPoly()

2.轮廓的关键点
    轮廓的关键点是:轮廓上包含曲线信息比较多的点。关键点是轮廓顶点的子集。
    可以使用cvFindDominantPoints函数来获取轮廓上的关键点,该函数返回的结果一个包含 关键点在轮廓顶点中索引 的序列。再次强调:是索引,不是具体的点。如果要得到关键点的具体坐标,可以用索引到轮廓上去找。
3.轮廓的周长和面积
    轮廓的周长可以用cvContourPerimeter或者cvArcLength函数来获取。
    轮廓的面积可以用cvContourArea函数来获取。

4.轮廓的边界框
    有三种常见的边界框:矩形、圆形、椭圆。
    (1)矩形:在图像处理系统中提供了一种叫Rectangle的矩形,不过它只能表达边垂直或水平的特例;OpenCv中还有一种叫Box的矩形,它跟数学上的矩形一致,只要4个角是直角即可。
    如果要获取轮廓的Rectangle,可以使用cvBoundingRect函数。
    如果要获取轮廓的Box,可以使用cvMinAreaRect2函数。
    (2)圆形
    如果要获取轮廓的圆形边界框,可以使用cvMinEnclosingCircle函数。
    (3)椭圆
    如果要获取轮廓的椭圆边界框,可以使用cvFitEllipse2函数。
5.轮廓的矩

    矩是通过对轮廓上所有点进行积分运算(或者认为是求和运算)而得到的一个粗略特征。

在连续情况下,图像函数为 f(x,y),那么图像的p+q阶几何矩(标准矩)定义为:

p ,q = 0,1,2…… 

p+q阶中心距定义为:

 p,q = 0,1,2……

 

 

其中代表图像的重心,

 

,

 

对于离散的数字图像,采用求和号代替积分:

 

,,p,q = 0,1,2 ……

 

N和M分别是图像的高度和宽度;

归一化的中心距定义为:;其中

在公式中,p对应x维度上的矩,q对应y维度上的矩,阶数表示对应的部分的指数。该计算是对轮廓界上所有像素(数目为n)进行求和。如果p和q全部为0,那么m00实际上对应轮廓边界上点的数目。

虽然可以直接计算出轮廓的矩,但是经常会用到归一化的矩(因此不同大小但是形状相同的物体会有相同的值)。同样,简单的矩依赖于所选坐标系,这意味着物体旋转后就无法正确匹配。

于是就产生了Hu矩以及其他归一化矩的函数。

Hu矩是归一化中心矩的线性组合。之所以这样做是为了能够获取代表图像某个特征的矩函数。这些矩函数对缩放,旋转和镜像映射出了(h1)具有不变性。

Hu矩是从中心矩中计算得到。即七个由归一化中心矩组合成的矩:  

轮廓匹配、识别_第1张图片

 其中中心矩和归一化中心矩的定义为:

   我们可以使用cvContoursMoments函数、cvMoments函数方便的得到轮廓的矩集,然后再相应的方法或函数获取各种矩。
    特定的矩:cvGetSpatialMoment函数
    中心矩:cvGetCentralMoment函数
    归一化中心矩:cvGetNormalizedCentralMoment函数
    Hu矩:cvGetHuMoments函数
6.轮廓的轮廓树
    轮廓树用来描述某个特定轮廓的内部特征。注意:轮廓树跟轮廓是一一对应的关系;轮廓树不用于描述多个轮廓之间的层次关系。

    轮廓树的创建过程:

    从一个轮廓创建一个轮廓树是从底端(叶子节点)到顶端(根节点)的。首先搜索三角形突出或者凹陷的形状的周边(轮廓上的每一个点都不是完全和它的相邻点共线的)每个这样的三角形被一条线段代替,这条线段通过连接非相邻点的两点得到;因此实际上三角形或者被削平或者被填满。每个这样的替换都把轮廓的顶点减少,并且给轮廓树创建一个新节点。如果这样的一个三角形的两侧有原始边,那么她就是得到的轮廓树的叶子;如果一侧已是一个三角形,那么它就是那个三角形的父节点。这个过程的迭代最终把物体的外形简称一个四边形,这个四边形也被剖开;得到的两个三角形是根节点的两个子节点。

结果的二分树最终将原始轮廓的形状性比编码。每个节点被它所对应的三角形的信息所注释。

这样建立的轮廓树并不太鲁棒,因为轮廓上小的改变也可能会彻底改变结果的树,同时最初的三角形是任意选取的。为了得到较好的描述需要首先使用函数cvApproxPoly()之后将轮廓排列(运用循环移动)成最初的三角形不怎么收到旋转影响的状态。
    可以用函数cvCreateContourTree来构造轮廓树。

 7.轮廓的凸包和凸缺陷
    轮廓的凸包和凸缺陷用于描述物体的外形。凸包和凸缺陷很容易获得,不过我目前不知道它们到底怎么使用。
    如果要判断轮廓是否是凸的,可以用cvCheckContourConvexity函数。
    如果要获取轮廓的凸包,可以用cvConvexHull2函数,返回的是包含顶点的序列。
    如果要获取轮廓的凸缺陷,可以用cvConvexityDefects函数。
 8.轮廓的成对几何直方图
    成对几何直方图(pairwise geometrical histogram PGH)是链码编码直方图(chain code histogram CCH)的一个扩展或者延伸。CCH是一种直方图,用来统计一个轮廓的Freeman链码编码每一种走法的数字。这种直方图的一个优良性质为当物体旋转45度,那么新直方图是老直方图的循环平移。这样就可以不受旋转影响。

    (1)轮廓保存的是一系列的顶点,轮廓是由一系列线段组成的多边形。对于看起来光滑的轮廓(例如圆),只是线段条数比较多,线段长度比较短而已。实际上,电脑中显示的任何曲线都由线段组成。
    (2)每两条线段之间都有一定的关系,包括它们(或者它们的延长线)之间的夹角,两条线段的夹角范围是:(0,180)。
    (3)每两条线段上的点之间还有距离关系,包括最短(小)距离、最远(大)距离,以及平均距离。最大距离我用了一个偷懒的计算方法,我把轮廓外界矩形的对角线长度看作了最大距离。
    (4)成对几何直方图所用的统计数据包括了夹角和距离。
 

轮廓的匹配
    如果要比较两个物体,可供选择的特征很多。如果要判断某个人的性别,可以根据他(她)头发的长短来判断,这很直观,在长发男稀有的年代准确率也很高。也可以根据这个人尿尿的射程来判断,如果射程大于0.50米,则是男性。总之,方法很多,不一而足。
    我们在上文中得到了轮廓的这么多特征,它们也可以用于进行匹配。典型的轮廓匹配方法有:Hu矩匹配、轮廓树匹配、成对几何直方图匹配。
1.Hu矩匹配
    轮廓的Hu矩对包括缩放、旋转和镜像映射在内的变化具有不变性。cvMatchShapes函数可以很方便的实现对2个轮廓间的匹配。
2.轮廓树匹配
    用树的形式比较两个轮廓。cvMatchContourTrees函数实现了轮廓树的对比。
3.成对几何直方图匹配
    在得到轮廓的成对几何直方图之后,可以使用直方图对比的方法来进行匹。

 

 

轮廓匹配源码1:

 

轮廓匹配源码1    
    

IplImage* img_8uc1 = cvLoadImage("flower.jpg",CV_LOAD_IMAGE_GRAYSCALE);
    IplImage* img_edge1 = cvCreateImage(cvGetSize(img_8uc1),8,1);
    IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);

    cvThreshold(img_8uc1,img_edge1,128,255,CV_THRESH_BINARY);


    CvMemStorage* storage1 = cvCreateMemStorage();
    CvSeq* first_contour1 = NULL;

    int Nc = cvFindContours(
        img_edge1,
        storage1,
        &first_contour1,
        sizeof(CvContour),
        CV_RETR_LIST
        );

    IplImage* img_8uc12 = cvLoadImage("flower1.jpg",CV_LOAD_IMAGE_GRAYSCALE);
    IplImage* img_edge12 = cvCreateImage(cvGetSize(img_8uc12),8,1);
    IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);

    cvThreshold(img_8uc12,img_edge12,128,255,CV_THRESH_BINARY);


    CvMemStorage* storage2 = cvCreateMemStorage();
    CvSeq* first_contour2 = NULL;

    int Nc2 = cvFindContours(
        img_edge12,
        storage2,
        &first_contour2,
        sizeof(CvContour),
        CV_RETR_LIST
        );

    double n = cvMatchShapes(first_contour1,first_contour2,CV_CONTOURS_MATCH_I1,0);

    printf("%d",n);

    cvWaitKey();


    IplImage* img_8uc1 = cvLoadImage("flower.jpg",CV_LOAD_IMAGE_GRAYSCALE);
    IplImage* img_edge1 = cvCreateImage(cvGetSize(img_8uc1),8,1);
    IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);

   cvThreshold(img_8uc1,img_edge1,128,255,CV_THRESH_BINARY);


   CvMemStorage* storage1 = cvCreateMemStorage();
   CvSeq* first_contour1 = NULL;

   int Nc = cvFindContours(
      img_edge1,
      storage1,
      &first_contour1,
     sizeof(CvContour),
     CV_RETR_LIST
      );

    CvContourTree* tree1 = cvCreateContourTree(
    first_contour1,
   storage1,
   200
    );
 
    IplImage* img_8uc12 = cvLoadImage("flower1.jpg",CV_LOAD_IMAGE_GRAYSCALE);
    IplImage* img_edge12 = cvCreateImage(cvGetSize(img_8uc12),8,1);
    IplImage* img_8uc3 = cvCreateImage(cvGetSize(img_8uc1),8,3);
 
    cvThreshold(img_8uc12,img_edge12,128,255,CV_THRESH_BINARY);
 
 
    CvMemStorage* storage2 = cvCreateMemStorage();
    CvSeq* first_contour2 = NULL;
 
    int Nc2 = cvFindContours(
        img_edge12,
        storage2,
        &first_contour2,
        sizeof(CvContour),
        CV_RETR_LIST
        );
    CvContourTree* tree2 = cvCreateContourTree(
        first_contour2,
        storage2,
        200
        );
    double n = cvMatchContourTrees(tree1,tree1,CV_CONTOURS_MATCH_I1,200);
 
    printf("%d",n);
    cvWaitKey();

 

几何直方图匹配方:

#include "gesrec.h" 
#include //
 #define PI 3.14159f //轮廓面积比较函数 
static int gesContourCompFunc(const void* _a, const void* _b, void* userdata) 
{ 
int retval; double s1, s2; CvContour* a = (CvContour*)_a; 
CvContour* b = (CvContour*)_b; 
s1 = fabs(cvContourArea(a)); 
s2 = fabs(cvContourArea(b)); 
//s1 = a->rect.height * a->rect.width; 
//s2 = b->rect.height * b->rect.width; 
if(s1 < s2) { retval = 1; } 
else if(s1 == s2) { retval = 0; } else { retval = -1; } 
return retval;
 } //src:BGR dst: 
void gesFindContours(IplImage* src, IplImage* dst, CvSeq** templateContour, CvMemStorage* templateStorage, int flag)
 { int count;//轮廓数
  IplImage* gray; CvMemStorage* first_sto; CvMemStorage* all_sto; 
 CvSeq* first_cont; CvSeq* all_cont; CvSeq* cur_cont; //初始化动态内存
  first_sto = cvCreateMemStorage(0); first_cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), first_sto); 
all_sto = cvCreateMemStorage(0); all_cont = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvSeq), all_sto); 
//创建源图像对应的灰度图像
 gray = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); 
cvCvtColor(src, gray, CV_BGR2GRAY); //得到图像的外层轮廓 
count = cvFindContours(gray, first_sto, &first_cont, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); //如果没有检测到轮廓则返回 
if(first_sto == NULL) { return; } //将所有的轮廓都放到
first_cont中 for(;first_cont != 0;first_cont = first_cont->h_next) {
	 if(((CvContour* )first_cont)->rect.height * ((CvContour* )first_cont)->rect.width >=625) cvSeqPush(all_cont, first_cont); }
 //对轮廓按照面积进行排序
 cvSeqSort(all_cont, gesContourCompFunc, 0); 
//在dst中画出轮廓 
cvZero(dst);
 for(int i = 0;i < min(all_cont->total, 3);i++)///次数待改
 { cur_cont = (CvSeq* )cvGetSeqElem(all_cont, i);
 if(flag != 0 && i == 0) { *templateContour = cvCloneSeq(cur_cont, templateStorage); } 
CvScalar color = CV_RGB(rand()&255, rand()&255, rand()&255); 
cvDrawContours(dst, (CvSeq* )cur_cont, color, color, -1, 1, 8); 
} //判断原点位置以确定是否需要反转图像 
if(src->origin == 1) { cvFlip(dst); } //释放内存
 cvReleaseMemStorage(&first_sto); cvReleaseMemStorage(&all_sto);
 cvReleaseImage(&gray); } 
void gesMatchContoursTemplate(IplImage* src, IplImage* dst, CvSeq** templateContour) { CvSeq* contour; CvMemStorage* storage; //初始化动态内存 
storage = cvCreateMemStorage(0); contour = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage); 
//得到轮廓并进行匹配
 gesFindContours(src, dst, &contour, storage, 1); if(contour->total != 0)//如果得到的轮廓不为空 
{ double result = cvMatchShapes((CvContour* )contour, (CvContour* )(*templateContour), CV_CONTOURS_MATCH_I3); 
printf("%.2f\n", result);/ } //释放内存
 cvReleaseMemStorage(&storage); } //模版匹配法的完整实现
 int gesMatchContoursTemplate2(IplImage* src, IplImage* dst, CvSeq* templateContour) { CvSeq* contour; CvSeq* cur_cont; CvMemStorage* storage; double minValue, tempValue; int i, minIndex; //初始化动态内存
 storage = cvCreateMemStorage(0); contour = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage); 
//得到轮廓并进行匹配
 minIndex = -1; gesFindContours(src, dst, &contour, storage, 1); if(contour->total != 0)//如果得到的轮廓不为空
 { if(templateContour->total != 0) { cur_cont = (CvSeq* )cvGetSeqElem(templateContour, 0); 
minValue = cvMatchShapes((CvContour* )contour, (CvContour* )cur_cont, CV_CONTOURS_MATCH_I3); 
minIndex = 0; 
printf("0:%.2f\n", minValue); }
 for(i = 1;i < templateContour->total;i++) 
{ cur_cont = (CvSeq* )cvGetSeqElem(templateContour, i); 
tempValue = cvMatchShapes((CvContour* )contour, (CvContour* )cur_cont, CV_CONTOURS_MATCH_I3); 
if(tempValue < minValue) { minValue = tempValue; minIndex = i; } 
printf("%d:%.2f\n", i, tempValue); } 
if(minValue >= 0.3) { minIndex = -1; } } //打印匹配结果 
printf("the result is %d\n", minIndex); //释放内存 
cvReleaseMemStorage(&storage); return minIndex; } //找出轮廓最大的5个极大值点
 void gesFindContourMaxs(CvSeq* contour) { int i; CvScalar center;//重心位置 
CvPoint* p; CvMat max;
//存储5个极大值的数组 double initMax[] = {-1, -1, -1, -1, -1};//初始极大值设置为-1 
double minValue, maxValue;//5个极大值中的最大值与最小值 CvPoint minLoc;//最小值的位置 
double preDistance = 0; bool isCandidate = false;//是否是候选的极大值点 //初始化重心位置 
center = cvScalarAll(0); //初始化极大值矩阵 
max = cvMat(1, 5, CV_64FC1, initMax); 
//首先求出轮廓的重心
 for(i = 0;i < contour->total;i++) { p = (CvPoint* )cvGetSeqElem(contour, i); 
center.val[0] += p->x; center.val[1] += p->y; }
 center.val[0] /= contour->total; center.val[1] /= contour->total; //遍历轮廓,找出所有的极大值点
 for(i = 0;i < contour->total;i++) { p = (CvPoint* )cvGetSeqElem(contour, i); 
double distance = sqrt(pow(center.val[0] - p->x, 2) + pow(center.val[1] - p->y, 2));
 if(distance > preDistance) { isCandidate = true; } 
else if(distance < preDistance && isCandidate == true) 
{ cvMinMaxLoc(&max, &minValue, &maxValue, &minLoc); 
if(distance > minValue) {
	cvmSet(&max, minLoc.y, minLoc.x, distance); }
 isCandidate = false; }
 else { isCandidate = false; } preDistance = distance; } 
//打印5个极大值 
printf("%.2f %.2f %.2f %.2f %.2f\n", cvmGet(&max, 0, 0), cvmGet(&max, 0, 1), cvmGet(&max, 0, 2),
 cvmGet(&max, 0, 3), cvmGet(&max, 0, 4)); } //计算轮廓的pair-wise几何直方图
 CvHistogram* gesCalcContoursPGH(CvSeq* contour) { CvHistogram* hist;//成对几何直方图 
CvContour* tempCont; 
//得到成对几何直方图第二个维度上的范围 
tempCont = (CvContour* )contour; cvBoundingRect(tempCont, 1); 
int sizes[2] = {60, 200}; float ranges[2][2] = {
    {0,PI}, {0,200}}; 
float** rangesPtr = new float* [2]; 
rangesPtr[0] = ranges[0]; 
rangesPtr[1] = ranges[1]; //初始化几何直方图
 hist = cvCreateHist(2, sizes, CV_HIST_ARRAY, rangesPtr, 1);
 //计算轮廓的成对几何直方图
 cvCalcPGH(contour, hist); return hist; } //对轮廓的pair-wise几何直方图进行匹配
 void gesMatchContoursPGH(CvSeq* contour, CvHistogram* templateHist) { CvHistogram* hist; //得到轮廓的成对几何直方图
 hist = gesCalcContoursPGH(contour); //归一化直方图
 cvNormalizeHist(templateHist, 1);
 cvNormalizeHist(hist, 1); //直方图匹配 
double result = cvCompareHist(hist, templateHist, CV_COMP_INTERSECT); 
printf("result:%.2f\n", result); //释放内存 
cvReleaseHist(&hist); 
}

 

一个跟轮廓相关的最常用到的功能是匹配两个轮廓.如果有两个轮廓,如何比较它们;或者如何比较一个轮廓和另一个抽象模板.

比较两个轮廓最简洁的方式是比较他们的轮廓矩.这里先简短介绍一个矩的含义.简单的说,矩是通过对轮廓上所有点进行积分运算(或者认为是求和运算)而得到的一个粗略特征.通常,我们如下定义一个轮廓的(p,q)矩:

在公式中p对应x纬度上的矩,q对应y维度上的矩,q对应y维度上的矩,阶数表示对应的部分的指数.该计算是对轮廓边界上所有像素(数目为n)进行求和.如果p和q全为0,那么m00实际上对轮廓边界上点的数目.

下面的函数用于计算这些轮廓矩

void cvContoursMoments(CvSeq* contour,CvMoments* moments)

第一个参数是我们要处理的轮廓,第二个参数是指向一个结构,该结构用于保存生成的结果.CvMonments结构定义如下

/* Spatial and central moments */  typedef struct CvMoments  {      double  m00, m10, m01, m20, m11, m02, m30, m21, m12, m03; /* spatial moments */      double  mu20, mu11, mu02, mu30, mu21, mu12, mu03; /* central moments */      double  inv_sqrt_m00; /* m00 != 0 ? 1/sqrt(m00) : 0 */  }  CvMoments;  
  • 1

在cvContourMoments()函数中,只用到m00,m01,...,m03几个参数;以mu开头的参数在其他函数中使用.

 

在使用CvMoment结构的时候,我们可以使用以下的函数来方便地一个特定的矩:

CVAPI(double)  cvGetSpatialMoment( CvMoments* moments, int x_order, int y_order );  
  • 1

调用cvContoursMonments()函数会计算所有3阶的矩(m21和m12会被计算,但是m22不会被计算).

 

再论矩

刚刚描述的矩计算给出了一些轮廓的简单属性,可以用来比较两个轮廓.但是在很多实际使用中,刚才的计算方法得到的矩并不是做比较时的最好的参数.具体说来,经常会用到归一化的矩(因此,不同大小但是形状相同的物体会有相同的值).同样,刚才的小节中的简单的矩依赖于所选坐标系,这意味这物体旋转后就无法正确匹配.

OpenCV提供了计算Hu不变矩[Hu62]以及其他归一化矩的函数.CvMoments结构可以用cvmoments或者cvContourMoments计算.并且,cvContourMoments现在只是cvMoments
的一个别名.

一个有用的小技巧是用cvDrawContour()描绘一幅轮廓的图像后,调用一个矩的函数处理该图像.使用无论轮廓填充与否,你都能用同一个函数处理.

以下是4个相关函数的定义:

 

/* Calculates all spatial and central moments up to the 3rd order */  CVAPI(void) cvMoments( const CvArr* arr, CvMoments* moments, int binary CV_DEFAULT(0));  
  • 1
CVAPI(double)  cvGetCentralMoment( CvMoments* moments, int x_order, int y_order );  
  • 1
CVAPI(double)  cvGetNormalizedCentralMoment(CvMoments* moments,int x_order, int y_order);  
  • 1
/* Calculates 7 Hu's invariants from precalculated spatial and central moments */  CVAPI(void) cvGetHuMoments( CvMoments*  moments, CvHuMoments*  hu_moments );  
  • 1

第一个函数除了使用的是图像(而不是轮廓)作为参数,其他方面和cvContoursMoments()函数相同,另外还增加了一个参数.增加的参数isBinary如果为CV_TRUE,cvMoments将把图像当作二值图像处理,所有的非0像素都当作1.当函数被调用的时候,所有的矩被计算(包含中心矩,请看下一段).除了x和y的值被归一化到以0为均值,中心距本质上跟刚才描述的矩一样.

 

归一化矩和中心矩也基本相同,除了每个矩都要除以m00的某个幂:

最后来介绍Hu矩,Hu矩是归一化中心距的线性组合.之所以这样做是为了能够获取代表图像某个特性的矩函数,这些矩函数对于某些变化如缩放,旋转和镜像映射(除了h1)具有不变性.Hu矩是从中心矩中计算得到,其计算公式如下所示:

参考图8-9和表8-1,我们可以直观地看到每个图像对应的7个Hu矩.通过观察可以发现,当阶数变高时,Hu矩一般会变小.对于这一点不必感到奇怪,因为根据定义,高阶Hu矩由多个归一化矩的高阶幂计算得到,而归一化矩都是小于1的,所以指数越大,计算所得的值越小.


需要特别注意的是"I",它对于180度旋转和镜面反射都是对称的,它的h3到h7矩都是0;而"O"具有同样的对称特性,所有的Hu矩都是非0的.

使用Hu矩进行匹配

/* Compares two contours by matching their moments */  CVAPI(double)  cvMatchShapes( const void* object1, const void* object2,                                int method, double parameter CV_DEFAULT(0)); 
  • 1

很自然,使用Hu矩我们想要比较两个物体并且判明他们是否相似.当然,可能有很多"相似"的定义.为了使比较过程变得简单,OpenCV的函数cvMatShapes()允许我们简单地提供两个物体,然后计算他们的矩并根据我们提供的标准进行比较.

 

这些物体可以是灰度图图像或者轮廓.如果你提供了图像,cvMatchShape()会在对比的进程之间为你计算矩.cvMatchShapes()使用的方法是表8-2中列出的三种中的一种.

 关于对比度量标准(metric)是如何被计算的,表8-2中的三个常量每个都用了不同的方法.这个度量标准最终决定了cvMatchShapes()的返回值.最后一个参数变量现在不能用,因此我们可以把它设成默认值0.

等级匹配

我们经常想要匹配两个轮廓,然后用一个相似度量来计算轮廓所有匹配部分.使用概况参数的方法(比如矩)是相当快的,但是他们能够表达的信息却不是很多.

为了找到一个更精确的相似度量度,首先考虑一下轮廓树的结构应该会有帮助.请注意,此外的轮廓树是用来表述一个特定形状(不是多个特定形状)内各部分的等级关系.

类似于cvFindContours()着怎样的函数放回多个轮廓,轮廓树(contout tree)并不会把这些等级关系搞混,事实上,他正是对于某个特定轮廓形状的登记描述.

理解了轮廓树的创建会比较容易理解轮廓树.从一个轮廓创建一个轮廓树是从底端(叶节点)到顶端(根节点)的.首相搜索三角形突出或凹陷的形状的周边(轮廓上的每一个点都不是完全和它的相邻点共线的).每个这样的三角形被一条线段代替,这条线段通过连接非相邻点的两点得到;因此实际上三角形或者被削平(例如,图8-10的三角形D)或者被填满(三角形C).每个这样的替代把轮廓的顶点减少1,并且给轮廓创建一个新节点.如果这样一个三角形的两侧有原始的边,那么它就是得到的轮廓树的叶子;如果一侧是已存在三角形,那么他就是那个三角形的父节点.这个过程的迭代最终把物体的外形剪成一个四边形,这个四边形也被剖开;得到的两个三角形是根节点的两个子节点.

结果的二分树(图8-11)最终将原始轮廓的形状信息编码.每个节点被它对应的三角形信息(比如三角形的大小,它的生成是被切出来还是被填进去的,这样的信息)所注释

这些树一旦被建立,就可以很有效的对比两个轮廓.这个过程开始定义两个树节点的对应关系,然后比较对应节点的特性.对吼的结果就是两个树的相似度.

事实上,我们基本不需要理解这个过程.OpenCV提供了一个函数从普通的CvContour对象自动生成轮廓树并转换返回;还提供一个函数用来对比两个树.不幸的是,建立的轮廓树并不太鲁棒(例如,轮廓上很小的改变可能会彻底改变结果的树).同事,最初的三角形(树的根节点)是随意选取的.因此,为了得到较好的描述实现使用函数cvApproxPoly()之后将轮廓排列(运用循环移动)成最初的三角形不怎么受到旋转影响的状态.

CvContourTree* cvCreateContourTree(const CvSeq* contour,CvMemStorage* storage,double  threshold);

CvSeq * cvContourFromContourTree(const CvContourTree* tree,CvMemStorage* storage, CvTermCriteria  criteris);

double cvMatchContourTrees(const CvContourTree* tree1,const CvContourTree* tree2,int method,double threshold);

这个代码提到了CvTremCriteria(),该函数细节将在第9章给出.现在可以用下面的默认值使用cvTermCriteria()简单建立一个结构体.

CvTermCriteria termcrit = cvTermCriteria(CV_TERMCRIT_ITER | CV_TeRMCRT_EPS,5,1);

轮廓的凸包和凸缺陷

另一个理解物体形状或轮廓的有用的方法是计算一个物体的凸包(convex hull)然后计算其凸缺陷(convexity defects)[Homma85].很多复杂物体的特性能很好的被这种缺陷表现出来.

图8-12用人手举例说明了凸缺陷这一概念.手周围深色的线描画出了凸包,A到H被标出的区域是凸包的各个"缺陷".正如所看到的,这些凸度缺陷提供了手以及手状态的特征表现的方法.

 

enum  {      CV_CLOCKWISE         =1,      CV_COUNTER_CLOCKWISE =2  };  
  • 1
/* Calculates exact convex hull of 2d point set */  CVAPI(CvSeq*) cvConvexHull2( const CvArr* input,                               void* hull_storage CV_DEFAULT(NULL),                               int orientation CV_DEFAULT(CV_CLOCKWISE),                               int return_points CV_DEFAULT(0));  
  • 1
/* Checks whether the contour is convex or not (returns 1 if convex, 0 if not) */  CVAPI(int)  cvCheckContourConvexity( const CvArr* contour );  
  • 1
/* Finds convexity defects for the contour */  CVAPI(CvSeq*)  cvConvexityDefects( const CvArr* contour, const CvArr* convexhull,                                     CvMemStorage* storage CV_DEFAULT(NULL));  
  • 1

 

OpenCV有三个关于凸包和凸缺陷的重要函数.第一个函数简单计算已知轮廓的凸包,第二个函数用来检查一个已知轮廓是否是凸的.第三个函数在已知轮廓是凸包的情况下计算凸缺陷.

函数cvConvexHull2()的第一个参数是点的数组,这个数组是一个n行2列的矩阵(n×2),或者是一个轮廓.如果是点矩阵,点应该是32位整型(CV_32SC1)或者是浮点型(CV_32F1).下一个参数是指向内存存储的一个指针,为结果分配内存空间.下一参数是CV_CLOCkWISE或者是CV_COUNTERCLOCkWISE中的一个.这参数决定了程序返回点的排列方向.最后一个参数returnPoints,可以是0或1.如果设置为1,点会被存储在返回数组中.如果设置为0,只有索引被存储在返回数组中.索引是传递给cvConvexHull2()的原始数组索引.

读着可能要问:"如果参数hull_storage是内存存储,为什么它的类型是void* 而不是CvMemSotrage* ?",这是因为很多时候作为凸包放回的点的形式,数组可能比序列更加有用.可虑到这一点,参数hull_storage的另一个可能性是传递一个指向矩阵的指针CvMat*. 这种情况下,矩阵应该是一维的且和输入点的个数相同.当cvConvexHull2()被调用的时候,它会修改矩阵头来指明当前的列数.

有时候,已知一个轮廓但并不知道它是否是凸的.这种情况下,我们可以调用函数cvCheckContourConvexity().这个测试简单快速,但是如果传递的轮廓自身有交叉的时候不会得到正确的结果.

第三个函数cvConvexityDefects(),计算凸缺陷返回一个缺陷的序列.为了完成这个任务,cvConvexityDefects()要求输入轮廓,凸包和内存空间,从这个内存空间来获得存放结果序列的内存.前两个参数是CvArr*,和传递给cvConvexHull2()的参数input的形式相同.

typedef struct CvConvexityDefect  {      CvPoint* start; /* point of the contour where the defect begins */      CvPoint* end; /* point of the contour where the defect ends */      CvPoint* depth_point; /* the farthest from the convex hull point within the defect */      float depth; /* distance between the farthest point and the convex hull */  } CvConvexityDefect;  
  • 1

函数cvConvexityDefects()返回一个CvConvexityDefect结构体的序列,其中包括一些简单的参数用来描述凸缺陷.start和end是凸包上的缺陷的起始点和终止点.depth_point是缺陷中的距离凸包的边(跟该缺陷有关的凸包便)最远的点.最后一个参数depth是最远点和包的边(edge)的距离.
 

 

成对几何直方图

Freeman链码编码是对一个多边形的的序列如何"移动"的描述,每个这样的移动有固定长度和特定的方向.但是,我们并没有更多说明为什么需要用到这种描述.

Freeman链码编码的用处很多,但最常见的一种值得深入了解一下,因为它支持了成对几何直方图(PGH)的基本思想.

PGH实际上是链码编码直方图(CCH)的一个扩展或延伸.CCH是一种直方图,用来统计一个轮廓的Freeman链码编码每一种走法的数字.这种直方图有一些良好的性质.最显著的是,将物体旋转45度,那么新的直方图是老直方图的循环平移(图8-13).这就提供了一个不被此类旋转影响的形状识别方法.

PGH的构成如下图所示(图8-14).多边形的每一个边被选择成为"基准边".之后考虑其他的边相对于这些基础边的关系,并且计算三个值:dmin,dmax和θ.dmin是两条边的最小距离,dmax是最大距离,θ是两边的夹角.PGH是一个二维直方图,其两个维度分别是角度和距离.对于每一对边,有两个bin,一个bin为(dmin,θ),另一个bin为(dmax,θ).对于这样的每一组边,这两个bin都被增长,中间值d(dmin和dmax之间的值)同样也被增长.

PGh的使用和FCC相似.一个重要不同是,PGH的描述能力更强,因此在尝试解决复杂问题的时候很有用,比如说大量形状需要被辨识,并且/或者有很多背景噪声的时候.用来计算PGh的函数是

void cvCalcPGH(const CvSeq* contour,CvHistogram* hist)

在这里轮廓可以包含整数值的点的坐标;当然直方图必须是二维的.

 

二维物体形状识别方法比较

 

摘  要  针对模式识别中二维物体的形状识别问题,以二值图像中的物体形状为主要研究对象,依次从特征提取、分类器设计两个主要层面对形状识别方法进行了全面综述,并分析了国内外研究现状,特别是近年来所取得的最新研究成果。最后,指出了目前存在的问题以及今后的研究方向。

关键词  物体形状识别;特征提取;分类器设计

中图法分类号  TP391.41

Comparison on methods of 2D object shape recognition

Abstract: In view of two-dimensional object shape recognition question in pattern recognition, the object shape in binary image was taken as the main research object. It summarizes the shape recognition methods based on the contour, the region and the neural network separately, furthermore, analyzes the situation of present research in domestic and foreign simultaneously, especially obtained the newest research results in recent years. Finally, the paper points out the current question existed as well as the direction of future research.

Key words: Object shape recognition; feature extraction; classifier design

 

物体的形状识别[1]是 模式识别中一个基本问题,也是一个重要问题,其广泛应用于图像分析、计算机视觉和目标识别等领域。人类可以很容易地识别物体的形状,但是对于计算机来说, 自动识别任意物体的形状却相当困难。物体的形状是人的视觉系统分析和识别物体的基础。一般来说,我们对物体的识别更注重于它们的形状,而物体的纹理、颜色 次之,因此如何表示形状以及比较形状间的差异在机器视觉的应用和研究领域具有非常重要的意义。

物体形状识别一般包括图像中的物体在旋转、缩放、平移、扭曲、遮挡、仿射、射影变换下以及在含噪声图像中通过提取物体的形状识别物体。由于该问题在研究上的复杂性和实现起来的困难性,大多数的研究报道的方法仅针对上述变换的一种或其中几种进行讨论。

目前对物体的形状识别的表示和描述国内外已经提出了多种方法。本文以下的部分组织如下:第1节介绍特征提取;第2节介绍分类器设计;第3节给出了仿真实验结果;最后作出总结。

1  特征提取

在物体识别中,由于图像本身的原始数据量相当大。如果把所有的原始特征都送往分类器,会使得分类器异常复杂同时计算量巨大。因此,需要对物体形状进行分解产生基元并对其符号化,形成特征矢量或符号串、关系图,从而产生代表对象的模式,这个过程被称为特征提取。

1.1 简单几何不变性

利用各种几何不变性对物体进行识别也提出了多种方法:

利用角点特征[2],它通常定义为在图像边界上曲率足够高的点。角点特征具有平移、旋转、缩放不变性。它只适用于物体边界角点多且能代表物体形状的特征点的物体。

用等价曲线类[3]来表示形状,这 种形状的表示方法具有平移、旋转、缩放不变的特性。它是利用形状边界点的极半径的变化是否一致来判断它们是否属于同一类型。它具有对边界的扰动不敏感,不 受形状大小、位置以及方向的影响。它要根据形状的大小以及实际精度的需要来选定合适尺度,过大会使执行过程中引入冗余的计算,减慢执行速度,而太小会使识 别精度太粗,从而导致误识,因此选择合适的尺度是此方法的关键。

隐含多项式曲线[4]对物体描述有许多良好的性质,基于高次隐含多项式曲线获得的不变量对物体的识别有较好的鲁棒性,能够克服噪音的影响,并且能识别出部分遮挡的目标物体,但是,隐含多项式曲线不变量的寻找较为困难。

另外还有基于曲率函数法[5],物体的轮廓由它们的曲线函数表示;利用弧长和切线角[6]识别物体等等。

利用几何不变性来进行物体识别,对于具有某种特殊特征的物体来说,容易实现,但是对轮廓的描述太抽象,无法实现准确的识别或检索。

1.2 高斯描述子

高斯描述子[7]是一种基于边界的形状特征,具有识别或匹配率高,相对于平移、旋转、缩放不变、计算量小、对适度的边缘变动和噪声不敏感以及适用范围广等优点。

后来,文献[8]又对高斯描述子进行了推广,提出了局部高斯描述子,将它应用于物体形状识别获得了更高的识别率。但是无论是高斯描述子还是改进了的局部高斯描述子,它们都不具有仿射不变性,有待于进一步的改进。

1.3 傅立叶描述子

傅立叶描述子[9]具有简单、高效的特点,已经成为识别物体形状的重要方法之一。它的基本思想是:假定物体的形状是一条封闭的曲线,沿边界曲线上的一个动点p(l)的坐标变化x(l)+jy(l) ( p(l)坐标用复数形式表示) 是一个以形状边界周长为周期的函数。 这个周期函数可以展开成傅立叶级数形式表示。傅立叶级数中的一系列系数是直接与边界曲线的形状有关的, 称为傅立叶描述子。当系数项取到足够阶次时,它可以将物体的形状信息完全提取并恢复出来。

傅 立叶描述子是物体形状边界曲线的傅立叶变换系数,它是物体边界曲线信号的频域分析结果。根据傅立叶变换的性质,傅立叶描述子与形状尺度、方向和起始点有 关。因此为了识别具有旋转、平移和尺度不变性的形状,需对傅立叶描述子进行归一化。总之,傅立叶描述子方法是利用图像轮廓进行识别的,只适用于封闭边界, 而且不能反映区域内部特征,面对较复杂的图像,图像中有多个目标的或者轮廓不明显的图像,这种方法识别效果不理想。

1.4 小波描述子

   小波变换[10-11]具有空间-频 率局部性、方向性、多分辨率性等优点,在信号处理、图像处理、模式识别等众多领域得到应用。小波变换属于多分辨率变换,它在不同尺度上对图像分解。应用小 波进行轮廓表示时,需要选择小波系数的限制级数来描述轮廓,并且要对小波系数进行归一化,从而达到平移、缩放、旋转不变性的要求。称这些小波系数为小波描 述子,它弥补了傅立叶变换的一些缺陷。在物体形状识别上,单一的小波变换是基于边界的,计算量大。

以上提到的四种方法都是基于边界的。在基于边界的物体形状识别方法中,由于轮廓的检测、表示和后续计算常常不稳定,在应用中一般难以获得理想效果。如何克服现有基于边界的特征表示的困难,并提出稳定可行的新方法,是图像形状表示和识别领域中一个具有挑战性的问题。

1.5 骨架化的方法

骨架化方法[12]的核心思想是使用物体的中轴或骨架的拓扑关系来描述其形状。它能够用于识别旋转、平移、缩放的物体,且抗噪。骨架化方法的缺点在于骨架本身并不容易得到,稳定性也不理想,尤其对于形状比较复杂的物体更是如此。

1.6 矩不变量

矩不变量是指物体图像经过平移、旋转以及缩放变换仍然不变的矩特征量。利用矩不变量进行物体形状识别是模式识别中的一种重要方法。Hu[13]在1961年首先提出了连续函数矩的定义和关于矩的基本性质,证明了有关矩的平移、旋转以及缩放不变性等性质,具体给出了具有平移、旋转和缩放不变性的七个不变矩的表达式。Hu建立的矩不变式,需要目标区域的所有像素参与计算,尽管有些学者研究了矩的快速算法,但它们还是相当耗时的。Li[14]利用Fourier-Mellin变换的不变性推导出一种构造任意阶矩不变量的方法,并指出Hu’s矩不变量就是它的一个特例。Teague[15]建议利用正交多项式构造正交矩来克服Hu’s矩不变量包含大量冗余信息的缺点。正交矩在信息冗余度、图像表达以及在识别效果方面比其它类型的矩要好。Zernike 矩就是一种正交的不变量,由于它具有正交基,并且容易构造高阶矩,因而被广泛采用。在基于区域的Hu矩不变量的基础上,又对矩进行了推广[16]构造了一些新的矩不变量,如轮廓矩不变量[17]、极半径不变矩[18]、相对边界矩[19]等。以上的矩特征都是在整个图像空间中计算的,得到的是图像的全局特征,容易受到噪声的干扰。而且只适用于具有明显差异的图像,因而提高对相近物体的区分能力成为解决此类问题的关键。

以 上提到的两种方法都是基于区域的,基于区域的表示法和图像的灰度值密切相关,易于受到非均匀光照等因素的影响,而且由于计算面向整个区域,计算量很大。从 另一方面来说,由于它考虑物体的内部结构,比基于边界的方法包含了更多的物体形状的信息,因此它又比较稳定且识别率高。

1.7 小波矩

结合矩特征和小波特征而成的小波矩[20-21]既反映了图像的全局信息,又反映了图像的局域性信息。该算法不但解决了图像识别中特征量随图像旋转、平移和缩放而变化的问题,而且提高了对近似物体的识别能力,且具有较强的鲁棒性, 大大地加强了对图像精细程度的分析能力。它具有识别率高,尤其是在相似物体差别小的情况下,抗噪性强等优点,但是它不能识别具有遮挡、扭曲的物体。

1.8 独立分量分析(ICA)[22]

   设有n类待识别物体,经ICA预处理的同类物体图像像素排成n维行向量,对于k个同类训练样本,组成k*n矩阵。设这组观测信号是由d个独立分量线性混合而成。对此矩阵进行独立分量分析,分离出d独立分量,由这些独立分量构成特征空间的一组基,这d个基向量张成的子空间就形成了描述第i类物体的特征空间。由于共有n类物体,可以得到n组独立分量构成的特征空间,并且每组基描述了相应物体类的特征。

ICA在小样本训练的情况下,具有快速提取样本特征的能力,能够识别具有缺失和变形的物体图像,并且对噪声干扰不敏感,这在目标识别的实际应用中是很重要的。

1.9 主分量分析(PCA)[23]

在图像识别领域中,设输入的原始数据x的维数是N,希望通过预处理得到M()维数据y,如果不加任何限制条件,仅对x进行简单的截断,那么所引起的均方误差将等于舍弃的各分量方差之和。为此,希望得到一个线性变换W,使得对Wx的截断在最小均方误差下为最优,这就要求被舍弃的分量具有较低的方差,而保留的分量具有较高的方差,PCA正是寻找这个线性变换的方法。它是基于K-L分解,其目的是在数据空间中找一组向量以尽可能地解释数据的方差,通过一个特殊的向量矩阵,将数据从原来的高维空间投影到一个低维的向量空间中,降维后保存了数据的主要信息,从而使数据更易于处理。

PCA在最小均方误差的意义下是最优变换,它在消除模式特征之间的相关性,突出其差异性方面可达到最优效果。但PCA法提取的图像特征不具备位移、尺度及旋转等的不变性。PCA识别过程中整幅图像所有象素都参与了运算,比较适合进行较复杂的图像识别,但要求图像大小一致。

1.10圆周分解的方法

   圆周分解法[24]可视为对传统基于边界方法的扩展,既保留了这类方法在细节描述方面的优势,又将它们的描述范围从边界扩展到整个形状区域,因而可以视为基于边界和基于区域的方法的结合。

圆周分解法对全局和局部的信息都有很强的描述能力,该方法具有平移、旋转、缩放不变性,并对形变、遮挡以及随机噪声有良好的抵抗能力,但不适用于扭曲物体形状的识别且识别率并不是很好。

1.11Radon变换

Radon变换[25-26]是计算图像f(x,y)沿着一个指定角度方向投影的变换方法。在指定的方向上投影,就是二维函数在该方向上的线积分,也可理解为图像顺时针旋转角度后在水平轴上的投影。它由于其固有的抗噪性能好的优点,在带有噪声源的环境中应用它作为图像分析的一种有效方法是十分有利的。

图像经Radon变换后,主要优点是可以把识别问题从二维降到一维,这样便可当作一维信号来处理,大大提高了处理速度,并且提取的图像特征具有旋转、平移和缩放不变性,但是在经处理后一般需要与小波、矩等技术结合实用才会更有效。

2 分类器设计

在d 维特征空间已经确定的前提下,分类器设计问题是一个选择什么准则,使用什么方法,将已确定的d维特征空间划分成决策域的问题。设计分类精度高、误识率低、可靠性好的分类器是识别的最终目的。

2.1 BP神经网络

BP神经网络[27]是模式识别分类中使用最广的神经网络模型,有隐含层的网络可完成多维空间的任意分割。BP网络采取误差反向传播学习算法,广泛应用于函数逼近、模式识别、分类、数据压缩等方面。它 是一种具有三层或三层以上的多层神经网络,每一层都由若干个神经元组成。它按有教师学习方式进行训练,当一对学习模式提供给网络后,其神经元的激活值将从 输入层经各中间层向输出层传播,在输出层的各神经元输出对应于输入模式的网络响应。然后,按减少希望输出与实际输出误差的原则,从输出层经各中间层、最后 回到输入层修正各连接权。

BP算 法本质上是一种局部寻优的方法,其收敛过程存在着两个很大的缺陷:一是收敛速度慢,二是存在“局部极小点”问题。在学习过程中,有时会出现当学习反复进行 到一定次数后,虽然网络的实际输出与希望输出还存在很大的差距,但无论在如何学习下去,网络全局误差的减少速度都变得很缓慢,或者根本不再变化,这种现象 是因网络收敛于局部极小点所致。如果适当改进BP网络中间层的单元个数,或者给每个连接权加上一个很小的随机数,都有可能使收敛过程避开局部极小点。

2.2 遗传BP神经网络[28-29]

由于BP神经网络算法采用的是梯度下将法,因而易陷入局部极小并且训练时间较长。遗传算法(GA)采用启发式搜索技术寻找最优解,具有鲁棒性好、搜索效率高、对目标函数限制少、易于采用并行机并行高速运算等优点。遗传BP网络算法综合了遗传算法的全局优化和神经网络的并行计算等特点,可克服遗传算法最终进化至最优解较慢和神经网络易陷入局部解的缺陷,具有较好的全局性和收敛速度。

此算法的基本思想是:首先由GA求解优化问题,由于GA是同时搜索解空间的一群点,并构成不断进化的群体序列,因而在进化一定的代数后,可以同时得到一些具有全局性的好点,从这些好点出发,再分别用神经网络求解,进而得到全局优化解。

2.3 小波神经网络

小波神经网络[30-31]是一种以小波基函数为神经元激励函数的前馈网络,它既可看作是一种以小波函数为基底的函数连接型网络,也可认为是径向基函数网络的推广。

小波神经网络模型包括输入层、输出层和隐层。隐层包含两种节点:小波基节点和尺度函数节点。小波神经网络是基于小波分析而构成的神经网络,它充分利用小波变换的良好的局部化性质并结合神经网络的自学习功能,因而具有较强的逼近、容错能力,它避免了BP神经网络结构设计的盲目性和局部最优等非线性优化问题,大大简化了训练,具有较强的函数学习能力。它具有良好的时频定位特性,将其应用于物体形状识别比传统的神经网络有更好的识别效果,但其受噪声影响。

2.4 自组织竞争人工神经网络(SCNN)

SCNN[32]基于生物神经细胞的“侧抑制”结构,由单层神经网络组成,其输入节点与输出节点之间为全连接。因为网络在学习过程中的竞争特性表现在输出层上,所以,其输出层也称为竞争层,竞争网络的激活函数为二值型函数。SCNN采用科荷伦学习规则进行训练,它能够对输入模式进行自组织训练和判断,并最终将各类目标进行识别和分类。

它可以克服BP网络不易收敛、学习时间长等缺点,进行识别时训练和识别是同时完成的,具有实时性,有很高的效率。

2.5 卷积神经网络(CNNS)

CNNS[33]是近年来一种专门应用在二维图像处理、模式识别和机器视觉领域中的方法。

大多数分类方式都是基于特征的,这就意味着在进行分辨前必须提取某些特征。然而,显示的特征提取并不容易,在一些应用问题中也并非总是可靠的。CNNS分 类器可避免显示特征提取,可隐式地从训练数据中进行学习。它通过结构重组和减少权值将特征提取功能融合进多层感知机。某一层的输出,即特征图,构成下一层 的输入,与同一特征图关联的神经元分享共同的一组权值(也即卷积核函)。在最后一层,特征图被一通常是全连通的单层感知机分类。这使得它有别于其它基于神 经网络的分类器,而且它可以直接运作于灰度图像,使其能够直接用于处理物体图像的分类。

2.6 支持向量机(SVM)

   SVM[34-35]是 一种基于结构风险最小化原理的机器学习方法,它利用核函数将输入向量映射到一个高维特征空间,然后在该空间中构造一个最优超平面来逼近分类函数。它的基本 思想可以概括为:首先通过非线性变换将输入空间变换到一个高维空间,然后在这个新空间中求取最优线性分类面,这种非线性变换是通过定义适当的内积函数实现 的。

SVM克 服了神经网络许多固有的缺陷,如容易出现过学习或陷入局部极小等,对小样本数据的数据分析具有出色的学习能力和推广能力。它对于物体的平移和旋转有很好的 识别率,并且具有较强的鲁棒性,但对于缩放物体的识别率有所下将。对于大数据的物体识别分类问题,如何提高它的数据处理的实时性、缩短训练样本的时间,仍 是它亟待解决的问题。

3 仿真实验

   在Matlab 7的环境下进行了部分方法的仿真实验:用小波矩进行特征提取;针对小波矩特征维数较高的特点,本文采用徐旭东[36]提出的结合分散度和顺序前进法的特征选择方法;分别用BP神经网络(BP)、遗传BP神经网络(GABP)作为分类器。本实验选择两类二值飞机图像用于识别,如图1所示F1、F2,图像大小均为512*512像素,格式为bmp。其中每类飞机均取不同平移、缩放、旋转变换下共40幅图像,接着从每类飞机中选取三种几何变换下的各10幅分别加入均值为0,方差分别为0.01、0.05、0.1的高斯噪声共60幅。因此本实验共得到100幅图像,其中20幅(每类选取10幅)未加入噪声的图像用于训练,其它80幅用于测试。

总结

    本 文从特征提取和分类器设计两个方面介绍了物体形状识别的各种方法,每种方法都引用了一定的参考文献,因此本文省去了相关方法的公式,只说明了相关方法在识 别物体时的思路和优缺点。通过以上的介绍可知,到目前为止,仍没有一种普遍通用的方法能够在各种环境下识别物体。这些方法只有在特定的条件下才能够识别相 对应的物体,并且大部分方法并不是由单一的一种技术来实现。更重要的是,所有的算法都有待于改进,以使它们能够适应普遍的场合和提高识别率以及能够识别三 维物体。由于轮廓的检测、表示常常使基于边界的方法不稳定;由于计算面向整个区域,使基于区域的方法的计算量很大;而神经网络是一种智能化的方法,它的很多技术发展还不成熟。所以,今后的主要工作是不断完善和改进这些方法。

    对于在物体识别方面的初学者来说,本文很有参考价值。

参考文献

[1] 丁险峰,吴洪,张宏江,马颂德.形状匹配综述[J].自动化学报,2001,27(5):678-694.

[2]  Mehrotra R,Nichani S.Corner detection[J].Pattern Recogni tion, 1990,23(11):1223-1233.

[3] 陈孝春,叶懋冬,倪臣敏.一种形状识别的方法[J].模式识别与人工智能,2006,9(6):758-763.

[4] 吴刚,李道伦.基于隐含多项式曲线仿射不变量的目标识别[J].电子学报,2004,32(12):1987-1992.

[5] Bandera A, Urdiales C. 2D object recognition based on curvature functions obtained from local histograms of the contour chain code[J]. Pattern Recognition Letters,1999,20(1)4955.

[6] Dong Xu,Wenli Xu. Description and recognition of object contours using arc length and tangent orientation[J]. Pattern Recognition Letters ,2005,26(7):855-864.

[7] 刘亦书,利用高斯描绘子进行字符识别[J].计算机应用,2006,26(11):2778-2780.

[8] 刘亦书.区域高斯描绘子及其在物体识别中的应用[J].计算机工程与设计,2007,28(15):3650-3652.

[9] 王涛,刘文印,孙家广,张宏江.傅立叶描述子识别物体的形状[J].计算机研究与发展,2002,39(12):1714-1719.

[10] Tieng QM,Boles WW.Recognition of 2D object contours using the wavelet transform zero-crossing representation[J] .IEEE Transactions on Pattern Analysis and Machine Intelligence, 1997,19(8):910-916.

 [11] 潘泓,夏良正.具有平移、尺度和旋转不变性的小波变换[J].信号处理,2004,20(2):147-152.

[12] Cecilia DR.Recognition of shapes by attributed skeletal graphs[J]. Pattern Recognition,2004,37(1):21-31.

[13] Hu MK.Visual pattern recognition by moment invariants[J]IEEE Transactions on Information Theory,1962,8(2):179-18.

 [14] Li Y.Refarming the theory of invariant moments for pattern recognition[J].Pattern Recognition,1992,25(7):723-730.

[15] Teagure MR. Image analysis via the general theory of moments[J].Journal of the Optical Society of America,1979,70(8):920-930.

[16] 刘进,张天序.图像不变矩的推广[J].计算机学报,2004,27(5):668-674.

[17] 刘亦书,杨力华,孙倩.轮廓矩不变量及其在物体形状识别中的应用[[J].中国图象图形学报,2004,9(3):308-313.

[18] 曹茂永,孙农亮,郁道银.用于模式识别的极半径不变矩[J].计算机学报,2004,27(6):860-864.

[19] 李丽宏,苗敬利,王静爽,安庆宾.相对边界矩在模式识别中的应用[J].微计算机信息,2005,21(7):3-4.

[20] Dinggan S,Horace H S Ip.Discriminative wavelet shape descriptors for recognition of 2-D patterns[J]. Pattern Recognition,1999,32(2):151-165.

[21] 张虹,陈文楷.一种基于小波矩的图像识别方法[J].北京工业大学学报,2004,30(4):427-431.

[22] 芮挺,沈春林,Qi Tian,丁健..基于ICA的特征不变性目标识别[J].小型微型计算机系统,2005,26(3):505-50.8

[23] 董火明,高隽,胡良梅,董文雯.基于主分量分析的形状特征提取及识别研究[J].合肥工业大学学报(自然科学版),2003,26(2):176-179.

[24] 王逸飞,陈雁秋.用于二维形状描述圆周分解法[J].计算机科学,2006,33(11):228-233.

[25] 王耀明,严炜,俞时权.基于Radon变换的图像矩特征抽取及其在图像识别中的应用[J].计算机工程,2001,27(2):82-84.

[26] 李军宏,潘泉,张洪才,崔建锋,崔培玲.基于Radon变换的图像识别研究[J],计算机应用研究,2004,21(5):207-209.

[27] 张华,张淼,刘魏,孟祥增.基于BP神经网络的图像形状识别.计算机科学,2006,33(1):269-271.

[28] Maniezzo V. Genetic evolution of the topology and weightdistribution of neural network[J].IEEE Transactions on  Neural Networks,1994,5(1):39-53.

[29]  郭晓婷,朱岩.基于遗传算法的进化神经网络[J].清华大学学报(自然科学版),2000,40(10):116-119.

[30]  Zhang Qinghua,Benveniste.A Wavelet network[J].IEEE Trans on Neural Networks,1992,3(6):889-898.

[31] 王阿明,刘天放,王绪.用于图像与模式识别的小波神经网络模型[J].中国矿业大学学报,2002,31(5):382-385.

[32] 袁宝玺,冯大毅,杨百愚.基于小波矩和SCNN的多目标图像识别[J].计算机工程与应用,2006,42(17):90-93.

[33] 李葆青.基于卷积神经网络的模式分类器[J].大连大学学报,2003,24(2):19-23.

[34] 范劲松,陶卿,方廷健.基于统计学习理论优化感知器的遗传算法[J].模式识别与人工智能,2001,14(2):211-215.

[35] 张学工.关于统计学习理论与支持向量机[J].自动化学报,2000,26(1):32-42.

[36] 徐旭东,周源华.基于小波不变矩的模式识别方法[J].红外与毫米波学报,2000,19(3):215-218.

你可能感兴趣的:(轮廓匹配、识别)