原创文章,欢迎转载。转载请注明:转载自 祥的博客
原文链接:http://blog.csdn.net/humanking7/article/details/46606791
图象的边缘是指图象局部区域亮度变化显著的部分,该区域的灰度剖面一般可以看作是一个阶跃,既从一个灰度值在很小的缓冲区域内急剧变化到另一个灰度相差较大的灰度值。
Canny边缘检测基本特征如下:
(1) 必须满足两个条件:①能有效地抑制噪声;②必须尽量精确确定边缘的位置。
(2) 根据对信噪比与定位乘积进行测度,得到最优化逼近算子。这就是Canny边缘检测算子。
(3) 类似与Marr(LoG)边缘检测方法,也属于先平滑后求导数的方法。
Canny边缘检测算法步骤:
步骤1:用高斯滤波器平滑处理原图像;
步骤2:用一阶偏导的有限差分进行计算梯度的幅值和方向;
步骤3:对梯度幅值进行非极大值抑制;
步骤4:用双阈值算法检测和连接边缘。
步骤详解
步骤1:用高斯滤波器平滑处理原图像
使用平滑滤波的原因从根本上来说是边缘检测算子中的导数计算。导数计算对噪声十分敏感,如果不提前使用滤波器加以改善,则在导数计算后,噪声将会被放大,使得检测出来的虚假边缘变多,不利于边缘的提取。
平滑滤波和边缘检测是一对矛盾的概念。一方面,平滑滤波能够有效的抑制噪声,而在此过程中会使得图像边缘模糊,增加了边缘定位的不确定性。另一方面,平滑滤波能够除去对边缘检测中导数运算敏感的噪声,有效的抑制了虚假边缘的产生。实际工程经验表明,高斯滤波器可以在抗噪声干扰和边缘检测精确定位之间提供一个较好的折中方案。
步骤2:用一阶偏导的有限差分进行计算梯度的幅值和方向
图像的边缘有方向和幅度两个属性,沿边缘方向像素变化平缓,垂直于边缘方向像素变化剧烈,边缘上的这种变化可以用微分算子检测出来,通常用一阶或二阶导数来检测边缘.
由上图可以看出,一阶导数可以用于检测图像中的一个点是否是边缘点(也就是判断一个点是否在斜坡上)。同样,二阶导数的符号可以用于判断一个边缘像素是否在亮的一边还是暗的一边。
用一阶偏导的有限差分来计算梯度的幅值和方向。
下图中,图a经过梯度计算后的得到梯度三维示意图b。图b中x和y代表图像像素位置,竖轴的数值反映了梯度幅值的大小。由公式(3-17)可知,边缘方向与梯度方向是相垂直的,如图b所示。
步骤3:对梯度幅值进行非极大值抑制
步骤4:用双阈值算法检测和连接边缘
代码
主函数代码
主函数代码文件main.m
clear all;
clear
clc;
%读进图像
[filename, pathname] = uigetfile({‘*.jpg‘; ‘*.bmp‘; ‘*.gif‘}, ‘选择图片‘);
%没有图像
if filename == 0
return;
end
imgsrc = imread([pathname, filename]);
[y, x, dim] = size(imgsrc);
%转换为灰度图
if dim>1
imgsrc = rgb2gray(imgsrc);
end
sigma = 1;
gausFilter = fspecial(‘gaussian‘, [3,3], sigma);
img= imfilter(imgsrc, gausFilter, ‘replicate‘);
zz = double(img);
%----------------------------------------------------------
%自己的边缘检测函数
[m theta sector canny1 canny2 bin] = canny1step(img, 22);
[msrc thetasrc sectorsrc c1src c2src binsrc] = canny1step(imgsrc, 22);
%Matlab自带的边缘检测
ed = edge(img, ‘canny‘, 0.5);
[xx, yy] = meshgrid(1:x, 1:y);
figure(1)
%mesh(yy, xx, zz);
surf(yy, xx, zz);
xlabel(‘y‘);
ylabel(‘x‘);
zlabel(‘Grayscale‘);
axis tight
figure(2)
subplot(4,2,1);
imshow(imgsrc);%原图
subplot(4,2,2);
imshow(img);%高斯滤波后
subplot(4,2,3);
imshow(uint8(m));%导数
subplot(4,2,4);
imshow(uint8(canny1));%非极大值抑制
subplot(4,2,5);
imshow(uint8(canny2));%双阈值
subplot(4,2,6);
imshow(ed);%Matlab自带边缘检测
subplot(4,2,8);
imshow(bin);%我自己的bin
figure(3)
edzz = 255*double(ed);
mesh(yy,xx,edzz);
xlabel(‘y‘);
ylabel(‘x‘);
zlabel(‘Grayscale‘);
axis tight
figure(4)
mesh(yy,xx,m);%画偏导数
xlabel(‘y‘);
ylabel(‘x‘);
zlabel(‘Derivative‘);
axis tight
figure(5)
mesh(yy,xx,theta);
xlabel(‘y‘);
ylabel(‘x‘);
zlabel(‘Theta‘);
axis tight
figure(6)
mesh(yy,xx,sector);
xlabel(‘y‘);
ylabel(‘x‘);
zlabel(‘Sector‘);
axis tight
figure(7)
mesh(yy,xx,canny2);
xlabel(‘y‘);
ylabel(‘x‘);
zlabel(‘Sector‘);
axis tight
Canny边缘检测函数代码
Canny边缘检测函数文件canny1step.m
function [ m, theta, sector, canny1, canny2, bin] = canny1step( src, lowTh)
%canny函数第一步,求去x,y方向的偏导,模板如下:
% Gx
% 1 -1
% 1 -1
% Gy
% -1 -1
% 1 1
%------------------------------------
% 输入:
% src:图像,如果不是灰度图转成灰度图
% lowTh:低阈值
% 输出:
% m: 两个偏导的平方差,反映了边缘的强度
% theta:反映了边缘的方向
% sector:将方向分为3个区域,具体如下
% 2 1 0
% 3 X 3
% 0 1 2
% canny1:非极大值
% canny2:双阈值抑制
% bin : 二值化
%---------------------------------------
[Ay Ax dim ] = size(src);
%转换为灰度图
if dim>1
src = rgb2gray(src);
end
src = double(src);
m = zeros(Ay, Ax);
theta = zeros(Ay, Ax);
sector = zeros(Ay, Ax);
canny1 = zeros(Ay, Ax);%非极大值抑制
canny2 = zeros(Ay, Ax);%双阈值检测和连接
bin = zeros(Ay, Ax);
for y = 1:(Ay-1)
for x = 1:(Ax-1)
gx = src(y, x) + src(y+1, x) - src(y, x+1) - src(y+1, x+1);
gy = -src(y, x) + src(y+1, x) - src(y, x+1) + src(y+1, x+1);
m(y,x) = (gx^2+gy^2)^0.5 ;
%--------------------------------
theta(y,x) = atand(gx/gy) ;
tem = theta(y,x);
%--------------------------------
if (tem<67.5)&&(tem>22.5)
sector(y,x) = 0;
elseif (tem<22.5)&&(tem>-22.5)
sector(y,x) = 3;
elseif (tem<-22.5)&&(tem>-67.5)
sector(y,x) = 2;
else
sector(y,x) = 1;
end
%--------------------------------
end
end
%-------------------------
%非极大值抑制
%------> x
% 2 1 0
% 3 X 3
%y 0 1 2
for y = 2:(Ay-1)
for x = 2:(Ax-1)
if 0 == sector(y,x) %右上 - 左下
if ( m(y,x)>m(y-1,x+1) )&&( m(y,x)>m(y+1,x-1) )
canny1(y,x) = m(y,x);
else
canny1(y,x) = 0;
end
elseif 1 == sector(y,x) %竖直方向
if ( m(y,x)>m(y-1,x) )&&( m(y,x)>m(y+1,x) )
canny1(y,x) = m(y,x);
else
canny1(y,x) = 0;
end
elseif 2 == sector(y,x) %左上 - 右下
if ( m(y,x)>m(y-1,x-1) )&&( m(y,x)>m(y+1,x+1) )
canny1(y,x) = m(y,x);
else
canny1(y,x) = 0;
end
elseif 3 == sector(y,x) %横方向
if ( m(y,x)>m(y,x+1) )&&( m(y,x)>m(y,x-1) )
canny1(y,x) = m(y,x);
else
canny1(y,x) = 0;
end
end
end%end for x
end%end for y
%---------------------------------
%双阈值检测
ratio = 2;
for y = 2:(Ay-1)
for x = 2:(Ax-1)
if canny1(y,x)<lowTh %低阈值处理
canny2(y,x) = 0;
bin(y,x) = 0;
continue;
elseif canny1(y,x)>ratio*lowTh %高阈值处理
canny2(y,x) = canny1(y,x);
bin(y,x) = 1;
continue;
else %介于之间的看其8领域有没有高于高阈值的,有则可以为边缘
tem =[canny1(y-1,x-1), canny1(y-1,x), canny1(y-1,x+1);
canny1(y,x-1), canny1(y,x), canny1(y,x+1);
canny1(y+1,x-1), canny1(y+1,x), canny1(y+1,x+1)];
temMax = max(tem);
if temMax(1) > ratio*lowTh
canny2(y,x) = temMax(1);
bin(y,x) = 1;
continue;
else
canny2(y,x) = 0;
bin(y,x) = 0;
continue;
end
end
end%end for x
end%end for y
end%end of function
结果对比
对比图像
分析
图2所示的四张图片展示了整个边缘检测的过程。
- 图(a)显示的为图像的梯度值,其三维图像为图3左所示。
- 图(b)显示了经过极大值抑制之后的图像,可以看出经过非极大值抑制之后,排除非边缘的像素,仅仅保留了一些细的线条,即就是候选的边缘。
- 图(c)为经过双阈值算法处理过后的二值化图像(选用的低阈值为22,高低阈值之比为2:1),对比图(b)发现此过程有效的抑制了“虚假边缘”的产生,而且也相应的连接了一些断裂的边缘。
- 图(d)是利用Matlab自带edge函数的Canny方法,可以看出其效果优于我实现的传统Canny程序,说明Matlab在阈值选取原则和梯度算子两方面有自己相应的改进。
图3展示了梯度幅值在非极大值抑制前后的三维效果,对应于图2中的图(a)和图(b)。
注: 本文所用的图像和代码源自于本人的毕设论文。