图像的边缘检测-三种方法

图像的边缘检测:
比较拉普拉斯算子,LOG算子,Canny算子三种边缘检测算法。
编程思路:
图像边缘就是图像灰度值突变的地方,也就是图像在该部分的像素值变化速度非常之快,就比如在坐标轴上一条曲线有刚开始的平滑突然来个大转弯,在变化出的导数非常大。
LoG算子,Canny算子属于二阶差分算子
Laplace算子是各项同性的,即具有旋转不变性。
二阶拉普拉斯方程:
∇^2 f(x,y)=(∂^2 f)/(∂^2 x)+(∂^2 f)/∂y
写成二维离散形式:
∇^2 f(x,y)=f(x+1,y)+f(x-1,y)+f(x,y+1)+f(x,y-1)-4f(x,y)
拉普拉斯的卷积模板就是下图,这里我们使用第一个。
图像的边缘检测-三种方法_第1张图片
1980年,Marr和Hildreth提出将Laplace算子与高斯低通滤波相结合,提出了LOG(Laplace and Guassian)算子。
步骤如下:

  1. 对图像先进性高斯滤波(G × f),再进行Laplace算子运算Δ(G × f);
    高斯算子
    图像的边缘检测-三种方法_第2张图片
    2.保留一阶导数峰值的位置,从中寻找Laplace过零点
    3.对过零点的精确位置进行插值估计。
    Canny边缘检测算法步骤:
    步骤1:用高斯滤波器平滑处理原图像;
    步骤2:用一阶偏导的有限差分进行计算梯度的幅值和方向;
    步骤3:对梯度幅值进行非极大值抑制;
    步骤4:用双阈值算法检测和连接边缘。

源代码:

%% 三种边缘检测比较
clc;
clear;
close all;
img = imread('tom.png'); %读取图像
img = rgb2gray(img);
%img=imnoise(img,"salt & pepper"); %添加椒盐噪声
[h w]=size(img);
Lapu=zeros(h,w);
t = 128;
for row = 2 : h-1  %实现拉普拉斯
    for col = 2 : w-1
        Lapu(row,col)=abs(4*img(row,col) - img(row-1,col) -img(row,col-1) - img(row,col+1) - img(row+1,col+1));
    end
end
LOG = edge(img,'log');  %实现LOG
LOG1 = myLog(img,0.1);
% Canny算子
CANNY= edge( img, 'canny'); %使用Canny算子进行边缘检测,得到二值边界图像
CANNY1 = myCanny(img,0.1);
figure();
subplot(231),imshow(img),title("原图");
subplot(232),imshow(Lapu),title("拉普拉斯算子后的图");
subplot(233),imshow(LOG),title("LOG算子后的图");
subplot(234),imshow(LOG1),title("手写LOG算子后的图");
subplot(235),imshow(CANNY),title("canny算子后的图");
subplot(236),imshow(CANNY1),title("手写canny算子后的图");

myLog.m
function [BW] = myLog(I,th)

grayI = I;%判断是否为灰度图,不是则转为
gauss = [1 2 1; 2 4 2;1 2 1] / 16;  % Gauss平滑模板
grayI = conv2(grayI, gauss, 'same');   % 平滑
[m,n]=size(grayI);
newI=grayI;
LogNum=0;
for j=2:m-1
    for k=2:n-1
        LogNum = abs(4*grayI(j,k)-grayI(j-1,k)-grayI(j+1,k)-grayI(j,k+1)-grayI(j,k-1));
        if(LogNum > th*255)
            newI(j,k)=255;
        else
            newI(j,k)=0;
        end
    end
end
BW = im2bw(newI);

myCanny.m
function I1= myCanny(I,k)
img0 = double(I);
gauss = [1 2 1; 2 4 2;1 2 1] / 16;  % Gauss平滑模板
sobelx = [-1 0 1; -2 0 2; -1 0 1];  % Sobel水平边缘模板
sobely = sobelx';                   % Sobel垂直边缘模板

img = conv2(img0, gauss, 'same');   % 平滑
gradx = conv2(img, sobelx, 'same'); % 水平边缘卷积
grady = conv2(img, sobely, 'same'); % 垂直边缘卷积

% M = sqrt(gradx .^ 2 + grady .^ 2);  % 边缘高度
M = abs(gradx)+ abs(grady);  % 边缘高度
alpha = atan(grady ./ gradx);       % 边缘方向

