形状特征
(一)特点:各种基于形状特征的检索方法都可以比较有效地利用图像中感兴趣的目标来进行检索,但它们也有一些共同的问题,包括:①目前基于形状的检索方法还缺乏比较完善的数学模型;②如果目标有变形时检索结果往往不太可靠;③许多形状特征仅描述了目标局部的性质,要全面描述目标常对计算时间和存储量有较高的要求;④许多形状特征所反映的目标形状信息与人的直观感觉不完全一致,或者说,特征空间的相似性与人视觉系统感受到的相似性有差别。另外,从 2-D 图像中表现的 3-D 物体实际上只是物体在空间某一平面的投影,从 2-D 图像中反映出来的形状常不是 3-D 物体真实的形状,由于视点的变化,可能会产生各种失真。
(二)常用的特征提取与匹配方法
Ⅰ几种典型的形状特征描述方法
通常情况下,形状特征有两类表示方法,一类是轮廓特征,另一类是区域特征。图像的轮廓特征主要针对物体的外边界,而图像的区域特征则关系到整个形状区域。
几种典型的形状特征描述方法:
(1)边界特征法该方法通过对边界特征的描述来获取图像的形状参数。其中Hough 变换检测平行直线方法和边界方向直方图方法是经典方法。Hough 变换是利用图像全局特性而将边缘像素连接起来组成区域封闭边界的一种方法,其基本思想是点—线的对偶性;边界方向直方图法首先微分图像求得图像边缘,然后,做出关于边缘大小和方向的直方图,通常的方法是构造图像灰度梯度方向矩阵。
(2)傅里叶形状描述符法
傅里叶形状描述符(Fourier shape descriptors)基本思想是用物体边界的傅里叶变换作为形状描述,利用区域边界的封闭性和周期性,将二维问题转化为一维问题。
由边界点导出三种形状表达,分别是曲率函数、质心距离、复坐标函数。
(3)几何参数法
形状的表达和匹配采用更为简单的区域特征描述方法,例如采用有关形状定量测度(如矩、面积、周长等)的形状参数法(shape factor)。在 QBIC 系统中,便是利用圆度、偏心率、主轴方向和代数不变矩等几何参数,进行基于形状特征的图像检索。
需要说明的是,形状参数的提取,必须以图像处理及图像分割为前提,参数的准确性必然受到分割效果的影响,对分割效果很差的图像,形状参数甚至无法提取。
(4)形状不变矩法
利用目标所占区域的矩作为形状描述参数。
(5)其它方法
近年来,在形状的表示和匹配方面的工作还包括有限元法(Finite Element Method 或 FEM)、旋转函数(Turning Function)和小波描述符(Wavelet Descriptor)等方法。
实际上,只是提取物体的形状,这并不难,最难的是这些特征该怎么用!
特征嘛,自然是讲此物区分彼物的特点。
那么假如,给出了一系列不同形状物体的轮廓该如何识别出他们呢?正方形,圆形,矩形,椭圆,不规则图形,再进一步,这些图形由于受到信号的干扰,有噪声存在时,该如何去识别他们呢?
那就可以使用形状的特征了,我们定义一些参数,来描述这些形状。
1 矩形度:R = A0/A; A0为区域面积,A为区域最小外接矩形面积。
那么R = 1 时,为矩形的概率很大,R = PI/4时为圆的可能性最大
2 体态比 T = a/b;
a b 分别为区域最小外接矩形的长和宽。
T = 1 为正方形或者圆形,
T>1 为细长图形
3 球状性 S = Ri/Rc
Ri Rc分别为内切圆和外接圆半径,圆心都在中心上
4 球状性 C = Ur/Pr
Ur 为区域重心到轮廓点的平均距离
Pr 为区域重心到轮廓点的均方差
5 中心矩
这一特征,使用颇为频繁,OpenCV有专门的函数求解(p,q)次矩
6 长轴 短轴
最小外接矩形的长轴和短轴
7面积
一般会作为阈值使用,判定某个区域的面积在两个阈值之间才判定有效
下面给出各个形状特征的求法:
1 //图像的形状特征分析 2 #include <cv.h> 3 #include <cxcore.h> 4 #include <highgui.h> 5 #include <iostream> 6 using namespace std; 7 8 int main() 9 { 10 IplImage *src = cvLoadImage("E:\\image\\mapleleaf.tif",0); 11 IplImage *image = cvCreateImage(cvGetSize(src),8,3); 12 image = cvCloneImage(src); 13 cvNamedWindow("src",1); 14 cvNamedWindow("dst",1); 15 cvShowImage("src",src); 16 17 CvMemStorage *storage = cvCreateMemStorage(0); 18 CvSeq * seq = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage); 19 CvSeq * tempSeq = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage); 20 //新图,将轮廓绘制到dst 21 IplImage *dst = cvCreateImage(cvGetSize(src),8,3); 22 cvZero(dst);//赋值为0 23 double length,area; 24 25 //获取轮廓 26 int cnt = cvFindContours(src,storage,&seq);//返回轮廓的数目 27 cout<<"number of contours "<<cnt<<endl; 28 29 //计算边界序列的参数 长度 面积 矩形 最小矩形 30 //并输出每个边界的参数 31 CvRect rect; 32 CvBox2D box; 33 double axislong,axisShort;//长轴和短轴 34 double temp1= 0.0,temp2 = 0.0; 35 double Rectangle_degree;//矩形度 36 double long2short;//体态比 37 double x0,y0; 38 long sumX = 0 ,sumY = 0; 39 double sum =0.0; 40 int i,j,m,n; 41 unsigned char* ptr; 42 43 double UR;//区域重心到轮廓的平均距离 44 double PR;//区域重心到轮廓点的均方差 45 CvPoint * contourPoint; 46 int count = 0; 47 double CDegree;//圆形性 48 49 CvPoint *A,*B,*C; 50 double AB,BC,AC; 51 double cosA,sinA; 52 double tempR,inscribedR; 53 54 for (tempSeq = seq;tempSeq != NULL; tempSeq = tempSeq->h_next) 55 { 56 //tempSeq = seq->h_next; 57 length = cvArcLength(tempSeq); 58 area = cvContourArea(tempSeq); 59 cout<<"Length = "<<length<<endl; 60 cout<<"Area = "<<area<<endl; 61 cout<<"num of point "<<tempSeq->total<<endl; 62 //外接矩形 63 rect = cvBoundingRect(tempSeq,1); 64 65 66 //绘制轮廓和外接矩形 67 cvDrawContours(dst,tempSeq,CV_RGB(255,0,0),CV_RGB(255,0,0),0); 68 cvRectangleR(dst,rect,CV_RGB(0,255,0)); 69 cvShowImage("dst",dst); 70 //cvWaitKey(); 71 72 //绘制轮廓的最小外接圆 73 CvPoint2D32f center;//亚像素精度 因此需要使用浮点数 74 float radius; 75 cvMinEnclosingCircle(tempSeq,¢er,&radius); 76 cvCircle(dst,cvPointFrom32f(center),cvRound(radius),CV_RGB(100,100,100)); 77 cvShowImage("dst",dst); 78 //cvWaitKey(); 79 80 //寻找近似的拟合椭圆 可以使斜椭圆 81 CvBox2D ellipse = cvFitEllipse2(tempSeq); 82 cvEllipseBox(dst,ellipse,CV_RGB(255,255,0)); 83 cvShowImage("dst",dst); 84 //cvWaitKey(); 85 86 87 //绘制外接最小矩形 88 CvPoint2D32f pt[4]; 89 box = cvMinAreaRect2(tempSeq,0); 90 cvBoxPoints(box,pt); 91 for(int i = 0;i<4;++i){ 92 cvLine(dst,cvPointFrom32f(pt[i]),cvPointFrom32f(pt[((i+1)%4)?(i+1):0]),CV_RGB(0,0,255)); 93 } 94 cvShowImage("dst",dst); 95 //cvWaitKey(); 96 97 98 //下面开始分析图形的形状特征 99 //长轴 短轴 100 temp1 = sqrt(pow(pt[1].x -pt[0].x,2) + pow(pt[1].y -pt[0].y,2)); 101 temp2 = sqrt(pow(pt[2].x -pt[1].x,2) + pow(pt[2].y -pt[1].y,2)); 102 103 if (temp1 > temp2) 104 { 105 axislong = temp1; 106 axisShort=temp2; 107 } 108 else 109 { 110 axislong = temp2; 111 axisShort=temp1; 112 } 113 114 cout<<"long axis: "<<axislong<<endl; 115 cout<<"short axis: "<<axisShort<<endl; 116 //矩形度 轮廓面积和最小外接矩形面积(可以是斜矩形)之比 117 Rectangle_degree = (double)area/(axisShort*axislong); 118 119 cout<<"Rectangle degree :"<<Rectangle_degree<<endl; 120 //体态比or长宽比 最下外接矩形的长轴和短轴的比值 121 long2short = axislong/axisShort; 122 cout<<"ratio of long axis to short axis: "<<long2short<<endl; 123 //球状性 由于轮廓的内切圆暂时无法求出先搁置 124 //先求内切圆半径 枚举任意轮廓上的三个点,半径最小的就是内切圆的半径 125 //以下的最大内切圆半径求法有误 待改进 126 127 /* 128 for (int i = 0 ; i< tempSeq->total -2;i++) 129 { 130 for (int j= i+1; j<tempSeq->total-1;j++) 131 { 132 for (int m = j+1; m< tempSeq->total; m++) 133 { 134 //已知圆上三点,求半径 135 A = (CvPoint*)cvGetSeqElem(tempSeq ,i); 136 B = (CvPoint*)cvGetSeqElem(tempSeq ,j); 137 C = (CvPoint*)cvGetSeqElem(tempSeq,m); 138 AB = sqrt(pow((double)A->x - B->x,2)+ pow((double)A->y - B->y,2)); 139 AC =sqrt(pow((double)A->x - C->x,2) + pow((double)A->y - C->y,2)); 140 BC = sqrt(pow((double)B->x - C->x,2)+ pow((double)B->y - C->y,2)); 141 142 cosA = ((B->x - A->x)*(C->x - A->x) + (B->y - A->y)*(C->y - A->y))/(AB*AC); 143 sinA = sqrt(1 - pow(cosA,2)); 144 tempR = BC/(2*sinA); 145 146 if (m == 2) 147 { 148 inscribedR = tempR; 149 } 150 else 151 { 152 if (tempR < inscribedR) 153 { 154 inscribedR = tempR; 155 } 156 } 157 158 159 } 160 } 161 } 162 163 //输出最大内切圆半径 164 cout<<"radius of max inscribed circle "<<inscribedR<<endl; 165 */ 166 //圆形性 假设轮廓内是实心的 167 //球区域中心x0 y0 168 sumX = 0; 169 sumY = 0; 170 src = cvCloneImage(image); 171 for (int i = 0 ; i< src->height;i++) 172 { 173 for (int j = 0; j< src->width;j++) 174 { 175 ptr = (unsigned char *)src->imageData + i*src->widthStep + j; 176 if ((*ptr) > 128) 177 { 178 sumX += (long)j; 179 sumY += (long)i; 180 } 181 182 } 183 } 184 x0 = sumX/area; 185 y0 = sumY/area; 186 cout<<"center of gravity "<<x0<<" "<<y0<<endl; 187 //求区域到重心的平均距离 188 sum = 0; 189 count = 0; 190 for (m = 0 ; m< tempSeq->total;m++) 191 { 192 contourPoint = (CvPoint*)cvGetSeqElem(tempSeq,m); 193 sum += sqrt(pow(contourPoint->x - x0,2)+ pow(contourPoint->y - y0,2)); 194 count++; 195 } 196 UR = sum/count; 197 cout<<"mean distance to center of gravity"<<UR<<endl; 198 //求区域重心到轮廓点的均方差 199 sum = 0; 200 for (m = 0 ; m< tempSeq->total;m++) 201 { 202 contourPoint = (CvPoint*)cvGetSeqElem(tempSeq,m); 203 temp1 = sqrt(pow(contourPoint->x - x0,2)+ pow(contourPoint->y - y0,2)); 204 sum += pow(temp1 - UR,2); 205 } 206 PR = sum/count; 207 cout<<"mean square error of distance to center of gravity"<<PR<<endl; 208 //圆形性 209 CDegree= UR/PR; 210 cout<<"degree of circle "<<CDegree<<endl; 211 //中心距 212 213 cvWaitKey(0); 214 } 215 216 217 cvReleaseImage(&src); 218 cvReleaseImage(&dst); 219 cvReleaseMemStorage(&storage); 220 221 return 0; 222 }