(来点有用的)MATLAB二值图连通域快速标记算法

  • 基本原理
  • 算法实现
    • 0. 变量解释
    • 1.种子点搜索
    • 2 区域生长
  • 完整代码
  • 实验
  • 其他

by HPC_ZY


由于工程需要,用C++实现三维二值图像的连通域标记。像往常一样拿起冈萨雷斯《数字图像处理(第三版)》,按照算法原理先实现二维再改成三维。用(256,256,256)数据测试,根本停不下来(尴尬)。因此明白一个道理,在一定程度上 “经典” = “效率低” = “不考虑时间复杂度和空间复杂度”……
故上网查阅资料,学习快速算法,并记录在此分享给大家。


基本原理

连通域标记的本质就是把所有块找出来,并设置不同的编号。
循环使用区域生长即可达成目的。
主要步骤:
1)遍历图像找到种子点
2)区域生长找到区域Ni
3)记录,并在原图中删去区域Ni
4)重复1~3直至原图没有值为1的像素
参考文献《一种二值图像连通区域标记的简单快速算法》-葛春平


算法实现

有条件的同学请直接调用MATLAB自带的bwlabel,又快又准!
有条件的同学请直接调用MATLAB自带的bwlabel,又快又准!
有条件的同学请直接调用MATLAB自带的bwlabel,又快又准!

0. 变量解释


[M,N] = size(bw);      % 二值图像与尺寸
label = zeros(M,N);            % 标记结果
class = 0;                     % 类别
stopflag = 0;                  % 停止标记
sp = 1;                        % 遍历起始点
stack = zeros(M*N,2,'uint32');  % 缓存栈
stackidx = 0;          % 栈索引
neib = [-1,0;0,-1;1,0;0,1];    % 邻域偏移坐标

1.种子点搜索

  1. 不好的索引方式(不使用)
    两重循环,且需要利用flag才能退出外层循环
%%%%%%%%%%%%%%%%%%%% 不使用以下代码 %%%%%%%%%%%%%%%%%%%
flag = 0;
for i = 1:Mfor j = 1:Nif bw(i,j)x = i;y = j;flag = 1;breakendendif flagbreakend
end
  1. 好的索引方式,不好的搜索方式(不使用)
    利用单层循环,通过计算获得二维坐标。反复测试可提速2~4倍。
%%%%%%%%%%%%%%%%%%%% 不使用以下代码 %%%%%%%%%%%%%%%%%%%
for k = 1:M*Nif bw(k)y = ceil(k/M);x = k-(y-1)*M;breakend
end
  1. 好的搜索方式
    显而易见,已经搜索过的地方不可能再出现种子点。
    连通域越多,重复次数就越多,如果每次都从(1,1)开始搜索则浪费时间。
    因此我们每次迭代都从上一种子点之后开始搜索,时间复杂度为O(n)。
sp = 1;
for k = sp:M*Nif bw(k)y2 = ceil(k/M);x2 = k-(y2-1)*M;sp = k+1;breakend
end

2 区域生长

方法很多,这里不做对比。我们用空间换时间,牺牲内存消耗来减少时间开销。
方法如下:
1)建立一个大小为NUM2数组stack 来保存等待生长的点的坐标(NUM的值有图像大小决定,我取的MN),并设置一个索引记录当前缓存点的个数stackidx;
2)找到种子点,放在数组第一行,并令stackidx加1;
3)对stack中第stackidx行的点进行四领域生长,在stack中删去stackidx行的点,同时stackidx减1;然后将新得到的点加入stack,同时stackidx加1。
注意:每次找到目标点都要在原图中置0,并在结果图中标号。

neib = [-1,0;0,-1;1,0;0,1];
stackidx = 0;
while stackidxx = stack(stackidx,1);y = stack(stackidx,2);stackidx = stackidx-1;for n = 1:4dx = x+neib(n,1);dy = y+neib(n,2);if bw(dx,dy)stackidx = stackidx+1;stack(stackidx,:) = [dx,dy];bw(dx,dy) = 0;label(dx,dy) = class;endend
end

