在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算法的四个步骤,特别是**非极大值抑制**对边缘的"细化"作用,以及**双阈值**在平衡噪声和边缘连续性方面的重要性。