关于Two-Pass标记连通域个数

背景

在完成图像的一系列处理后,得到二值图,一般会统计目标数量,即是获取连通域个数,这里采用TwoPass的方法。

基本思想

在Two-pass连通域标记中,第一次标记(first pass)时从左向右,从上向下扫描,会将各个有效像素置一个label值,判断规则如下(以4邻域为例):

  1. 当该像素的左邻像素和上邻像素为无效值时,给该像素置一个新的label值,label ++;
  2. 该像素的左邻像素或者上邻像素有一个为有效值时,将有效值像素的label赋给该像素的label值;
  3. 当该像素的左邻像素和上邻像素都为有效值时,选取其中较小的label值赋给该像素的label值。

此时,还需维护一个关系表,记录哪些label值属于同一个连通域。这个关系表通常用union-find数据结构来实现。

原文在这

还有个图:

原文说用union-find结构保存连通域,没仔细看,于是自己用了个数组保存,类似于hash...实现起来也并不是很麻烦,大概这样,在开始建立一个map数组固定大小,一般而言应该与用于标记的最大数一致(注意,这里是标记数,是小于连通域数的),这里取uint16最大值,65535,这样也就意味着连通域数不能太大,估计最大能达到万把个吧,因为不同的图标记数和连通域数关系是不一样的。

在FirstPass时,取周围点的标记最小值,map[标记]=min,这样就构成了map数组,然后根据map数据进行第二次标记。

实现

int ImageAlgorithm::TwoPassConnetedDomin(Mat image) {
Mat imageFlag;
imageFlag.create(image.rows, image.cols, CV_16UC1);
for (int k = 0; k < image.rows; ++k) {
for (int i = 0; i < image.cols; ++i) {
imageFlag.at<ushort>(k, i) = UINT16_MAX;
}
}
uint16_t mapp[UINT16_MAX];
memset(mapp, UINT16_MAX, sizeof(uint16_t));
//第一次扫描,完成ImageFlag中的标记
int num = 0;
for (int i = 0; i < image.rows; ++i) {
for (int j = 0; j < image.cols; ++j) {
auto &cu = image.at<Vec3b>(i, j);
if (cu[0] == 255) {
uint16_t pos[4];
auto &up = pos[0];
auto &left = pos[1];
auto &ul = pos[2];
if (i > 0)
up = imageFlag.at<ushort>(i - 1, j);
if (j > 0)
left = imageFlag.at<ushort>(i, j - 1);
if (i > 0 && j > 0)
ul = imageFlag.at<ushort>(i - 1, j - 1); uint16_t min = pos[0];
for (int m = 1; m < 3; m++)
if (min > pos[m])
min = pos[m];
for (int m = 0; m < 3; m++) {
if (mapp[pos[m]] > min)
mapp[pos[m]] = min;
}
if (min == UINT16_MAX) {
imageFlag.at<ushort>(i, j) = num;
mapp[num] = num;
num++;
if(num>=UINT16_MAX)
return -1;
} else {
imageFlag.at<ushort>(i, j) = min;
} }
}
}
//第二次扫描,进行标记,以及修改map
map<ushort, uint32_t> colorMap;
int total = 0;
for (int n = 0; n < num; ++n) {
if (mapp[n] == n) {
total++;
uint32_t t = 0;
for (int i = 0; i < 3; ++i) {
t += (uint32_t) (rand() / (RAND_MAX + 0.0) * 255) << (i * 8);
}
colorMap[n] = t;
} else
mapp[n] = mapp[mapp[n]];
} for (int i = 0; i < imageFlag.rows; ++i) {
for (int j = 0; j < imageFlag.cols; ++j) {
auto t = imageFlag.at<ushort>(i, j);
if (t != UINT16_MAX) {
uint32_t c = colorMap[mapp[t]];
image.at<Vec3b>(i, j)[0] = (uchar) c;
image.at<Vec3b>(i, j)[1] = (uchar) (c >> 8);
image.at<Vec3b>(i, j)[2] = (uchar) (c >> 16);
}
}
}
return total;
}

