canny边缘检测学习笔记
Canny边缘检测的步骤:
1、高斯平滑(gaussian smoothing)
2、图像求导(derivation)
3、非极大值抑制(non-local maxima suppression)
4、滞后门限(hysteresis threshold)
* 非极大值抑制 和 滞后门限 是canny边缘检测的特色步骤
具体介绍:
http://blog.csdn.net/likezhaobin/article/details/6892176
http://www.cnblogs.com/blue-lg/archive/2011/12/25/2300899.html
算法实现(matlab源代码):
matlab中边缘检测的函数是edge(gray_image, 'canny');我把里面关于canny算法的部分提出来写成一个单独的函数。
0、首先是预处理
变量a是一个m*n的矩阵,是输入的灰度图。变量e是用来保存边缘的矩阵,是一个二值图。
1 function e = canny_edge_matlab(a) 2 % Transform to a double precision intensity image if necessary 3 if ~isa(a,'double') && ~isa(a,'single') 4 a = im2single(a); 5 end 6 [m,n] = size(a); 7 % The output edge map: 8 e = false(m,n);
1、高斯平滑
GaussianDieOff是高斯衰变值,小于该值的高斯函数值都视为0.
PercentOfPixelsNotEdges代表非边缘像素占总像素个数的百分比,默认为70%。ThresholdRatio是一个系数,lowThreshhold=ThresholdRatio*highThreshold。
sigma是高斯函数里的标准差,默认为1。高斯函数的模板大小最大为60*60,实际大小由GaussianDieOff和sigma共同决定
gau保存了1D的高斯模板,用来做高斯平滑。分别沿x和y轴做平滑
dgau2D保存了2D高斯函数对x的一阶偏导,为了做图像微分。
1 % Magic numbers 2 GaussianDieOff = .0001; 3 PercentOfPixelsNotEdges = .7; % Used for selecting thresholds 4 ThresholdRatio = .4; % Low thresh is this fraction of the high. 5 % Design the filters - a gaussian and its derivative 6 sigma=1; % sigma是一个入参,默认是1,这句是我加的,为了代码可以正常运行 7 pw = 1:30; % possible widths 8 ssq = sigma^2; 9 10 % 为了确定高斯核大小,当函数值小于衰减系数时,就把他视为0 11 % 窗口大小为power(2*width) 12 width = find(exp(-(pw.*pw)/(2*ssq))>GaussianDieOff,1,'last'); 13 if isempty(width) 14 width = 1; % the user entered a really small sigma 15 end 16 t = (-width:width); 17 gau = exp(-(t.*t)/(2*ssq))/(2*pi*ssq); % the gaussian 1D filter 18 19 % Find the directional derivative of 2D Gaussian (along X-axis) 20 % Since the result is symmetric along X, we can get the derivative along 21 % Y-axis simply by transposing the result for X direction. 22 [x,y]=meshgrid(-width:width,-width:width); 23 dgau2D=-x.*exp(-(x.*x+y.*y)/(2*ssq))/(pi*ssq); 24 25 % Convolve the filters with the image in each direction 26 % The canny edge detector first requires convolution with 27 % 2D gaussian, and then with the derivitave of a gaussian. 28 % Since gaussian filter is separable, for smoothing, we can use 29 % two 1D convolutions in order to achieve the effect of convolving 30 % with 2D Gaussian. We convolve along rows and then columns. 31 32 % smooth the image out 33 % 高斯模糊 34 aSmooth=imfilter(a,gau,'conv','replicate'); % run the filter across rows 35 aSmooth=imfilter(aSmooth,gau','conv','replicate'); % and then across columns
2、图像微分
dgau2D是高斯函数对x的偏导,其转置dgau2D‘是高斯函数对y的偏导。
ax是图像在x方向上与高斯微分卷积的结果(x方向上的图像微分),ay是y方向的图像微分。用ax和ay可以求出图像各个像素的梯度值和梯度方向。mag是梯度值。最后要对mag进行归一化。
1 %apply directional derivatives 2 %一阶高斯 3 ax = imfilter(aSmooth, dgau2D, 'conv','replicate'); 4 ay = imfilter(aSmooth, dgau2D', 'conv','replicate'); 5 mag = sqrt((ax.*ax) + (ay.*ay));
3、非极大值抑制
- 如果用户指定了阈值,就用用户指定的值,否则,用默认值计算阈值。阈值的计算方法是:
1)将图像按灰度值转化为64阶的直方图。
2)计算高阈值。高阈值就是图像中边缘像素灰度值的下限,这里默认有70%的像素不是边缘,现在要求出这70%的像素的灰度值水平。例如:

