MATLAB中实现Canny边缘检测

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

### Canny边缘检测简介

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

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

### 实现方法

#### 使用内置函数 `edge`

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

“`matlab
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)`。

**代码**:

“`matlab
% 读取图像并转换为灰度图
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算法的细节,可以参考以下步骤手动实现。这里提供一个基于的简化框架,实际应用中可能需要调整参数和细化连接逻辑。

“`matlab
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)
% 简单的2×2差分计算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 ``` 调用自定义函数: ```matlab % 设置低阈值 (例如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/contentale/78285.html ### 总结 在MATLAB中进行Canny边缘检测: 1. **快速上手**:直接使用 `edge(I, 'canny')` 函数。 2. **深入理解与控制**:可以尝试自己编写代码实现,或通过调整 `edge` 函数的 `thresh` 和 `sigma` 参数优化结果。 3. **关键点**:理解Canny算法的四个步骤,特别是**非极大值抑制**对边缘的"细化"作用,以及**双阈值**在平衡噪声和边缘连续性方面的重要性。

专注于matlab/simulink,电子电路,编程