0.前言
这篇文章就慢慢一边做实验一边写基本的形态学处理的内容,看着阮老师翻译的书直接用MATLAB靠自己的记忆写程序。慢慢更这篇,一边理解一边更,包括边界提取、孔洞填充(重点,因为我要考的课有道题就考这个)、骨架、连通分量、细化、裁剪等等。
1.边界提取
表示为
β
(
A
)
\beta(A)
β(A) 的集合
A
A
A 的边界可以先用
B
B
B 对
A
A
A 服饰,而后做
A
A
A 和 腐蚀结果的之间的集合之差得到,即
β
(
A
)
=
A
−
(
A
⊖
B
)
\beta(A) = A - (A \ominus B)
β(A)=A−(A⊖B)
我在思考边界提取和边缘提取到底有什么区别,就写了写对比了下,思考了下。
边界:专门指外部的轮廓
边缘:在数字图像处理中专门指灰度突变的地方
然后写了个程序对比了下,发现其实确实形态学处理起来还真有点带感。
程序的思路是读入彩色图像,然后将彩色图转灰度图之后利用 edge 提取边缘,以及提取3个通道的分量去提取边缘,然后进行对比。
而边界提取可以直接在彩色空间中操作,所以我用上面的公式,直接得到彩色的边界,然后在彩色边界中提取R、G、B分量,之后进行显示对比。其中结构元是 5*5 的 strucure
先看下面的结果吧
以前手撸过canny边缘的算法,这次用了系统自带的 edge 函数,二阶边缘,速度快、识别效率高,但是MATLAB中 edge 函数需要转换到灰度空间去做,并且最后一步连通的时候出来结果是 二值图 ,采用 rgb2gray 函数不免会有灰度损失,而直接直接提取各个分量容易造成在浮点型转化的时候的精度损失。左边的图是边缘提取的结果,是黑白二值图 ,而右边的是利用上面公式提取出来的 彩色边界 ,看到轮廓分明,神清气爽。
再看下面的另一个结果吧
诚如我在之前说的,利用 edge 函数需要在灰度图像进行,所以我提取R、G、B空间进行边缘提取,但是由于提取出来的灰度图每个灰度值不一样,所以容易导致了可能灰度突变的地方不一样,所以显示出的 边缘 有些不一样,而利用传统的边界提取方法的边界提取的绝对一样,仅仅只是在灰度显示有差别罢了,况且人眼还识别不出来。
1.1总结
边缘和边界的提取有差别,并且有不同的方法,在边界提取的时候结构元的大小选择也有考究,需要再进一步思考吧!
1.2代码
%Coding = UTF8
%By neverland!
%Nov 19th
%version1.0
%读入图像
im = imread('test.jpg');
%操作
gray_im = rgb2gray(im); %灰度图
r = im(:,:,1); %红
g = im(:,:,2); %绿
b = im(:,:,3); %蓝
r_edge = edge(r,'canny'); %红边缘
g_edge = edge(g,'canny'); %绿边缘
b_edge = edge(b,'canny'); %蓝边缘
%显示图像
subplot(2,3,1);imshow(r_edge); title('红通道canny边缘','fontsize',20);
subplot(2,3,2);imshow(g_edge); title('绿通道canny边缘','fontsize',20);
subplot(2,3,3);imshow(b_edge); title('蓝通道canny边缘','fontsize',20);
%利用边界提取的方法提取
str_ = ones(5,5); %结构元
combined_edge = im - imerode(im,str_); %边界提取
%显示图像
subplot(2,3,4);
r_edge2 = combined_edge(:,:,1);
imshow(r_edge2);title('第二种方法提红边界','fontsize',20);
subplot(2,3,5);
g_edge2 = combined_edge(:,:,2);
imshow(g_edge2);title('第二种方法提绿边界','fontsize',20);
b_edge2 = combined_edge(:,:,3);
subplot(2,3,6);
imshow(b_edge2);title('第二种方法提蓝边界','fontsize',20);
%开启另一个窗口对比
figure
gray_edge = edge(gray_im,'canny');
subplot(1,2,1);
imshow(gray_edge);
title('灰度图像提取边缘','fontsize',20);
subplot(1,2,2);
imshow(combined_edge);
title('利用形态学处理提取的边界','fontsize',20);
2.孔洞填充
孔洞是和背景相互连接的边界所包围的一个背景区域,如下面图所示,红色部分框起来的部分就是孔洞,在《数字图像处理第三版》中,处理孔洞一共有3种方法,但是在各种paper里也提出了其他的方法,其实不外乎就是基本的形态学处理、种子填充、扫描线 ,由于书上给的方法很难实现,实验的时候直接用了一个 imfill 的函数,感觉好菜,不过确实如果要知道孔洞的一个起始点,这样好费劲,还不自动。
2.1 形态学的方法
X
k
=
(
X
K
−
1
⊕
B
)
h
∪
A
C
h
h
k
=
1
,
2
,
3
,
⋯
X_k = (X_{K-1} \oplus B ) \phantom{h} \cup A^C \phantom{hh} k=1,2,3,\cdots
Xk=(XK−1⊕B)h∪AChhk=1,2,3,⋯ 看了看图,实际是就是用了一个结构元从孔洞的一个任意内部开始膨胀,然后再和原图像的补集求交集,因为原图像孔洞部分(挖去的一部分)的值为0,而求完补集之后就变成有值的部分了,那么和做完膨胀的图像求交集就填充了一部分孔洞,然后多做几次,直到不再变化为止。
这种方法费时费力,需要自己先定义一个起始点,然后进行膨胀,然后和补集合求交集,最后再和原来图像求并集,才能得到想要的图。
有一个结构元是十字架,然后比如已经知道了
X
0
X_0
X0 即一个孔洞的起点,用十字架去膨胀,膨胀之后和补集求交就补了一部分,持续下去,直到你认为可以为止,matlab直接使用imfill函数即可,当然如果是手撕算法的话,
就是这样吧。。。把。。。。
%MATLAB内置的函数填充
im = imread('kongdong.tif');
im_ = im2bw(im);
true_im = imfill(im_,'holes');
subplot(1,2,1);
imshow(im);
subplot(1,2,2);
imshow(true_im);
%手撕算法
A = imread('kongdong.tif');
A = im2bw(A); %二值化
A_hat = ~A; %A的补集
[m,n] = size(A);
X = zeros(m,n);
[x,y] = find(A==1); %任意选取A中灰度值为1的点的位置
X(x(1),y(1)) = 1; %选取第一个灰度值为1的点为初始点
se = [0,1,0;1,1,1;0,1,0]; %结构元
flag = 0; % 迭代标志
i = 0; %迭代次数
while(~flag)
i = i + 1;
Xp = X;
X = imdilate(X,se) & A_hat; %膨胀后与补集求交集
if X == Xp
flag = 1;
end
end
A_ = ~X | A; %求并集
figure(1);
subplot(1,2,1);
imshow(A);
title('二值化后的图','fontsize',30);
subplot(1,2,2);
imshow(A_);
title('填充结果','fontsize',30);
比imfill好像厉害点。。
https://blog.csdn.net/LYduring/article/details/80443709
这个手撕算法的话引用的是这篇blog里的。