MATLAB中实现Canny边缘检测

在MATLAB中实现Canny边缘检测,既可以直接使用内置函数,也可以自己编写代码分步实现以加深理解。

Canny边缘检测简介

Canny边缘检测旨在找出图像中灰度变化剧烈的地方,这些地方通常对应物体的边缘。其优势在于低错误率良好的定位能力以及对单一边缘点的唯一边缘响应。算法主要包含四个步骤:

  1. 高斯滤波:平滑图像以去除噪声。
  2. 计算梯度幅值和方向:找出图像灰度变化最强的位置和方向。
  3. 非极大值抑制:"细化"边缘,只保留梯度幅值局部最大的点。
  4. 双阈值检测与边缘连接:利用高低阈值筛选并连接真正的边缘。

实现方法

使用内置函数 edge

最快捷的方法是使用MATLAB的 edge 函数,其基本语法如下:

BW = edge(I, 'canny')
BW = edge(I, 'canny', thresh)
BW = edge(I, 'canny', thresh, sigma)
  • I 是输入的灰度图像。如果是彩色图像,需要先转换为灰度图(使用 rgb2gray)。
  • 'canny' 指定使用Canny方法。
  • thresh 是阈值,用于决定边缘的灵敏度。它可以是一个双元素向量 [low_thresh, high_thresh],也可以是一个标量(此时高阈值自动设为标量乘以2.5)。不指定时,MATLAB会自动计算。
  • sigma 是高斯滤波器的标准差,控制平滑程度。默认值为 sqrt(2)

代码

% 读取图像并转换为灰度图
I = imread('your_image.jpg');
if size(I, 3) == 3
    I_gray = rgb2gray(I);
else
    I_gray = I;
end

% 使用Canny算子进行边缘检测
BW_canny = edge(I_gray, 'canny');

% 显示原图、灰度图和边缘检测结果
subplot(1, 3, 1), imshow(I), title('原始图像');
subplot(1, 3, 2), imshow(I_gray), title('灰度图像');
subplot(1, 3, 3), imshow(BW_canny), title('Canny边缘检测');

自己编写Canny算法

如果你想深入了解Canny算法的细节,可以参考以下步骤手动实现。这里提供一个基于的简化框架,实际应用中可能需要调整参数和细化连接逻辑。

