bwlabel实现(递归、一步法、两步法)

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图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

递归方法代码:

 
 
  1. clc;
  2. clear;
  3. %彩色图像转化成灰度再转化成二值图像
  4. global K;
  5. global bw;
  6. K = imread('rice.png');
  7. K = im2bw(K);
  8. imshow(K)
  9. global r;
  10. global c;
  11. [r ,c ] = size(K);
  12. bw = zeros(r,c);
  13. %global count;
  14. count = 1;
  15. global s;
  16. s = 1;%变量s用来控制count,使得一次递归只加一次
  17. %连通
  18. for i = 1:r
  19. for j = 1:c
  20. liantong(i,j,count);
  21. if s == 0;
  22. count = count +1;
  23. s = 1;
  24. end
  25. end
  26. end
  27. count = count - 1;
  28. figure
  29. imshow(bw)

递归函数文件(liantong.m)

 
 
  1. function liantong(i,j,count)
  2. global K;
  3. global bw;
  4. global r;
  5. global c;
  6. global s;
  7. if(K(i,j) == 1 && bw(i,j) == 0 )
  8. s = 0;
  9. bw(i,j) = count;
  10. if(i>1 )
  11. liantong(i-1,j,count);
  12. if( j > 1)
  13. liantong(i-1,j-1,count);
  14. end
  15. end
  16. if(i>1 && j<c)
  17. liantong(i-1,j+1,count);
  18. end
  19. if(j>1)
  20. liantong(i,j-1,count);
  21. end
  22. if(j<c)
  23. liantong(i,j+1,count);
  24. end
  25. if(i<r)
  26. liantong(i+1,j,count);
  27. if(j>1)
  28. liantong(i+1,j-1,count);
  29. end
  30. end
  31. if(i<r && j<c)
  32. liantong(i+1,j+1,count);
  33. end
  34. end
  35. end

一步法:

 
 
  1. K = imread('rice.png');
  2. tic
  3. K = im2bw(K);
  4. %在最外圈加上一圈
  5. [r,c] = size(K);
  6. K1 = zeros(r+2,c+2);
  7. for i = 2:r+1
  8. for j = 2:c+1
  9. K1(i,j) = K(i-1,j-1);
  10. end
  11. end
  12. output = zeros(r,c);
  13. label = 1;
  14. neighbour = [-1,-1;-1,0;-1,1;0,-1;0,1;1,-1;1,0;1,1];%8 邻域
  15. head = 1;
  16. tail = 1;
  17. %output相对于K1来说 向左上平移了一个单元
  18. for i = 2:r+1
  19. for j = 2:c+1
  20. if (K1(i,j) == 1 && output(i-1,j-1) == 0)%种子点
  21. s{tail} = [i,j];
  22. tail = tail + 1;
  23. while (head ~= tail)
  24. for l = 1:8
  25. q = s{head} + neighbour(l,:);%头指针为K(i,j)这个点,在for循环里面不能改变,而尾指针需要增加
  26. if q(1) > 1 && q(1) < r+2 && q(2) > 1 && q(2) < c+2
  27. if K1(q(1),q(2)) == 1 && output(q(1)-1,q(2)-1) == 0 %满足点条件
  28. output(q(1)-1,q(2)-1) = label;
  29. s{tail} = q ;%加入队列
  30. tail = tail + 1;
  31. end
  32. end
  33. end
  34. head = head + 1;
  35. end
  36. %
  37. clear s;
  38. label = label + 1;%最后一次label+1,但是并没有使用
  39. head = 1;
  40. tail =1;%恢复原状,以便下次循环使用
  41. end
  42. end
  43. end
  44. label = label - 1;%最后一次label+1没有使用,所以需要减一次
  45. toc
  46. figure
  47. imshow(output)

两步法:

 
 
  1. clear all;
  2. clc;
  3. s=imread('rice.png');
  4. s = im2bw(s);
  5. [r c]=size(s);
  6. output=zeros(r,c);
  7. label=0; %第一遍遍历时标记的标签数量
  8. s1 = zeros(r+2,c+2);
  9. for i = 2:r+1
  10. for j = 2:c+1
  11. s1(i,j) = s(i-1,j-1);
  12. end
  13. end
  14. imtool(s);
  15. output1 = zeros(r+2,c+2);
  16. pre = zeros(1000);
  17. new_label = zeros(1000);
  18. new_label_count = 1;
  19. %第一次for循环得到下面情况
  20. %对于每个点的4周的情况只有三种:
  21. %1. 4个点全是0 处理方法:label+1,并赋值
  22. %2. (至多4个点)全是一样的label 将周围的label赋值给这个点即可
  23. %3. (至多4个点)有两种label
  24. label赋值为label小的那个,并将这两个label放到同一个数组里面去
  25. %4. (至多4个点)有三种label,这种情况我一开始一直以为是不存在的,直到程序跑出来因为漏掉了这种情况有很多黑点
  26. %对于pre数组来说,pre[label] = label的时候是根节点
  27. for i = 2:r+1 % y
  28. for j = 2:c+1 % x
  29. if s1(i,j) == 1
  30. %查找周围4个点的情况
  31. m = [];%将4个点对应的label值存起来
  32. m = [output1(i-1,j),output1(i-1,j-1),output1(i,j-1),output1(i-1,j+1)];
  33. m = unique(m);
  34. %先去重复,看有没有全是0的情况,没有的话再去0
  35. if length(m) == 1 && m(1) == 0
  36. label = label + 1;
  37. output1(i,j) = label;
  38. pre(label) = label;
  39. else
  40. m(find(m == 0)) = [];
  41. m = sort(m);
  42. %去除数组中的0元素,并讨论是一种label,还是两种label,并将数组排序,方便将两个label放到同一个数组里面
  43. if length(m) == 1
  44. output1(i,j) = m(1);
  45. elseif length(m) == 2
  46. output1(i,j) = m(1);
  47. pre(m(2)) = m(1);%m(2)是子节点 m(1)是父节点
  48. elseif length(m) == 3
  49. output1(i,j) = m(1);
  50. pre(m(2)) = m(1);
  51. pre(m(3)) = m(1);
  52. end
  53. end
  54. end
  55. end
  56. end
  57. for i = 2:r+1
  58. for j = 2:c+1
  59. output(i-1,j-1) = output1(i,j);
  60. end
  61. end
  62. %处理pre数组,new_label给根节点重新编号
  63. for i = 1:1000
  64. if pre(i) ~= 0
  65. %当pre数组的内容不为0的时候
  66. %1.pre[label] = label label为根节点
  67. %2.pre[label] = a labela的父节点,需要寻找a的根节点
  68. if pre(i) ~= i
  69. tmp = i;
  70. while(pre(tmp) ~= tmp)
  71. tmp = pre(tmp);
  72. end
  73. pre(i) = tmp;
  74. else
  75. new_label(i) = new_label_count;
  76. new_label_count = new_label_count + 1;
  77. end
  78. end
  79. end
  80. %需要重新将label编号
  81. for i = 1:r
  82. for j = 1:c
  83. if output(i,j) ~= 0
  84. output(i,j) = new_label(pre(output(i,j)));
  85. end
  86. end
  87. end
  88. imtool (output);
  89. new_label_count = new_label_count - 1;
  90. new_label_count

猜你喜欢

转载自blog.csdn.net/Zhou_xinke/article/details/81000651