一直在使用halcon进行图像处理,但本人更倾向于自己写算法,所以也一直在使用Opencv。对于halcon,其连通域的处理相当方便,所以一直想用Opencv来实现这样的功能。由于最近项目以及对后续转用Opencv的想法,利用工作之余的时间查了些资料,再结合自己的一些想法,用Opencv实现了这项功能。
最开始在网上找了些方法,例如1)Two-Pass法;2)Seed-Filling种子填充法[1] ,这两种方法容易理解,都是基于8连通或者4连通的基础,但效率相当低。后来找了几篇论文,提到了基于行程的提取方式,只需要对图像进行单次扫描就可以实现连通域的提取。所谓基于行程,其实就是在每一行中,一个连续的区域,其起始到结束,相对于图像最左侧起始端的距离。具体理解可以看参考论文[2][3][4]。但实现方式多样,有的基于二叉树数据结构来构建连通域,有的基于多叉树。本篇博文,主要是基于双向列表。下面介绍连通域结构的定义,最后贴上伪码。
一、数据结构:
//连通域结构定义,此结构对应于每一行中连通的区域
typedef struct stRegion
{
int row; //行号
int begin; //区域在行中的起始列
int end; //区域在行中的结束列
int prevNum;
int nextNum;
stRegion* prev;
stRegion* next;
stRegion* header;
stRegion* tail;
int label;//仅供表头使用
bool ispaint;
}Region;
//连通域集合
typedef struct stRegions
{
private:
vector<OCVRegion*> arr;
}Regions;
二、算法原理:
扫描每一行,对每行中的连通域进行标记,每一行的每一个连通域对应于一个Region结构。然后,对前一行与当前行的连通域进行比较,如果两个连通域的行程,也即它们的起始和终止点之间的区域有重合的情况,如下图所示,1和2,5和6之间就是重合的关系。如果当前Region与上一行Region有重合,如果是第一次,则创建一个空的Region结构,将上一行Region的next指向当前行Region,将当前行Region的prev指向上一行Region,然后将空的Region的next指向当前行Region,作为表头,同时给空的Region的label赋上当前行号。再创建一个空的Region,其next指向上一行Region,作为表底。然后将表头压入Regions中
如果不是第一次,则有新的Region,则插入表中,然后更新表头的指向,直至扫描结束。最终Regions中压入的表头,就是一个连通区域,通过表头可以遍历整个区域在图像中的每一行,进而进行其他的运算。
三、伪码
Require lastRow:Region指针数组,用于存放上一行连通区域结构
Require label:连通标记号
Require currentRow:Region指针数组,用于存放当前行连通区域结构
def Connection(image):
normalize(image)
for y in image's rows do
for x in image's cols do
if pixel value equal to 1
创建新 region 同时设置 reigon.begin as x
继续扫描直到像素等于 0 同时设置 region.end as x-1
endif
for n in lastRow do
if lastRow 与 region重合
if lastRow.header == region.header==null
创建列表,及表头,将表头压入Regions中
else if lastRow.header!=null && region.header==null
将region加入列表的前头,并将列表header指向它
else if lastRow.header==null && region.header!=null
将lastRow加入region当前列表头,并将列表header指向它
else if lastRow.header!=null && region.header!=null
若是两个表,则合并
endif
endif
endfor
endfor
将当前行的所有Region都赋给LastRow
endfor
输出Regions
四、效率
这里不做严谨的实验,只说说大概的效率,对于如下图像(500万原生图像)
处理效果:
时间在300ms左右。效率还可以提高,期待下篇博文。
参考:
[1].https://blog.csdn.net/icvpr/article/details/10259577 OpenCV_连通区域分析(Connected Component Analysis-Labeling)
[2].快速连通域分析算法及其实现. 著; 孙斌 ,模式识别与人工智能
[3].基于快速连通域分析的目标特征提取算法. 著;张恒,胡文龙,丁赤飙
[4].A run-based two-scan labeling algorithm[J].JEEE Transaction on Image Processing, 2008,17(5)