bwlabel的实现方法我这里列举三种(递归、一步法、两步法),其中递归方法和一步法很类似,只不过一步法中用了一个队列实现递归。
主要思路:
递归:从上至下,从左至右,当搜索到第一个白色点并且其对应的output值没有被标签,然后开始从这个点搜索,在其八邻域内搜索,如果它满足是白色点并且其对应的output值没有被标签这个条件,就将这个点的八邻域继续搜索,这个地方可以使用递归实现.
但是递归方法没有一步法好,因为递归有着次数限制,当图像不大的时候没有问题,图像很大的时候会栈溢出,报错,此时可以运用一步法,用队列这种数据结构。
一步法:思路和递归一样,从上至下,从左至右,当搜索到第一个白色点并且其对应的output值没有被标签,然后开始从这个点搜索,在其八邻域内搜索,如果它满足是白色点并且其对应的output值没有被标签这个条件,就将这个点添加到待搜索的队列里面,队列移动,直到队列里面没有元素,表示搜索完毕
两步法:应该是这些方法里面最快的方法了,顾名思义Two-pass第一次for循环将图1中4个周围点label先打上,并使用一个数组记录他们的父节点,这个打label的规则如下:
1. 4个点全是0 处理方法:label+1,并赋值
2. (至多4个点)全是一样的label 将周围的label赋值给这个点即可
3. (至多4个点)有两种label label赋值为label小的那个,并将这两个label放到同一个数组里面去
4. (至多4个点)有三种label,并使最小的值为另外两个值的父节点
第二次for循环,将每个label更新,首先更新为父节点,但是父节点并不一定是连续的,所以需要将不连续的父节点label变成连续的label。
图1
一步法及两步法相关资料网址:
https://blog.csdn.net/u012526003/article/details/50970850
http://www.cnblogs.com/tiandsp/archive/2012/12/06/2805276.html
https://www.cnblogs.com/evilKing/p/6001654.html
http://www.cnblogs.com/tiandsp/archive/2012/12/06/2804922.html
递归方法代码:
clc;
clear;
%彩色图像转化成灰度再转化成二值图像
global K;
global bw;
K = imread('rice.png');
K = im2bw(K);
imshow(K)
global r;
global c;
[r ,c ] = size(K);
bw = zeros(r,c);
%global count;
count = 1;
global s;
s = 1;%变量s用来控制count,使得一次递归只加一次
%连通
for i = 1:r
for j = 1:c
liantong(i,j,count);
if s == 0;
count = count +1;
s = 1;
end
end
end
count = count - 1;
figure
imshow(bw)
递归函数文件(liantong.m)
function liantong(i,j,count)
global K;
global bw;
global r;
global c;
global s;
if(K(i,j) == 1 && bw(i,j) == 0 )
s = 0;
bw(i,j) = count;
if(i>1 )
liantong(i-1,j,count);
if( j > 1)
liantong(i-1,j-1,count);
end
end
if(i>1 && j<c)
liantong(i-1,j+1,count);
end
if(j>1)
liantong(i,j-1,count);
end
if(j<c)
liantong(i,j+1,count);
end
if(i<r)
liantong(i+1,j,count);
if(j>1)
liantong(i+1,j-1,count);
end
end
if(i<r && j<c)
liantong(i+1,j+1,count);
end
end
end
一步法:
K = imread('rice.png');
tic
K = im2bw(K);
%在最外圈加上一圈
[r,c] = size(K);
K1 = zeros(r+2,c+2);
for i = 2:r+1
for j = 2:c+1
K1(i,j) = K(i-1,j-1);
end
end
output = zeros(r,c);
label = 1;
neighbour = [-1,-1;-1,0;-1,1;0,-1;0,1;1,-1;1,0;1,1];%8个 邻域
head = 1;
tail = 1;
%output相对于K1来说 向左上平移了一个单元
for i = 2:r+1
for j = 2:c+1
if (K1(i,j) == 1 && output(i-1,j-1) == 0)%种子点
s{tail} = [i,j];
tail = tail + 1;
while (head ~= tail)
for l = 1:8
q = s{head} + neighbour(l,:);%头指针为K(i,j)这个点,在for循环里面不能改变,而尾指针需要增加
if q(1) > 1 && q(1) < r+2 && q(2) > 1 && q(2) < c+2
if K1(q(1),q(2)) == 1 && output(q(1)-1,q(2)-1) == 0 %满足点条件
output(q(1)-1,q(2)-1) = label;
s{tail} = q ;%加入队列
tail = tail + 1;
end
end
end
head = head + 1;
end
%
clear s;
label = label + 1;%最后一次label+1,但是并没有使用
head = 1;
tail =1;%恢复原状,以便下次循环使用
end
end
end
label = label - 1;%最后一次label+1没有使用,所以需要减一次
toc
figure
imshow(output)
两步法:
clear all;
clc;
s=imread('rice.png');
s = im2bw(s);
[r c]=size(s);
output=zeros(r,c);
label=0; %第一遍遍历时标记的标签数量
s1 = zeros(r+2,c+2);
for i = 2:r+1
for j = 2:c+1
s1(i,j) = s(i-1,j-1);
end
end
imtool(s);
output1 = zeros(r+2,c+2);
pre = zeros(1000);
new_label = zeros(1000);
new_label_count = 1;
%第一次for循环得到下面情况
%对于每个点的4周的情况只有三种:
%1. 4个点全是0 处理方法:label+1,并赋值
%2. (至多4个点)全是一样的label 将周围的label赋值给这个点即可
%3. (至多4个点)有两种label
label赋值为label小的那个,并将这两个label放到同一个数组里面去
%4. (至多4个点)有三种label,这种情况我一开始一直以为是不存在的,直到程序跑出来因为漏掉了这种情况有很多黑点
%对于pre数组来说,pre[label] = label的时候是根节点
for i = 2:r+1 % y
for j = 2:c+1 % x
if s1(i,j) == 1
%查找周围4个点的情况
m = [];%将4个点对应的label值存起来
m = [output1(i-1,j),output1(i-1,j-1),output1(i,j-1),output1(i-1,j+1)];
m = unique(m);
%先去重复,看有没有全是0的情况,没有的话再去0
if length(m) == 1 && m(1) == 0
label = label + 1;
output1(i,j) = label;
pre(label) = label;
else
m(find(m == 0)) = [];
m = sort(m);
%去除数组中的0元素,并讨论是一种label,还是两种label,并将数组排序,方便将两个label放到同一个数组里面
if length(m) == 1
output1(i,j) = m(1);
elseif length(m) == 2
output1(i,j) = m(1);
pre(m(2)) = m(1);%m(2)是子节点 m(1)是父节点
elseif length(m) == 3
output1(i,j) = m(1);
pre(m(2)) = m(1);
pre(m(3)) = m(1);
end
end
end
end
end
for i = 2:r+1
for j = 2:c+1
output(i-1,j-1) = output1(i,j);
end
end
%处理pre数组,new_label给根节点重新编号
for i = 1:1000
if pre(i) ~= 0
%当pre数组的内容不为0的时候
%1.pre[label] = label label为根节点
%2.pre[label] = a label是a的父节点,需要寻找a的根节点
if pre(i) ~= i
tmp = i;
while(pre(tmp) ~= tmp)
tmp = pre(tmp);
end
pre(i) = tmp;
else
new_label(i) = new_label_count;
new_label_count = new_label_count + 1;
end
end
end
%需要重新将label编号
for i = 1:r
for j = 1:c
if output(i,j) ~= 0
output(i,j) = new_label(pre(output(i,j)));
end
end
end
imtool (output);
new_label_count = new_label_count - 1;
new_label_count