图片共45个像素,做成7阶直方图,70%像素不是边缘,即31个像素不是边缘,所以高阈值要淘汰掉这些像素。由图可知,前5阶像素个数和为31,所以将灰度值的水平设置为6,则70%的像素就被淘汰了。由于阈值取值范围是(0,1),所以该图片的阈值就是6/7。
3)计算低阈值。计算公式是lowThresh = ThresholdRatio*highThresh;
1 % Select the thresholds 2 thresh = ''; % 我自己加的,为了程序可以进入if 3 if isempty(thresh) 4 % 这里的直方图阶数可任意,理论上大于1小于255都可以,64是个比较好的值 5 counts=imhist(mag, 64); 6 highThresh = find(cumsum(counts) > PercentOfPixelsNotEdges*m*n,... 7 1,'first') / 64; 8 lowThresh = ThresholdRatio*highThresh; 9 thresh = [lowThresh highThresh]; 10 % 用户指定的阈值,我把他注释掉了 11 % elseif length(thresh)==1 12 % highThresh = thresh; 13 % if thresh>=1 14 % eid = sprintf('Images:%s:thresholdMustBeLessThanOne', mfilename); 15 % msg = 'The threshold must be less than 1.'; 16 % error(eid,'%s',msg); 17 % end 18 % lowThresh = ThresholdRatio*thresh; 19 % thresh = [lowThresh highThresh]; 20 % elseif length(thresh)==2 21 % lowThresh = thresh(1); 22 % highThresh = thresh(2); 23 % if (lowThresh >= highThresh) || (highThresh >= 1) 24 % eid = sprintf('Images:%s:thresholdOutOfRange', mfilename); 25 % msg = 'Thresh must be [low high], where low < high < 1.'; 26 % error(eid,'%s',msg); 27 % end 28 end
- 非极大值抑制主要使用了cannyFindLocalMaxima()这个函数来查找梯度方向上,与该像素相邻的区域内梯度的最大值。其实就是线性插值。这里要用到梯度的方向。

