MATLAB中实现Canny边缘检测
在MATLAB中实现Canny边缘检测,既可以直接使用内置函数,也可以自己编写代码分步实现以加深理解。
Canny边缘检测简介
Canny边缘检测旨在找出图像中灰度变化剧烈的地方,这些地方通常对应物体的边缘。其优势在于低错误率、良好的定位能力以及对单一边缘点的唯一边缘响应。算法主要包含四个步骤:
- 高斯滤波:平滑图像以去除噪声。
- 计算梯度幅值和方向:找出图像灰度变化最强的位置和方向。
- 非极大值抑制:"细化"边缘,只保留梯度幅值局部最大的点。
- 双阈值检测与边缘连接:利用高低阈值筛选并连接真正的边缘。
实现方法
使用内置函数 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较小时,平滑效果弱,能保留更精细的边缘,但对噪声更敏感。- 通常从
1到2开始尝试。
Canny与其他边缘检测算子对比
MATLAB的 edge 函数还提供了其他边缘检测方法,以下是一个简单对比:
| 算子 | 优点 | 缺点 |
|---|---|---|
| Canny | 低错误率,定位准确,能检测真实弱边缘 | 计算相对复杂 |
| Sobel/Prewitt | 计算简单,有一定抗噪能力 | 边缘可能较粗,定位不如Canny精确 |
| Roberts | 边缘定位精度较高 | 对噪声敏感,未做平滑处理 |
| LOG | 抗噪声能力较强 | 可能丢失部分尖锐边缘 |
参考代码 matlab中实现canny边缘检测算法 www.youwenfan.com/contentcnl/78285.html
总结
在MATLAB中进行Canny边缘检测:
- 快速上手:直接使用
edge(I, 'canny')函数。 - 深入理解与控制:可以尝试自己编写代码实现,或通过调整
edge函数的thresh和sigma参数优化结果。 - 关键点:理解Canny算法的四个步骤,特别是非极大值抑制对边缘的"细化"作用,以及双阈值在平衡噪声和边缘连续性方面的重要性。
浙公网安备 33010602011771号