經典的邊緣檢測方法是對原始映像中像素的某小鄰域來構造邊緣檢測運算元。常用的邊緣檢測方法有Roberts運算元、Sobe l運算元、Prew itt運算元、K irsch運算元、Laplacian運算元、LOG 運算元、C anny運算元等。
一、基於一階微分的邊緣檢測方法
1、最簡單的梯度運算元是Roberts運算元, 它是一個2 * 2模板, 利用局部差分檢測比較陡峭的邊緣, 但對於雜訊較敏感, 經常會出現孤立點 。
//roberts運算元求映像梯度提取邊緣,輸入源映像,輸出梯度圖,此方法不常用void roberts(IplImage *src,IplImage *dst){ //為roberts映像申請空間,因為要利用源映像指標中的imageData,因此使用複製方式 dst=cvCloneImage(src); int x,y,i,w,h; int temp,temp1; uchar* ptr=(uchar*) (dst->imageData ); int ptr1[4]={0}; int indexx[4]={0,1,1,0}; int indexy[4]={0,0,1,1}; w=dst->width; h=dst->height; for(y=0;y<h-1;y++) for(x=0;x<w-1;x++) { for(i=0;i<4;i++) //取每個2*2矩陣元素的指標 0 | 1 { // 3 | 2 ptr1[i]= *(ptr+(y+indexy[i])*dst->widthStep+x+indexx[i]); } temp=abs(ptr1[0]-ptr1[2]); //計算2*2矩陣中0和2位置的差,取絕對值temp temp1=abs(ptr1[1]-ptr1[3]); //計算2*2矩陣中1和3位置的差,取絕對值temp1 temp=(temp>temp1?temp:temp1); //若temp1>temp,則以temp1的值替換temp temp= (int)sqrt(float(temp*temp)+float(temp1*temp1)); //輸出值 /* if (temp>100) temp=255; else temp=0; */ *(ptr+y*dst->widthStep+x)=temp; //將輸出值存放於dst像素的對應位置 } double min_val = 0,max_val = 0;//取圖並顯示像中的最大最小像素值 cvMinMaxLoc(dst,&min_val,&max_val); printf("max_val = %f\nmin_val = %f\n",max_val,min_val); cvSaveImage("RobertsImg.jpg", dst);//把映像存入檔案 cvNamedWindow("robert",1); cvShowImage("robert",dst);}
2、Prewitt運算元和Sobe l運算元。這兩種運算元在求梯度之前, 首先進行鄰域平均或加權平均, 然後進行微分, 就抑制了雜訊, 但容易出現邊緣模糊現象。
//如果源映像是8位的,為避免溢出,靶心圖表像深度必須是16S,或32位
void sobel(IplImage *src,IplImage *dst){//為soble微分映像申請空間,建立圖片函數IplImage *pSobelImg_dx = cvCreateImage(cvGetSize(src),32,1);IplImage *pSobelImg_dy = cvCreateImage(cvGetSize(src),32,1);IplImage *pSobelImg_dxdy = cvCreateImage(cvGetSize(src), 32,1);//用sobel運算元計算兩個方向的微分cvSobel(src , pSobelImg_dx, 1, 0, 3);cvSobel(src , pSobelImg_dy, 0, 1, 3); //total gradient = sqrt(horizontal*horizontal+vertical*vertical) int i,j; double v1,v2,v; for (i=0;i<src->height;i++) { for (j=0;j<src->width;j++) { v1 = cvGetReal2D(pSobelImg_dx,i,j); v2 = cvGetReal2D(pSobelImg_dy,i,j); v = sqrt(v1*v1+v2*v2);/*if(v>100) v = 255;else v = 0;*/ cvSetReal2D(pSobelImg_dxdy,i,j,v); } }cvConvertScale(pSobelImg_dxdy,dst); //將映像轉化為8位double min_val = 0,max_val = 0;//取圖並顯示像中的最大最小像素值cvMinMaxLoc(pSobelImg_dxdy,&min_val,&max_val); printf("max_val = %f\nmin_val = %f\n",max_val,min_val); //歸一化 cvNormalize(dst,dst,0,255,CV_MINMAX,0);
//prewitt運算元,模板卷積公式編寫,常用方法
void prewitt(IplImage *src,IplImage *dst){ //定義prewitt運算元的模板 float prewittx[9] = { -1,0,1, -1,0,1, -1,0,1 }; float prewitty[9] = { 1,1,1, 0,0,0, -1,-1,-1 }; CvMat px; px = cvMat(3,3,CV_32F,prewittx); CvMat py; py = cvMat(3,3,CV_32F,prewitty);
//為輸出映像申請空間 IplImage *dstx = cvCreateImage(cvGetSize(src),8,1); IplImage *dsty = cvCreateImage(cvGetSize(src),8,1);
//對映像使用模板,自動填滿邊界 cvFilter2D(src,dstx,&px,cvPoint(-1,-1)); cvFilter2D(src,dsty,&py,cvPoint(-1,-1));
//計算梯度,範數為2,注意學習指標的使用方法 int i,j,temp; float tempx,tempy; //定義為浮點型是為了避免sqrt函數引起歧義 uchar* ptrx = (uchar*) dstx->imageData; uchar* ptry = (uchar*) dsty->imageData; for(i = 0;i<src->width;i++) { for(j = 0;j<src->height;j++) { tempx = ptrx[i+j*dstx->widthStep]; //tempx,tempy表示的是指標所指向的像素 tempy = ptry[i+j*dsty->widthStep]; temp = (int) sqrt(tempx*tempx+tempy*tempy); /*if(temp>100) temp = 255; else temp = 0;*/ dst->imageData[i+j*dstx->widthStep] = temp; } } double min_val = 0, max_val = 0;//取圖並顯示像中的最大最小像素值 cvMinMaxLoc(dst,&min_val,&max_val); printf("max_val = %f\nmin_val = %f\n",max_val,min_val); //計算梯度,範數為1 //cvAdd(dstx,dsty,dst); cvSaveImage("PrewittImg.jpg", dst);//把映像存入檔案 cvReleaseImage(&dstx); cvReleaseImage(&dsty); cvNamedWindow("prewitt",1); cvShowImage("prewitt",dst);}
//Kirsch運算元,根據方向的對稱性,可以只對前面4個模板進行處理,求最大值。採用求中心像素周圍像素梯度,此方法是最好用的方法。void kirsch(IplImage *src,IplImage *dst){dst = cvCloneImage(src);//cvConvert(src,srcMat); //將映像轉化成矩陣處理int x,y;float a,b,c,d;float p1,p2,p3,p4,p5,p6,p7,p8,p9; uchar* ps = (uchar*)src->imageData ; //ps為指向輸入圖片資料的指標uchar* pd = (uchar*)dst->imageData ; //pd為指向輸出圖片資料的指標int w = dst->width;int h = dst->height;int step = dst->widthStep;for(x = 0;x<w-2;x++) //取以(x+1,y+1)為中心的9個鄰域像素 1 4 7{ // 2 5 8for(y = 0;y<h-2;y++) // 3 6 9{ p1=ps[y*step+x];p2=ps[y*step+(x+1)];p3=ps[y*step+(x+2)];p4=ps[(y+1)*step+x];p5=ps[(y+1)*step+(x+1)];p6=ps[(y+1)*step+(x+2)];p7=ps[(y+2)*step+x];p8=ps[(y+2)*step+(x+1)];p9=ps[(y+2)*step+(x+2)];//得到(i+1,j+1)周圍九個點的灰階值a = fabs(float(-5*p1-5*p2-5*p3+3*p4+3*p6+3*p7+3*p8+3*p9)); //計算4個方向的梯度值b = fabs(float(3*p1-5*p2-5*p3+3*p4-5*p6+3*p7+3*p8+3*p9));c = fabs(float(3*p1+3*p2-5*p3+3*p4-5*p6+3*p7+3*p8-5*p9));d = fabs(float(3*p1+3*p2+3*p3+3*p4-5*p6+3*p7-5*p8-5*p9));a = max(a,b); //取各個方向上的最大值作為邊緣強度a = max(a,c);a = max(a,d);pd[(y+1)*step+(x+1)] = a;/*if(a>100){pd[(y+1)*step+(x+1)]=255;}else pd[(y+1)*step+(x+1)]=0;*/}}double min_val = 0, max_val = 0;//取圖並顯示像中的最大最小像素值cvMinMaxLoc(dst,&min_val,&max_val); printf("max_val = %f\nmin_val = %f\n",max_val,min_val);cvNormalize(dst,dst,0,255,CV_MINMAX); //歸一化處理cvSaveImage("KirschImg.jpg", dst);//把映像存入檔案cvNamedWindow("kirsch",1);cvShowImage("kirsch",dst);