完整代码

function label = label2d(bw)
[M,N] = size(bw);
label = zeros(M,N);             % 分类结果
class = 0;                      % 类别
stopflag = 0;                   % 停止标记
sp = 1;                         % 遍历起始点
stack = zeros(M*N,2,'uint32');   % 缓存栈
stackidx = 0;           % 栈索引
neib = [-1,0;0,-1;1,0;0,1];      % 邻域偏移坐标while 1% 寻找种子点for k = sp:M*Nif bw(k)y = ceil(k/M);x = k-(y-1)*M;stackidx = stackidx+1;stack(stackidx,:) = [x,y];class = class+1;label(x,y) = class;bw(x,y) = 0;sp = k+1;breakendif k == M*N % 达到图像末端stopflag = 1;endend% 结束if stopflagbreakend% 连通域while stackidxx = stack(stackidx,1);y = stack(stackidx,2);stackidx = stackidx-1;for n = 1:4dx = x+neib(n,1);dy = y+neib(n,2);if bw(dx,dy)stackidx = stackidx+1;stack(stackidx,:) = [dx,dy];bw(dx,dy) = 0;label(dx,dy) = class;endendend
end
end

实验

对于连通域标记,MATLAB有库函数

% CC输出,BW二值图,CONN连通方式
CC = BWCONNCOMP(BW,CONN);


其输出为一结构体,内容分别为:连通方式,图像尺寸,连通域数量,像素索引列表。要获得标记图像还需进行操作,在下面测试中演示:

load bw2% 库函数
tic
label1tmp = bwconncomp(bw2,4);
label1 = zeros(size(bw2));
for k = 1:label1tmp.NumObjectslabel1(label1tmp.PixelIdxList{k}) = k;
end
t1 = toc;% 自函数
tic
label2 = label2d(bw2);
t2 = toc;figure
subplot(131),imagesc(bw2),title('原图')
subplot(132),imagesc(label1),title('库函数'),xlabel(t1)
subplot(133),imagesc(label2),title('自函数'),xlabel(t2)
colormap jet

通过实验结果可以看出,两者输出完全一致,库函数在速度上还是更胜一筹。但相比于教材上的经典算法,自函数速度已经提升百倍以上,尤其是在三维数据上(不做测试)。


其他

  1. 在种子点搜索和四邻域判断中,都使用了单层循环代替双层循环,以提高了计算速度。(对于三维图像效果更明显)
  2. 三维连通域标记只是从二维数组变为三维数组,比较简单,大家可在上述代码基础上加入第三维(z)即可。
  3. 由于C语言与MATLAB在数组存储上行列相反,所以改写为C语言时种子点搜索中坐标x,y要互换。

关于文中代码有任何问题欢迎讨论,最后还是把测试代码上传
https://download.csdn.net/download/xsz591541060/11877360
(包含二维、三维MATLAB版本以及测试代码)