N = zeros(size(M));                 % 非最大抑制图像
for i = 2: length(M(:, 1)) - 1
    for j = 2: length(M(1, :)) - 1
        dirc = alpha(i, j);         % 四个基本方向判断并进行非最大抑制,比如矩阵是
                                    % [1 2 3;4 5 6;7 8 9],边缘延[4 5 6]方向,那
                                    % 么我们关心的是元素2和元素8与元素5的大小关系
        if abs(dirc) <= pi / 8
            if M(i, j) == max([(M(i, j - 1)), M(i, j), M(i, j + 1)])%竖直梯度,水平边缘
                N(i, j) = M(i, j);
            end
        elseif abs(dirc) >= 3 * pi / 8
            if M(i, j) == max([(M(i - 1, j)), M(i, j), M(i + 1, j)])%水平梯度,竖直边缘
                N(i, j) = M(i, j);
            end
        elseif dirc > pi / 8 && dirc < 3 * pi / 8
            if M(i, j) == max([(M(i - 1, j - 1)), M(i, j), M(i + 1, j + 1)])
                N(i, j) = M(i, j);
            end
        elseif dirc > - 3 * pi / 8 && dirc < - pi / 8
            if M(i, j) == max([(M(i + 1, j - 1)), M(i, j), M(i - 1, j + 1)])
                N(i, j) = M(i, j);
            end
        end
    end
end

TH = 0.05* max(max(N));              % 高阈值
TL = 0.025 * max(max(N));              % 低阈值
THedge = N; 
TLedge = N;

THedge(THedge < TH) = 0;             % 强边缘
TLedge(TLedge < TL) = 0;             % 弱边缘

THedge = padarray(THedge, [1, 1], 0, 'both');   % 进行边扩展,防止遍历时出错
TLedge = padarray(TLedge, [1, 1], 0, 'both');
TLedge0 = TLedge;

isvis = ones(size(THedge));          % 是否遍历过某像素,是为0,不是为1(便于计算)

while(sum(sum(THedge)))
    [x, y] = find(THedge ~= 0, 1);   % 寻找8邻域内非0像素,作为下一步搜索的起点
    THedge = THedge .* isvis;        % 搜索过的点标记为0
    [TLedge0, isvis] = traverse(TLedge0, x, y, isvis);      % 递归遍历,最终剩下的是未遍历的元素,即孤立点或非目标边缘
end

TLedge = TLedge - TLedge0;           % 作差求出Canny边缘
THedge(:, end) = []; THedge(end, :) = []; THedge(1, :) = []; THedge(:, 1) = []; % 删去扩展的边缘
TLedge(:, end) = []; TLedge(end, :) = []; TLedge(1, :) = []; TLedge(:, 1) = [];

pmin=min(min(TLedge));pmax=max(max(TLedge));
I1=uint8((double(TLedge)-pmin)/(pmax-pmin)*255);
I1=im2bw(I1,k);

Traverse.m
function [output, isvis] = traverse(mat, i, j, isvis)

mat(i, j) = 0;
isvis(i, j) = 0;
neighbor = mat(i - 1: i + 1, j - 1: j + 1);

while(sum(sum(neighbor)))
    [x, y] = find(neighbor ~= 0, 1);
    neighbor(x, y) = 0;
    mat(i - 2 + x, j - 2 + y) = 0;
    [mat, isvis] = traverse(mat, i - 2 + x, j - 2 + y, isvis);
end
output = mat;

结果:
Log和Canny两个k都为0.1的图

图像的边缘检测-三种方法_第3张图片

Log和Canny两个k都为0.2的图
图像的边缘检测-三种方法_第4张图片

添加了椒盐噪声0.1
图像的边缘检测-三种方法_第5张图片

添加了椒盐噪声0.02系数
图像的边缘检测-三种方法_第6张图片

分析:
从以上的检测结果中可以看出:
没有写阈值判断的拉普拉斯,效果不好,因为首先有一部分没有显示出来,头发那里的细节太多了,将不需要的结果展现了出来。
手写:拉普拉斯算子特点:在有噪声的时候,log算子双倍加强了噪声的影响,去噪功能不是太好。当给Log算子给的阈值为0.1的时候效果还行,当大一点的时候效果就不明显了,不能有效地提取边缘。这里我手写的log算子效果在有噪声的时候和没噪声的时候都没有自带的Log算子效果好。
Canny算子而言,我感觉手写的效果比系统带的效果更好,其次我感觉去噪效果都不好。
LOG算子容易受尺度的影响,不同尺度下的边缘点要用不同尺度的LOG算子检测,Canny 算子受尺度的影响不太明显,不同尺度下,边缘点的位置都有偏差,但几乎相同; LOG算子对噪声的抑制能力随着尺度的增加而增加,相同尺度下的Canny算子比LOG算子的抗噪声能力强,而LOG算子比Canny算子的边缘点准确;在尺度选择合适的情况下,LOG算子对图像边缘点检测的位置非常准确,能够保留边缘点比较细致的组织结构,而Canny算子对图像边缘检出率比较高,包括纹理区域,以及对比度很弱的边缘点,但是对这些边缘点的组织结构刻画得不是特别细致,边缘点的位置有小范围的偏差。

你可能感兴趣的:(数字图像处理,计算机视觉,图像处理)