经典的边缘检测方法是对原始图像中像素的某小邻域来构造边缘检测算子。常用的边缘检测方法有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);
}
//如果源图像是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 8
for(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);