MATLAB二值图连通域快速标记算法相关推荐

  1. MATLAB二值图求figure图中任意图形面积

    MATLAB二值图求figure图中任意图形面积 问题描述 思路 代码 效果 问题描述 MATLAB的figure图遇到曲线是由离散点组成,没有解析表达式.想求曲线包围面积时无法直接编程求解.如下图 ...

  2. OpenCV-细化算法(thinning algorithm)算法详解——提取二值图的骨架

    昨天不是说同学问我怎么绘制出轮廓的中心线.然后我上网查了一下其实这个有专门的算法叫做细化算法.用专业术语去描述绘制出轮廓的中心线叫做(提取图像的骨架).然后这一篇博客呢是我对这个细化算法的解读与实操~ ...

  3. Python中使用PIL快速实现二值图

    二值图 二值图像是指:每个像素不是黑就是白,其灰度值没有中间过渡的图像.二值图像一般用来描述文字或者图形,其优点是占用空间少,缺点是,当表示人物,风景的图像时,二值图像只能描述其轮廓,不能描述细节.这 ...

  4. matlab二维图绘制分析总结

    Matlab二维图绘图详解 函数分析-plot plot函数(二维线图) 语法 plot(X,Y) plot(X,Y,LineSpec) plot(X1,Y1,...,Xn,Yn) plot(X1,Y ...

  5. matlab 二值化_基于MATLAB的指纹识别系统[GUI界面+万字技术文档+语音播报]

    一.课题介绍 本设计为基于MATLAB的指纹识别系统.本设计系统主要对指纹图像进行三方面处理:图像预处理.特征提取和特征匹配.图像预处理包括四个步骤:图像灰度化.滤波增强.二值化.细化,对指纹图像进行 ...

  6. OTSU_图像二值化分割阈值的算法

    简介: 大津法(OTSU)是一种确定图像二值化分割阈值的算法,由日本学者大津于1979年提出.从大津法的原理上来讲,该方法又称作最大类间方差法,因为按照大津法求得的阈值进行图像二值化分割后,前景与背景 ...

  7. Python BFS 提取二值图联通域

    <Python BFS 提取二值图联通域>    2016年实习那会儿在京东搞身份证 OCR,那时候的OCR是基于 CNN 的单字识别的pipeline,所以就需要一些方法来对字符进行切割 ...

  8. OpenCV技巧 | 二值图孔洞填充方法与实现(附Python/C++源码)

    点击上方"OpenCV与AI深度学习",选择加"星标"或"置顶" 重磅干货,第一时间送达 导读 本文主要介绍使用OpenCV对二值图做孔洞填 ...

  9. C语言二值图的腐蚀膨胀及开闭运算

    (M2C系列)C语言二值图的形态学腐蚀膨胀及开闭运算 一.结构元 二.腐蚀 三.膨胀 四.开闭运算 五.其他 by HPC_ZY 由于剧情,需要纯C(不用三方库)实现图像算法.但作为一名MATLAB老 ...

最新文章

  1. AMD真龙一族网络社区成立
  2. java有点_JAVA 有点儿冷
  3. java直接量_Java教程:Java直接量(字面量)
  4. c++ decltype
  5. 安卓工程不继承ActionBarActivity的设置
  6. 基于Qt\C++实现的网络远程控制系统
  7. 摘花生(信息学奥赛一本通-T1284)
  8. Python官方文档学习心得(第五篇)
  9. glibc库详解及与POSIX,system V这些库之间关系的说明
  10. qt webkit 本地html5,Qt Webkit HTML5 Score
  11. 随想录(png的读取和显示)
  12. 真5G尚需时日,滑稽的假5G营销已经上演
  13. linux dropbox自动同步,Linux下DropBox定时同步备份文件
  14. Krpano元素的一些解析
  15. 自考本科英语(二)学习笔记和考试经验
  16. 一个Activity左右分别显示一个ListFragment
  17. 使用 Java 解决现代应用程序开发挑战
  18. 双重否定的翻译 百度翻译 VS. 谷歌翻译
  19. 案例分享|智慧广电的“宽带加速”之路,博睿数据来“私人定制”
  20. 用c语言编写英文词典软件下载,大家帮帮忙,谁帮忙用C语言链表编写一个英语查询词典!~...

热门文章

  1. 到底什么是股票委托接口?
  2. oracle应付账款凭证编号查找,记账凭证的编号方式
  3. 关于Scrum XP DevOps的学习
  4. 天气数据采集微服务的实现:数据采集组件、数据存储组件
  5. 基于Andriod的连锁药店管理系统APP(ssm+uinapp+Mysql)
  6. 达芬奇系列教程2-简单剪辑及一些快捷键
  7. Azkaban3.62版本搭建
  8. java 打包工具jar,打包工具类为jar包,注意事项
  9. excel批量超链接工作表_在Excel工作表中创建到另一个文档的超链接
  10. 倒计时时间可以任意设置的秒表