根据对称性,只需要分别求出4个方向区间的极大值。idxWeak保存了弱边缘的像素编号,idxStrong保存了强边缘的像素编号。代码如下:
1 % The next step is to do the non-maximum suppression. 2 % We will accrue indices which specify ON pixels in strong edgemap 3 % The array e will become the weak edge map. 4 idxStrong = []; 5 for dir = 1:4 6 idxLocalMax = cannyFindLocalMaxima(dir,ax,ay,mag); 7 idxWeak = idxLocalMax(mag(idxLocalMax) > lowThresh); 8 e(idxWeak)=1; % 先把弱边缘视为边缘,后面将用迟滞算法筛选与强边缘相连的弱边缘 9 idxStrong = [idxStrong; idxWeak(mag(idxWeak) > highThresh)]; %#ok<AGROW> 10 end
- 某个方向上的极大值查找主要使用了Local Function : cannyFindLocalMaxima。如果该像素是其梯度方向上的局部极大值,则返回该像素编号。注释写在代码中了:
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2 % 3 % Local Function : cannyFindLocalMaxima 4 % 5 function idxLocalMax = cannyFindLocalMaxima(direction,ix,iy,mag) 6 % 7 % This sub-function helps with the non-maximum suppression in the Canny 8 % edge detector. The input parameters are: 9 % 10 % direction - the index of which direction the gradient is pointing, 11 % read from the diagram below. direction is 1, 2, 3, or 4. 12 % ix - input image filtered by derivative of gaussian along x 13 % iy - input image filtered by derivative of gaussian along y 14 % mag - the gradient magnitude image 15 % 16 % there are 4 cases: 17 % 18 % The X marks the pixel in question, and each 19 % 3 2 of the quadrants for the gradient vector 20 % O----0----0 fall into two cases, divided by the 45 21 % 4 | | 1 degree line. In one case the gradient 22 % | | vector is more horizontal, and in the other 23 % O X O it is more vertical. There are eight 24 % | | divisions, but for the non-maximum suppression 25 % (1)| |(4) we are only worried about 4 of them since we 26 % O----O----O use symmetric points about the center pixel. 27 % (2) (3) 28 29 30 [m,n] = size(mag); 31 32 % Find the indices of all points whose gradient (specified by the 33 % vector (ix,iy)) is going in the direction we're looking at. 34 % 找到各个区间的梯度,其中ix.iy是沿坐标方向的两个梯度,按照上面的划分 35 % 第一个区间的梯度应满足(iy<=0 & ix<iy) | (iy>0 & ix>iy),其他区间以此类推 36 % ix和iy都是m*n的矩阵,find会把他们都当作一个向量来处理(按列存储!) 37 % find将在他们中找出满足条件的梯度对应的下标,返回的结果是一个列向量,重复的下标只保留一个 38 switch direction 39 case 1 40 idx = find((iy<=0 & ix>-iy) | (iy>=0 & ix<-iy)); 41 case 2 42 idx = find((ix>0 & -iy>=ix) | (ix<0 & -iy<=ix)); 43 case 3 44 idx = find((ix<=0 & ix>iy) | (ix>=0 & ix<iy)); 45 case 4 46 idx = find((iy<0 & ix<=iy) | (iy>0 & ix>=iy)); 47 end 48 49 % Exclude the exterior pixels 50 % 去除矩阵最外面的一圈 51 % 1 5 9 13 52 % 2 6 10 14 53 % 3 7 11 15 54 % 4 8 12 16 55 % v==1和v==0 可以去除第一行和最后一行 56 % idx<=m可以去除第一列,idx>(n-1)*m可以去除最后一列 57 if ~isempty(idx) 58 v = mod(idx,m); 59 extIdx = (v==1 | v==0 | idx<=m | (idx>(n-1)*m)); 60 idx(extIdx) = []; 61 end 62 63 ixv = ix(idx); 64 iyv = iy(idx); 65 gradmag = mag(idx); 66 67 % Do the linear interpolations for the interior pixels 68 % 线性插值。对于下面这个矩阵: 69 % 1 5 9 13 70 % 2 6 10 14 71 % 3 7 11 15 72 % 4 8 12 16 73 % 比较idx与其梯度方向上相邻两个像素梯度的大小,由于idx梯度方向上不一定存在像素, 74 % 所以要进行线性插值(注意matlab中,矩阵按列存储) 75 % 对于case1,假设idx=10,则mag(idx+m)得到的是idx右临像素14的梯度值, 76 % mag(idx+m-1)得到的是像素13的梯度值。 77 % d确定了梯度的方向,根据这个方向可以求出插值点与两个参考点的距离, 78 % 距离越小,这个参考点的权重越大 79 switch direction 80 case 1 81 d = abs(iyv./ixv); 82 gradmag1 = mag(idx+m).*(1-d) + mag(idx+m-1).*d; 83 gradmag2 = mag(idx-m).*(1-d) + mag(idx-m+1).*d; 84 case 2 85 d = abs(ixv./iyv); 86 gradmag1 = mag(idx-1).*(1-d) + mag(idx+m-1).*d; 87 gradmag2 = mag(idx+1).*(1-d) + mag(idx-m+1).*d; 88 case 3 89 d = abs(ixv./iyv); 90 gradmag1 = mag(idx-1).*(1-d) + mag(idx-m-1).*d; 91 gradmag2 = mag(idx+1).*(1-d) + mag(idx+m+1).*d; 92 case 4 93 d = abs(iyv./ixv); 94 gradmag1 = mag(idx-m).*(1-d) + mag(idx-m-1).*d; 95 gradmag2 = mag(idx+m).*(1-d) + mag(idx+m+1).*d; 96 end 97 idxLocalMax = idx(gradmag>=gradmag1 & gradmag>=gradmag2);
4、滞后门限
在强边缘的八个领域中,如果存在弱边缘,则将弱边缘加入到强边缘中,直到没有新的像素被加入到强边缘。
matlab使用的是bwselect()函数实现这个功能的,该函数可以从某些特定的像素开始,在二值图中查找4连通或者8联通区域。
程序最后,使用了形态学函数bwmorph(e, 'thin', 1)。我们知道,一个高质量的边缘检测算法应该满足两个条件:good localization (edges marked should be as close as possible to the edge in the real image) 以及 good detection(the algorithm should mark as many real edges in the images as possible)。这个函数可以把一条“粗”线,取中间值变成一条“细线”,得到 good localization。非极大值抑制可以得到 good localization,滞后门限可以得到 good detection。
1 % 1 5 9 13 2 % 2 6 10 14 3 % 3 7 11 15 4 % 4 8 12 16 5 if ~isempty(idxStrong) % result is all zeros if idxStrong is empty 6 rstrong = rem(idxStrong-1, m)+1; % 余数,代表强边缘的行号 7 cstrong = floor((idxStrong-1)/m)+1; % 商,代表强边缘的列号 8 % 从e中选出某些像素开始的8连通像素 9 e = bwselect(e, cstrong, rstrong, 8); 10 % 'thin' With N = Inf, remove pixels so that an object 11 % without holes shrinks to a minimally 12 % connected stroke, and an object with holes 13 % shrinks to a ring halfway between the hold 14 % and outer boundary 15 e = bwmorph(e, 'thin', 1); % Thin double (or triple) pixel wide contours 16 end

浙公网安备 33010602011771号