function [bin] = my_canny(src, lowTh)
    % 转换为灰度图
    if size(src, 3) > 1
        src = rgb2gray(src);
    end
    src = double(src);
    
    [Ay, Ax] = size(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);      % 最终边缘
    
    % 步骤1: 高斯滤波平滑
    sigma = 1.0; % 可调整
    gausFilter = fspecial('gaussian', [3,3], sigma);
    img_smooth = imfilter(src, gausFilter, 'replicate');
    
    % 步骤2: 计算梯度幅值和方向 (使用简单差分)
    for y = 1:(Ay-1)
        for x = 1:(Ax-1)
            % 简单的2x2差分计算x和y方向梯度
            gx =  img_smooth(y, x) + img_smooth(y+1, x) - img_smooth(y, x+1) - img_smooth(y+1, x+1);
            gy = -img_smooth(y, x) + img_smooth(y+1, x) - img_smooth(y, x+1) + img_smooth(y+1, x+1);
            m(y, x) = sqrt(gx^2 + gy^2); % 梯度幅值
            % 计算梯度方向 (角度)
            theta(y, x) = atan2(gy, gx) * 180 / pi; % 转换为度
        end
    end
    
    % 步骤3: 非极大值抑制
    % 将梯度方向划分为四个区域: 0°(水平), 45°, 90°(垂直), 135°
    sector = zeros(Ay, Ax);
    angle = theta;
    % 调整角度到0-180度范围并划分区域
    angle(angle < 0) = angle(angle < 0) + 180;
    sector(angle <= 22.5 | angle > 157.5) = 0;   % 水平方向
    sector(angle > 22.5 & angle <= 67.5) = 1;    % 45°
    sector(angle > 67.5 & angle <= 112.5) = 2;   % 垂直
    sector(angle > 112.5 & angle <= 157.5) = 3;  % 135°
    
    % 检查每个像素在梯度方向上的邻近像素
    for y = 2:(Ay-1)
        for x = 2:(Ax-1)
            switch sector(y, x)
                case 0 % 水平方向,比较左右像素
                    neighbor1 = m(y, x-1);
                    neighbor2 = m(y, x+1);
                case 1 % 45°方向,比较右上左下像素
                    neighbor1 = m(y-1, x+1);
                    neighbor2 = m(y+1, x-1);
                case 2 % 垂直方向,比较上下像素
                    neighbor1 = m(y-1, x);
                    neighbor2 = m(y+1, x);
                case 3 % 135°方向,比较左上右下像素
                    neighbor1 = m(y-1, x-1);
                    neighbor2 = m(y+1, x+1);
            end
            % 如果当前像素的梯度幅值不小于邻近两个像素,则保留
            if m(y, x) >= neighbor1 && m(y, x) >= neighbor2
                canny1(y, x) = m(y, x);
            else
                canny1(y, x) = 0;
            end
        end
    end
    
    % 步骤4: 双阈值检测和边缘连接
    highTh = lowTh * 2.5; % 通常高阈值是低阈值的2-3倍
    % 初始化强弱边缘
    strong_edges = canny1 > highTh;
    weak_edges = (canny1 >= lowTh) & (canny1 <= highTh);
    
    % 简单的边缘连接:弱边缘只有在与强边缘相邻时才保留
    [Ly, Lx] = bwlabel(weak_edges, 8);
    bin = strong_edges; % 强边缘直接保留
    % 遍历每个弱边缘连通区域,如果与强边缘相连,则保留
    for i = 1:max(Ly(:))
        [r, c] = find(Ly == i);
        if any(strong_edges(sub2ind([Ay, Ax], r, c))) % 检查该区域是否包含强边缘
            bin(sub2ind([Ay, Ax], r, c)) = 1;
        end
    end
    
    bin = im2bw(bin); % 转换为二值图像
end

调用自定义函数:

% 设置低阈值 (例如0.05到0.2之间,需要根据图像调整)
lowTh = 0.1;
% 调用自定义Canny函数
my_edges = my_canny(I, lowTh);
imshow(my_edges);
title('自定义Canny边缘检测');

参数调整建议

  • 阈值 (thresh):这是影响结果最关键的参数。
    • 高阈值:主要确定明显的强边缘。调得太高可能丢失真实边缘,太低则可能引入噪声或不相关结构。
    • 低阈值:控制弱边缘的连接。调得太高可能导致边缘断裂,太低则可能连接过多噪声或使边缘过粗。
    • 若不确定,可先让MATLAB自动计算 (edge(I, 'canny')),再微调。
  • 高斯滤波器标准差 (sigma)
    • sigma 较大时,高斯滤波器更"宽",平滑效果更强,有助于抑制噪声,但也可能模糊边缘,导致定位略有偏差。
    • sigma 较小时,平滑效果弱,能保留更精细的边缘,但对噪声更敏感。
    • 通常从 12 开始尝试。

Canny与其他边缘检测算子对比

MATLAB的 edge 函数还提供了其他边缘检测方法,以下是一个简单对比:

算子 优点 缺点
Canny 低错误率,定位准确,能检测真实弱边缘 计算相对复杂
Sobel/Prewitt 计算简单,有一定抗噪能力 边缘可能较粗,定位不如Canny精确
Roberts 边缘定位精度较高 对噪声敏感,未做平滑处理
LOG 抗噪声能力较强 可能丢失部分尖锐边缘

参考代码 matlab中实现canny边缘检测算法 www.youwenfan.com/contentcnl/78285.html

总结

在MATLAB中进行Canny边缘检测:

  1. 快速上手:直接使用 edge(I, 'canny') 函数。
  2. 深入理解与控制:可以尝试自己编写代码实现,或通过调整 edge 函数的 threshsigma 参数优化结果。
  3. 关键点:理解Canny算法的四个步骤,特别是非极大值抑制对边缘的"细化"作用,以及双阈值在平衡噪声和边缘连续性方面的重要性。
posted @ 2025-11-13 18:13  chen_yig  阅读(48)  评论(0)    收藏  举报