关于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. Random随机数

    Random类的常用方法 方法 备注 Int nextInt() 返回一个int类型的随机数 Int nextInt(n) 返回一个0到n之间的数,不包括n Double nextDouble() 返 ...

  2. 为ASP.NET MVC应用程序读取相关数据

    为ASP.NET MVC应用程序读取相关数据 2014-05-08 18:24 by Bce, 299 阅读, 0 评论, 收藏, 编辑 这是微软官方教程Getting Started with En ...

  3. 领域驱动设计(DDD)的实际应用

    领域驱动设计(DDD)的实际应用   笔者先前参与了一个有关汽车信息的网站开发,用于显示不同品牌的汽车的信息,包括车型,发动机型号,车身尺寸和汽车报价等信息.在建模时,我们只需要创建名为Car的实体( ...

  4. Linq无聊练习系列7----Insert,delete,update,attach操作练习

    /*********************Insert,delete,update,attach操作练习**********************************/            ...

  5. IOS开发小功能2:二维码扫描界面的设计(横线上下移动)

    效果图如上,实现的是一个二维码扫描界面. 下面我贴出线条上下移动的代码,至于二维码的代码是用的第三方库. 首先是整体的结构: 注意下面的库文件一个都不能少,否则会报错. TLTiltHighlight ...

  6. CSS 选择器的学习

    从蛋学习 CSS 选择器 + 做一个 Jumony 桌面工具,真费劲,目前Jumony Tool两大问题无法解决,完美的显示 html 文档和根据结果在文档中定位,有点儿跑题了…… 1. 选择“cla ...

  7. WCF/WPF公司内部订餐程序开发

    WCF/WPF公司内部订餐程序开发 (服务端篇) 上班的第一天,群里讨论关于订餐的问题,所以想到了要不要自己开发一个公司内部的订餐系统呢?方便公司内部员工的订餐,有了想法就简单的实践了下 . 实现还是 ...

  8. 【NET】Winform分页控件初探

    public partial class WinFormPager : UserControl { ; /// <summary> /// 当前页 /// </summary> ...

  9. 利用微信公众平台实现自动回复消息—java版

    最近公司需要拿微信公众平台做个东西,所以就开始了最基本学习,网上很多是php版的,对于我这个只会java,不会php的就只能在网上找点只言片语来一点一点学习了.不费话了直接贴图看效果(很简单的). 不 ...

  10. 利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句

    利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句 (喷血分享)利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句 在我们RDIFram ...