连通分量标记算法
2016-04-23 13:08
471 查看
引言
图像分割领域中一个重要步骤是求取图像的连通区域,后续图像轮廓理解的基石。Matlab图像工具箱函数bwlabel函数,就是对二值图像连通区域进行标记的函数。
bwlabel
Label connected components in 2-D binary image。
Syntax
L = bwlabel(BW, n)
[L, num] = bwlabel(BW, n)
Description
L = bwlabel(BW, n) returns a matrix L, of the same size as BW, containing labels for the connected objects in BW. The variable n can have a value of either 4 or 8, where 4 specifies 4-connected objects and 8 specifies 8-connected objects. If the argument is omitted, it defaults to 8.
该函数返回一个与原图大小一致的标记图像矩阵。冈萨雷斯书中只介绍了其使用方法,没有介绍其实现原理等,OpenCV也有connectedComponents函数。有必要造轮子,理解步骤。
Two-pass算法
这里我实现的是Two Pass算法,四邻域。该算法对二值图像扫描两次:当前像素点若是前景(非零),判断其已扫描过的邻接节点(上邻像素,左邻像素)有无已标记的。若仅有一个含有标记值,则将该标记赋予该位置,若两个都含有标记值,将最小标记赋予当前值,并将这两个标记值合并(Union),归为同一类;若无,新增标记赋予当前值,即label++。
对上述含有标记的像素,查找其集合的根节点(Find),用根节点对当前值赋值。
执行动态图如下(该图是盗网友的,上面有logo的)。
这里使用了一种并查集的数据结构,可以看作数组方式实现的树结构。该结构主要含有两个操作:
快速对集合元素合并归类
快速查找一个元素的根节点
这在严蔚敏的数据结构书中有介绍过的。看一下下图,希望能赶紧回忆起来吧。
这个并查集数组,初始化的时候全为0,即每个都是根节点。然后我们发现有些元素属于同一类,就将其一个节点作为另一个节点的孩子,合并为同一个树。最后,我们都用每个集合的根节点的作为该集合的标志。
代码
#define MAXLABEL 500 uchar parent[MAXLABEL] = {0}; int Find(uchar x, uchar parent[]) { int i = x; while (0 != parent[i]) i = parent[i]; return i; } void Union(uchar big, uchar small, uchar parent[]) { uchar i = big; uchar j = small; while (0 != parent[i]) i = parent[i]; while (0 != parent[j]) j = parent[j]; if (i != j) parent[i] = j; } Mat Label(Mat &I) { /// first pass int label = 0; Mat dst = Mat::zeros(I.size(), I.type()); for (int nY = 0; nY < I.rows; nY++) { for (int nX = 0; nX < I.cols;nX++) { if (I.at<uchar>(nY,nX) != 0) { uchar left = nX - 1<0?0:dst.at<uchar>(nY, nX - 1); uchar up = nY - 1<0?0:dst.at<uchar>(nY - 1, nX); if (left != 0 || up != 0) { if (left != 0 && up != 0) { dst.at<uchar>(nY, nX) = min(left, up); if(left < up) Union(up,left,parent); else if(up<left) Union(left, up, parent); } else dst.at<uchar>(nY, nX) = max(left, up); } else { dst.at<uchar>(nY, nX) = ++label; } } } } /// second pass for (int nY = 0; nY < I.rows; nY++) { for (int nX = 0; nX < I.cols; nX++) { if (I.at<uchar>(nY, nX) == 1) dst.at<uchar>(nY, nX) = Find(dst.at<uchar>(nY, nX), parent); } } return dst; }
效果
为了显示好看,我将连通区域着色,这里使用哈希函数,将每个标记乘以一个素数对256求余,这样可以将颜色散列开来。更多阅读
George Stockman and Linda G. Shapiro. 2001. Computer Vision (1st ed.). Prentice Hall PTR, Upper Saddle River, NJ, USA. Chapter 3.相关文章推荐
- caffe for windows 训练cifar10 及若干问题
- org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thre
- 通过字节码分析JDK8中Lambda表达式编译及执行机制
- 第八周项目33-对类中深复制的体验
- 摄像头自动对焦
- 静态方法与实例方法的区别
- Download Fedora 16 CD / DVD ISO
- for环中创建map,创建的是一个map对象还是多,map和内存的问题
- FOR LEI LEI
- Java千百问_05面向对象(011)_引用传递和值传递有什么区别
- ZOJ - 1295 Reverse Text
- Qt 打开安卓相册选择图片并获取图片的本地路径
- 升级Xcode7.3出现Cannot create __weak reference in file using manual reference counting 解决方法:
- Lucene查询结果高亮
- Ruby元编程-学习笔记(三)-代码块
- 第九周上机实践项目-阅读项目-3
- nginx 配置文件分析以及配置负载均衡器
- 3-2 Verilog 4位行波进位加法器
- proxifier 代理bluestack
- deviceready事件