小白学习图像处理6——Canny边缘检测算法原理

目录结构

  • 一、高斯滤波
  • 二、梯度计算
      • 1、原理和过程
      • 2、代码实现
  • 三、非极大值抑制
      • 1、原理和过程
      • 2、代码实现
  • 四、双阈值检测
      • 1、原理和过程
      • 2、代码实现
  • 五、matlab 的边缘检测函数
  • 六、总结

Canny边缘检测算法主要步骤:高斯滤波、梯度计算、非极大值抑制和双阈值检测。

一、高斯滤波

  使用高斯滤波的目的是平滑图像,滤除图像中的部分噪声(因为微分算子对噪声很敏感)。高斯滤波具体办法是生成一个高斯模板,使用卷积进行时域滤波:

img = im2double(img);
h = fspecial('gaussian', 5, 2); % gaussian平滑模板
img = imfilter(img, h);

效果如下(拿我家小宝贝的爪爪试试效果 ):
小白学习图像处理6——Canny边缘检测算法原理_第1张图片



二、梯度计算

1、原理和过程

  一幅图像可以表示为函数 I = f(x, y),其中(x, y)为坐标,I表示该像素点的灰度值,梯度gradient表示函数f(x, y)在点(x, y)处最大的变化率,计算的表达式为:
G ( x ) = d f ( x , y ) d x + d f ( x , y ) d y G(x)=\frac{df(x,y)}{dx}+\frac{df(x,y)}{dy} G(x)=dxdf(x,y)+dydf(x,y)
  对于图像,我们也可以计算梯度,由于数字图像是有离散的像素点的灰度值构成,所以微分运算就变成了差分,我们可以用相邻两个像素点之间的差分值表示该像素点在某个方向上灰度的变化情况。


  由于图像在边缘的变化情况很剧烈,而在非边缘处变化平缓,所以计算一幅图像的梯度得到的结果中,图像的边缘将被凸显出来,sobel算子是性能不错的微分算子,下图描述了使用sobel算子计算梯度的卷积过程:
小白学习图像处理6——Canny边缘检测算法原理_第2张图片
  上图所示为一个卷积过程(由于sobel算子做一个180旋转后和旋转前差别不大,因此可以用卷积替代相关运算),橙色的矩阵E为 sobel 算子,它可以用来计算 x 方向上的灰度变化,如果我们把矩阵E与图像中某个像素及其8邻域的元素构成的矩阵的对应元素相乘,可以得到该像素点在x方向的差分,所有像素点在x方向上的差分构成矩阵Gx,同理我们也可以计算得到y方向上的差分Gy,于是梯度G的大小为:
G = ( G x 2 + G y 2 ) 1 2 G = (Gx^2 + Gy^2)^\frac{1}{2} G=(Gx2+Gy2)21
另外,为了后续处理,我们需要知道梯度的方向,在matlab中可以使用atan函数通过正切值计算角度(角度取值范围为-pi到pi)


2、代码实现

