一、算法原理与流程
Canny边缘检测算法由John F. Canny于1986年提出,其核心流程包含5个关键步骤:
- 高斯滤波:消除噪声干扰(使用高斯核卷积)
- 梯度计算:通过Sobel算子计算梯度幅值和方向
- 非极大值抑制:细化边缘宽度(保留局部梯度最大值)
- 双阈值检测:区分强/弱边缘(通过高低阈值比例)
- 边缘连接:滞后跟踪弱边缘(连接强边缘邻域的弱边缘)
二、代码
1. 基础实现(手动版)
matlab
function edge_img = my_canny(I, sigma, low_thresh_ratio, high_thresh_ratio)
% 输入参数:
% I: 输入灰度图像
% sigma: 高斯滤波标准差
% low_thresh_ratio: 低阈值比例(相对于高阈值)
% high_thresh_ratio: 高阈值比例(相对于梯度最大值)
% 1. 高斯滤波
[m,n] = size(I);
k = 2*ceil(3*sigma)+1; % 核大小(奇数)
h = fspecial('gaussian', [k k], sigma);
blurred = imfilter(I, h, 'same', 'replicate');
% 2. 计算梯度
[Gx, Gy] = imgradientxy(blurred, 'sobel');
[G, theta] = imgradient(Gx, Gy);
% 3. 非极大值抑制
nms = non_maximum_suppression(G, theta);
% 4. 双阈值检测
high_thresh = max(G(:)) * high_thresh_ratio;
low_thresh = high_thresh * low_thresh_ratio;
strong_edges = nms > high_thresh;
weak_edges = (nms >= low_thresh) & (nms <= high_thresh);
% 5. 边缘连接
edge_img = edge_linking(strong_edges, weak_edges);
end
% 非极大值抑制函数
function nms = non_maximum_suppression(G, theta)
[m,n] = size(G);
nms = zeros(m,n);
theta = theta * 180/pi; % 转换为角度
theta(theta < 0) = theta + 180; % 角度归一化
for i = 2:m-1
for j = 2:n-1
% 根据梯度方向选择比较方向
if (0 <= theta(i,j) && theta(i,j) < 22.5) || (157.5 <= theta(i,j) && theta(i,j) < 180)
neighbor = [G(i,j-1), G(i,j+1)];
elseif (22.5 <= theta(i,j) && theta(i,j) < 67.5)
neighbor = [G(i-1,j+1), G(i+1,j-1)];
elseif (67.5 <= theta(i,j) && theta(i,j) < 112.5)
neighbor = [G(i-1,j), G(i+1,j)];
else
neighbor = [G(i-1,j-1), G(i+1,j+1)];
end
% 保留局部最大值
if G(i,j) >= max(neighbor)
nms(i,j) = G(i,j);
end
end
end
end
% 边缘连接函数
function final_edges = edge_linking(strong, weak)
[m,n] = size(strong);
final_edges = false(m,n);
strong_idx = find(strong);
% 使用8邻域连接弱边缘
for k = 1:length(strong_idx)
[i,j] = ind2sub([m,n], strong_idx(k));
if any(any(weak(i-1:i+1, j-1:j+1)))
final_edges(i,j) = true;
end
end
end
2. 内置函数实现(推荐)
matlab
% 直接调用edge函数
I = imread('lena.jpg');
gray = rgb2gray(I);
edges = edge(gray, 'Canny', [0.1 0.2], 2); % 自动计算高低阈值
imshow(edges);
三、参数优化
| 参数 | 影响范围 | 推荐范围 | 调整建议 |
|---|---|---|---|
sigma |
噪声抑制效果 | 1-3 | 噪声明显时增大(如σ=2) |
| 高低阈值比 | 边缘保留数量 | 0.1-0.3 | 通过直方图分析确定 |
| 滤波核大小 | 边缘平滑程度 | 3×3至7×7 | 核越大边缘越平滑 |
| 梯度算子 | 边缘方向灵敏度 | Sobel/Prewitt | 复杂纹理建议使用Prewitt |
四、工程应用案例
1. 医学图像血管检测
matlab
% 加载血管造影图像
img = imread('vessel.png');
gray = rgb2gray(img);
% 自适应Canny参数
sigma = graythresh(gray)*2; % 基于Otsu方法自动选择
edges = edge(gray, 'Canny', [0.05 0.15], sigma);
% 形态学后处理
se = strel('disk',2);
clean_edges = bwareaopen(edges, 50);
clean_edges = imclose(clean_edges, se);
imshow(clean_edges);
2. 工业检测(裂纹识别)
matlab
% 读取金属表面图像
img = imread('metal_surface.jpg');
gray = rgb2gray(img);
% 多尺度Canny检测
edges1 = edge(gray, 'Canny', [0.08 0.12], 1);
edges2 = edge(gray, 'Canny', [0.15 0.25], 2);
% 结果融合
final_edges = imfuse(edges1, edges2, 'blend');
imshow(final_edges);
五、常见问题解决方案
- 噪声干扰严重: 增加高斯滤波核大小(如σ=3) 先进行中值滤波预处理
- 边缘断裂 : 调整低阈值比例(建议0.1-0.2) 增加形态学闭操作(
imclose) - 计算效率低: 使用积分图像加速梯度计算 对ROI区域进行检测
六、参考
- MathWorks官方文档: Canny Edge Detection ww2.mathworks.cn/help/images/ref/edge.html
- 代码 matlab中实现canny边缘检测算法 www.3dddown.com/csa/78285.html
- 《数字图像处理》冈萨雷斯著
- Canny原始论文: A Computational Approach to Edge Detection(1986)