关于Two-Pass标记连通域个数的更多相关文章

  1. two Pass方法连通域检测

    原理: Two-Pass方法检测连通域的原理可参见这篇博客:http://blog.csdn.net/lichengyu/article/details/13986521. 参考下面动图,一目了然. ...

  2. [代码片段]YEAH!连通域标记和计数

    //标记的连通域存储在buff[]里 //返回值为连通域个数 int LinkBlob(unsigned char **imagedata,unsigned char buff[], int heig ...

  3. Java 对二值化图片识别连通域

    用Java 对 已经 二值化了的图片 标记连通域 每块的连通域都标记不一样的数字 public static void main(String [] args) throws IOException ...

  4. CDOJ 1965 连通域统计【DFS】

    求连通域个数,简单题 #include <bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; typedef l ...

  5. nyoj 86 --位标记

    nyoj 86 --位标记 点击打开题目链接 :                        找球号(一)  这道题目很多解法,其他解法请参考 http://www.cnblogs.com/play ...

  6. 使用OpenCV进行相机标定

    1. 使用OpenCV进行标定 相机已经有很长一段历史了.但是,伴随着20世纪后期的廉价针孔照相机的问世,它们已经变成我们日常生活的一种常见的存在.不幸的是,这种廉价是由代价的:显著的变形.幸运的是, ...

  7. 使用OpenCV进行标定(转载)

    转载自牛猫靖  http://www.cnblogs.com/2008nmj/p/6278076.html 使用OpenCV进行相机标定 1. 使用OpenCV进行标定 相机已经有很长一段历史了.但是 ...

  8. python之路之线程,进程,协程2

    一.线程 1.创建线程 2.主线程是否等待子线程 t.setDaemon(Ture/False):默认是false,等待子线程完成,ture,表示不等待子线程结束 3.主线程等待,子线程执行 join ...

  9. 剖析虚幻渲染体系(12)- 移动端专题Part 1(UE移动端渲染分析)

    目录 12.1 本篇概述 12.1.1 移动设备的特点 12.2 UE移动端渲染特性 12.2.1 Feature Level 12.2.2 Deferred Shading 12.2.3 Groun ...

随机推荐

  1. Weapsy 分析网站架构

    Weapsy 分析(一)网站架构 这个项目看了好久了,但是老没时间写一些分析心得.下班后想了想,事情也不能老拖着,还是得做. 如图所示:Weapsy由5个项目所组成,有点可惜了,没有测试的项目,说明一 ...

  2. thrift之TTransport层的内存缓存传输类TMemoryBuffer

    内存缓存是简单的在内存进行读写操作的一种传输,任何时候想在上面写入数据都是放入缓存中,任何时候读操作数据也是来至于缓存.内存缓存的分配使用c语言的malloc类函数,分配的长度是需要长度的两倍,需要考 ...

  3. How To: Use CLR Profiler

    (翻译)How To: Use CLR Profiler   第一次翻译对我而言比较长的E文,有很多不足之处,请见谅.(个人的习惯GC又做了名词又做了名词) 原文:http://msdn.micros ...

  4. ORACLE总结系列1--network文件夹里的admin的三个文件信息

    sqlnet.ora 作用类似于linux或者其他unix的 nsswitch.conf文件,通过这个文件来决定怎么样找一个连接中出现的连接字符串(connect descriptor) 假如sqln ...

  5. SZU:A12 Jumping up and down

    Judge Info Memory Limit: 32768KB Case Time Limit: 10000MS Time Limit: 10000MS Judger: Number Only Ju ...

  6. 玩转python之测试一个对象是否是类字符串

    提到类型测试,我首先想到python中“鸭子类型”的特点,所谓鸭子类型,即如果它走路像鸭子,叫声也像鸭子, 那么对于我们的应用而言,就可以认为它是鸭子了!这一切都是为了功能复用. 我们总是需要测试一个 ...

  7. Repeater 模板中查找子控件

    前言:对于Repeater控件,相信从事NETWeb开发的同仁们再熟悉不过了.因其呈现方式和Literal一样,并不在前端生成任何表单标签元素,所以属于比较轻量级的控件.不过青睐于Repeater的主 ...

  8. PRML 第三章 - 线性回归

    这段时间组里在有计划地学习书籍PRML (Pattern Recognition and Machine Learning),前两天自己做了一个里面第三章linear regression的分享,这里 ...

  9. LatestResultsProvider

    LatestResultsProvider 前言 阅读本文前,需要读者对happens-before比较熟悉,了解非阻塞同步的一些基本概念.本文主要为happens-before法则的灵活运用,和一些 ...

  10. 开启和禁用Wifi热点命令

    netsh wlan set hostednetwork mode=allow ssid=[无线网络名字] key=[密码] netsh wlan start hostednetwork --启用 禁 ...