小细节:
语句 Gx = conv2(gray_img, gx, 'same'); 表示灰度图与x方向的sobel算子进行卷积,结果只保留与gray_img大小相同的部分(两个矩阵卷积,col=col1+col2-1, row=row1+row2-1

gx = [1 2 1; 0 0 0; -1 -2 -1];
gy = [1 0 -1; 2 0 -2; 1 0 -1];
Gx = conv2(img, gx, 'same');
Gy = conv2(img, gy, 'same');
G = sqrt(Gx.^2 + Gy.^2);
theta = atan(Gy./Gx);   % atan2(y, x)
figure, imshow(G)
title('梯度运算后')
set(gcf, 'color', [1 1 1])

得到的结果如下:
小白学习图像处理6——Canny边缘检测算法原理_第3张图片



三、非极大值抑制

1、原理和过程

目的细化边缘,在上面的图像中,梯度计算得到的边缘很粗,一条边缘中央一般很亮,两边亮度逐渐降低,可以根据这个特点去掉非局部灰度最高的“假边”,达到细化边缘的目的。
主要步骤

  • 判断梯度的方向进行线性插值,如下图:
    小白学习图像处理6——Canny边缘检测算法原理_第4张图片
      假如我们知道了倾斜角θ的大小,那么可以确定插值的两个位置M和N,M和M的插值数值取决于与它最近的两个像素点的灰度和距离,假设P和Q的灰度分别为p和q,则M点的灰度应该为 dy*p + (1-dy)*q ,dy为MQ的长度。
  • 比较梯度方向上和反方向上的两个灰度值大小(当然,取N个点进行比较)
    - 如果当前像素点的回灰度是最大值,则不做任何操作
    - 否则,则当前像素点的灰度置零

2、代码实现

注意,代码在梯度矩阵G的边缘加了一圈,避免数组越界
根据梯度的方向将角度分为四种情况,分别计算插值得到的两个灰度值,比较灰度值

[M, N] = size(G);
temp = zeros(M+2, N+2); % 补充一圈轮廓
temp(2:M+1, 2:N+1) = G;
G = temp;
temp(2:M+1, 2:N+1) = theta;
theta = temp;
Edge = G(2:M+1, 2:N+1);

for x = 2 : M+1
    for y = 2 : N+1
        % 插值 判断
        direction = theta(x, y);
        dx = abs(cot(direction));
        dy = abs(tan(direction));
        
        % 根据梯度的方向(-pi/2, pi/2)划分情况 插值
        if (-pi/2 <= direction <= -pi/4)
            n1 = (1-dx)*G(x, y-1) + dx*G(x+1, y-1);
            n2 = (1-dx)*G(x, y+1) + dx*G(x-1, y+1);
        elseif -pi/4 < direction <= 0
            n1 = (1-dy)*G(x+1, y) + dy*G(x+1, y-1);
            n2 = (1-dy)*G(x-1, y) + dy*G(x-1, y+1);
        elseif 0 < direction <= pi /4
            n1 = (1-dy)*G(x+1, y) + dy*G(x+1, y+1);
            n2 = (1-dy)*G(x-1, y) + dy*G(x-1, y-1);
        else
            n1 = (1-dx)*G(x, y+1) + dx*G(x+1, y+1);
            n2 = (1-dx)*G(x, y-1) + dx*G(x-1, y-1);
        end
        
        if Edge(x-1, y-1) < n1 || Edge(x-1, y-1) < n2    % 判断是否为梯度最大值()
            Edge(x-1, y-1) = 0;
        end
    end
end

figure, imshow(Edge)
title('非极大值抑制后')
set(gcf, 'color', [1 1 1])

结果如下(边缘很细,但是存在一些暗的伪边缘):
小白学习图像处理6——Canny边缘检测算法原理_第5张图片


四、双阈值检测

1、原理和过程

目的减少伪边缘点非极大值抑制之后,检测到的边缘线条比较多,我们可以滤掉一些很暗的边缘,并让主要的边缘凸显出来
步骤:

  1. 设置两个阈值 threshold : threshold_low 和threshold_high
  2. 当某个像素点值高于threshold_low 时,则可以认为它是边缘,把它的灰度置为1
  3. 当某个像素点值高于threshold_low 时,则认为它不是边缘,把它的灰度置为0
  4. 处于threshold_low 和threshold_high 之间的像素点,如果它的八邻域有真边上的点,则认为它也是边缘,并把灰度置为1

小技巧:判断灰度值位于高阈值和低阈值之间的像素点八邻域是否含有真边,可以使用最大值滤波,模板大小为3 x 3


2、代码实现

  构建一个新的图像,用来保存最终的边缘,首先将灰度高于高阈值的点放进去,相当于定下边缘的一些点,接着把疑似边缘且八邻域有构成边缘像素点的像素点也放进去,用来连接边缘

%% 双阈值检测
% 设置阈值,先让两个阈值相等确定上下界限
threshold_low = 0.1;    % 排除假边上的点,影响边的连续性
threshold_high = 0.18;   % 确定真边上的点,影响边的数量

temp(2:M+1, 2:N+1) = Edge; 
Edge = temp;
TrueEdge = zeros(M+2, N+2);
domain = ones(3, 3);
isEdge = ordfilt2(Edge, 6, ones(3, 3));  % 获得像素点八邻域内的最大值

for x = 2 : M+1
    for y = 2 : N+1
        % 高于阈值threshold_high可认为是边
        if Edge(x, y) >= threshold_high
            TrueEdge(x, y) = 1;
        % 低于阈值threshold_low一定不是边
        elseif Edge(x, y) <= threshold_low
            TrueEdge(x, y) = 0;
        % 在双阈值之间,若像素周围含边则也当作边
        else
             TrueEdge(x, y) = isEdge(x, y) > threshold_high;
        end
    end
end

TrueEdge = TrueEdge(2:M+1, 2:N+1);
figure, imshow(TrueEdge)
title('双阈值检测后')
set(gcf, 'color', [1 1 1])

通过双阈值检测,我们得到的边缘就细了(不足:未进行进一步的细化、边不是很顺畅):
小白学习图像处理6——Canny边缘检测算法原理_第6张图片


五、matlab 的边缘检测函数

matlab的edge函数是一个现成的边缘提取函数,调用时可以自己选择算子的类型

BW = edge(I,method,threshold,sigma)

可以参考小白学习图像处理——图像的边缘检测(matlab实现)
代码

img = imread('baby_hand.jpg');
gray_img = rgb2gray(img);
sigma = 1;
threshold1 = 0.15;
threshold2 = 0.25;
img1 =edge(gray_img,'canny', threshold1, threshold2, sigma);
figure, imshow(img1), title('sobel')

效果如下
小白学习图像处理6——Canny边缘检测算法原理_第7张图片


六、总结

  以上就是使用canny边缘检测器全部内容了,实现步骤包含:

步骤 目的
梯度计算 得到边缘及其方向
抑制非极大值 细化边缘
双阈值检测 消除伪边缘,连接边缘

完结

你可能感兴趣的:(数字图像处理)