图像连通域检测的2路算法Code
本文算法描述参考链接:http://blog.csdn.net/icvpr/article/details/10259577
两遍扫描法:
(1)第一次扫描:
访问当前像素B(x,y),如果B(x,y) == 1:
a、如果B(x,y)的领域中像素值都为0,则赋予B(x,y)一个新的label:
label += 1, B(x,y) = label;
b、如果B(x,y)的领域中有像素值 > 1的像素Neighbors:
1)将Neighbors中的最小值赋予给B(x,y):
B(x,y) = min{Neighbors}
2)记录Neighbors中各个值(label)之间的相等关系,即这些值(label)同属同一个连通区域;
labelSet[i] = { label_m, .., label_n },labelSet[i]中的所有label都属于同一个连通区域(注:这里可以有多种实现方式,只要能够记录这些具有相等关系的label之间的关系即可)
(2)第二次扫描:
访问当前像素B(x,y),如果B(x,y) > 1:
a、找到与label = B(x,y)同属相等关系的一个最小label值,赋予给B(x,y);
完成扫描后,图像中具有相同label值的像素就组成了同一个连通区域。
算法代码:
从直觉上看,二路法比堆栈法要快,其实速度只有堆栈法的1/5 - 1/10,代码如下:
//使用两遍扫描法//查找所有的连通域
//简单方法,使用1-0矩阵//使用列标记的方法,收集所有的连通域
//需要使用倒排索引
bool CD2DetectInPic::searchConBy2Way(
const cv::Mat& _binImg,
float valueForeB, float valueForeUp,
std::vector<std::vector<cv::Point > > &foreAreas)
{
int vFore = 255;
int vBack = 0;
foreAreas.resize(0);
cv::Mat _lableImg;
if (_binImg.channels()>1)
{
cv::cvtColor(_binImg,_lableImg,cv::COLOR_BGR2GRAY);
}
else
{
_binImg.copyTo(_lableImg);
} //一遍扫描,得出前景和背景点,进行标记
//背景点标记为0,前景点标记为1
#ifdef SHOW_TEMP
cv::imshow("",_lableImg);//cv::waitKey(0);
#endif IplImage imageLabel = _lableImg;
for (int i=0;i< imageLabel.height;++i)
{
char* pI = (char*)imageLabel.imageData + i * imageLabel.widthStep;
for (int j=0;j<imageLabel.width;++j )
{
if ( *pI >=valueForeB &&*pI <=valueForeUp)//避开单一值失误!
{
*pI = vFore;
}
else
{
*pI = vBack;
}
++pI;
}
} //对label图像进行遍历,寻找连通域//对Mark矩阵,进行修改,不修改标识矩阵
cv::Mat imageMark(&imageLabel);
cv::Mat imageMarkRe = imageMark.clone(); //使用线扫描的方法进行分离合并
//使用set集合表示相等关系
#ifdef SHOW_TEMP
cv::imshow("imageMarkRe",imageMarkRe);//
cv::waitKey(1);
#endif std::stack<std::pair<int,int> > neighborPixels; //使用另外一个Label矩阵,用于记载当前点属于哪个连通域
cv::Mat conAreaLocMat = cv::Mat::zeros(_binImg.rows,_binImg.cols,CV_8UC1);
cv::bitwise_not(conAreaLocMat,conAreaLocMat);
IplImage imageConAreaLoc = conAreaLocMat; std::vector<std::pair<cv::Point,uchar> > conAreas(imageMark.cols);
std::vector<std::vector<cv::Point> > conPointsAreas(0); //第n个连通域
int counter =0;//用于标记为第N个连通域
int idx =0;//用于哈希索引 //设置重复集合//用于最后合并连通域
//std::vector<std::vector<int> > overlapVec(0);
//std::set<int,int> overlapSet; std::multimap<int,int> overlapMap;//使用多值哈希 //对第一行进行连通域划分
{
char* pLabel = (char*)imageLabel.imageData + 0* imageLabel.widthStep;
char* pLoc = (char*)imageConAreaLoc.imageData + 0* imageConAreaLoc.widthStep;
int lastL = *pLabel; if (vFore == lastL)
{
++counter;
*pLoc = counter;//计数从1开始,索引从0开始
//++idx; std::pair<int,int> p(counter,idx);
overlapMap.insert(p);//插入索引
}
else
{
*pLoc = -1;
} int lastLoc = *pLoc;
++pLabel;
++pLoc; for (int n=1; n< imageMark.cols; ++n)
{
if (vFore == *pLabel )//若标记为连通域,则进行标记
{
if ( *pLabel==lastL )//若和上一个像素联通,则标记为上一个的标记
{
*pLoc = lastLoc;
}
else
{//若上一个为0,则增加标记符号,为新的标记
++counter;
*pLoc = counter;
++idx; std::pair<int,int> p(counter,idx);
overlapMap.insert(p);//插入索引
//std::cout<< idx << "_" <<counter<<std::endl;
}
}
else
{
*pLoc = -1;//若遇到第n个点为变化点,标记为第n个连通域的位置
} lastL = *pLabel;
lastLoc = *pLoc;
++pLoc;
++pLabel;
}
} //对图片每一行进行寻找连通域,必须遍历
for (int m =1; m<imageMark.rows; ++m )
{
//对第一个像素进行指派
//上一行的指针
char* pLabelFore = (char*)imageLabel.imageData + (m-1)* imageLabel.widthStep;
char* pLocFore = (char*)imageConAreaLoc.imageData + (m-1)* imageConAreaLoc.widthStep;
//这一行的指针
char* pLabel = (char*)imageLabel.imageData + m* imageLabel.widthStep;
char* pLoc = (char*)imageConAreaLoc.imageData + m* imageConAreaLoc.widthStep; //标记第一个点,以上一行为准
int foreL = *pLabelFore;
int foreLoc = *pLocFore; int lastL = *pLabel; if (*pLabel == foreL)//若和上一行的值相等,则连通域位置和上一行的第一个值相等
{
*pLoc = *pLocFore;
}
else
{
if ( vFore == *pLabel)
{
//若为连通域,则增加标记符号,为新的标记
++counter;
*pLoc = counter;
++idx; std::pair<int,int> p(counter,idx);
overlapMap.insert(p);//插入索引
//std::cout<< idx << "_" <<counter<<std::endl;
}
else
{
*pLoc = -1;
} } int lastLoc = *pLoc; for (int n=0; n< imageMark.cols-1; ++n)
{
//寻找每一行的连通域
lastL = *pLabel;//上一个像素的标记
lastLoc = *pLoc;//上一个像素的连通域位置 //到下一个像素
++pLabel;
++pLoc;
++pLabelFore;
++pLocFore; foreL = *pLabelFore;//上一行此位置像素的值
foreLoc = *pLocFore;//上一个像素的连通域位置 if (vBack == *pLabel )//若当前label为0
{
*pLoc = -1;
}
else
{
if (*pLabel != foreL )//若当前label不等于上一行像素的label
{
if (*pLabel != lastL )
{//若当前label不等于上一个像素的label,则表示为新的连通域起始,新建集合
//若为连通域,则增加标记符号,为新的标记
++counter;
*pLoc = counter;
++idx; std::pair<int,int> p(counter,idx);
overlapMap.insert(p);//插入索引
//std::cout<< idx << "_" <<counter<<std::endl;
}
else
{//若当前label等于上一个像素的label,则为连通,直接赋值
*pLoc = lastLoc;
} }
else
{//若等于前一行像素,就麻烦了!
if (*pLabel != lastL)//若不等于上一个的像素label,则赋值为上一行的标记
{
*pLoc = foreLoc;
}
else
{
if (foreLoc == lastLoc)//若前一个和前一行相等
{
*pLoc = foreLoc;
}
else
{//若前一个和前一行不相等
//若等于前一个像素,需要合并集合//标记相同集合
*pLoc = foreLoc; //合并哈希表
//查找到值所在的哈希索引
int idxF = ( *overlapMap.find(foreLoc) ).first;
int idxFL =( *overlapMap.find(lastLoc) ).first;//map寻找的为key,而不是值
int L1 = ( *overlapMap.find(foreLoc) ).second;
int L2 = ( *overlapMap.find(lastLoc) ).second; if (L1 != L2)//若不在一个索引中,可以删除
{
//将当前值插入到索引之中,不增加idx索引
//必须先删除再插入,以免误删除 //去除掉旧的 键值 对
//overlapMap.erase(idxFL);//一个参数为key,是错误的
//int x = overlapMap.erase(lastLoc);
overlapMap.erase(idxFL); //std::pair<int,int> p(idxF,lastLoc);
std::pair<int,int> p(lastLoc,L1);
overlapMap.insert(p); }
else
{
}
} } } }
}
} SYSTEMTIME sys;
GetLocalTime( &sys );
int MileTs = sys.wSecond;int MileT = sys.wMilliseconds; //直接查找到位置
conPointsAreas.resize(overlapMap.size());
IplImage imageConArea= imageConAreaLoc;
for (int i=0;i< imageConArea.height;++i)
{
char* pI = (char*)imageConArea.imageData + i * imageConArea.widthStep;
for (int j=0;j<imageConArea.width;++j )
{
if (*pI >0)
{
cv::Point p(j,i);
int idx = (*overlapMap.find((int)*pI)).second-1;
conPointsAreas[idx ].push_back(p);
}
++pI;
}
} int validVec =0;
foreAreas.resize(validVec);
for (int i=0;i< conPointsAreas.size();++i)
{
if (conPointsAreas[i].size() >0 )
{
++validVec;
foreAreas.push_back(conPointsAreas[i]);
}
} GetLocalTime( &sys );
int MileT2 = sys.wMilliseconds;
int DetaT = MileT2 - MileT;
std::cout<< std::endl;
std::cout<< "The CopyVec time is :"<< DetaT<<"mS..........."<< std::endl;
std::cout<< std::endl; conPointsAreas.resize(0);
return true;
}
图像连通域检测的2路算法Code的更多相关文章
- OpenCV: 图像连通域检测的递归算法
序言:清除链接边缘,可以使用数组进行递归运算; 连通域检测的递归算法是定义级别的检测算法,且是无优化和无语义失误的. 同样可用于寻找连通域 void ClearEdge(CvMat* MM,CvPoi ...
- 14FPGA综设之图像边沿检测的sobel算法
连续学习FPGA基础课程接近一个月了,迎来第一个有难度的综合设计,图像的边沿检测算法sobel,用verilog代码实现算法功能. 一设计功能 (一设计要求) (二系统框图) 根据上面的系统,Veri ...
- two Pass方法连通域检测
原理: Two-Pass方法检测连通域的原理可参见这篇博客:http://blog.csdn.net/lichengyu/article/details/13986521. 参考下面动图,一目了然. ...
- 特征点检测学习_2(surf算法)
依旧转载自作者:tornadomeet 出处:http://www.cnblogs.com/tornadomeet 特征点检测学习_2(surf算法) 在上篇博客特征点检测学习_1(sift算法) 中 ...
- 模式匹配之surf----特征点检测学习_2(surf算法)
在上篇博客特征点检测学习_1(sift算法) 中简单介绍了经典的sift算法,sift算法比较稳定,检测到的特征点也比较多,其最大的确定是计算复杂度较高.后面有不少学者对其进行了改进,其中比较出名的就 ...
- CVPR目标检测与实例分割算法解析:FCOS(2019),Mask R-CNN(2019),PolarMask(2020)
CVPR目标检测与实例分割算法解析:FCOS(2019),Mask R-CNN(2019),PolarMask(2020)1. 目标检测:FCOS(CVPR 2019)目标检测算法FCOS(FCOS: ...
- 大尺寸卫星图像目标检测:yoloT
大尺寸卫星图像目标检测:yoloT 1. 前言 YOLT论文全称「You Only Look Twice: Rapid Multi-Scale Object Detection In Satellit ...
- Union-Find 检测无向图有无环路算法
不相交集合数据结构(Disjoint-set data structure)是一种用于跟踪集合被分割成多个不相交的子集合的数据结构,每个集合通过一个代表来标识,代表即集合中的某个成员. Union-F ...
- [算法]检测空间三角形相交算法(Devillers & Guigue算法)
#pragma once //GYDevillersTriangle.h /* 快速检测空间三角形相交算法的代码实现(Devillers & Guigue算法) 博客原地址:http://bl ...
随机推荐
- nyoj_17_单调递增最长子序列_201403121516
单调递增最长子序列 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描述 求一个字符串的最长递增子序列的长度如:dabdbf最长递增子序列就是abdf,长度为4 输入 ...
- php 数组 array()
定义和用法 array() 创建数组,带有键和值.如果在创建数组时省略了键,则生成一个整数键,默认从 0 开始,然后以 1 进行递增. 用 array() 创建一个数组,可使用 => 来分隔键和 ...
- GDUT Krito的讨伐(bfs&&优先队列)
题意 Description Krito最终干掉了99层的boss,来到了第100层. 第100层能够表示成一颗树.这棵树有n个节点(编号从0到n-1),树上每个节点可能有非常多仅仅怪物. Krito ...
- Test for Job (poj 3249 记忆化搜索)
Language: Default Test for Job Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 9733 A ...
- iOS7系统iLEX RAT冬青鼠安装教程:无需刷机还原纯净越狱系统
全网科技 温馨提醒:iLEX RAT和Semi-Restore的作用都是让你的已越狱的设备恢复至越狱的初始状态. 可是要注意无论你是用iLexRAT冬青鼠还是Semi-restore.对于还原来说都存 ...
- Xcode 自己主动生成版本技术最佳实践
在 bloglovin ,我们使用自己主动生成版本来设置Xcode,使当前的版本为在Git活跃的分支上 的提交数. 它一直正常工作着.但我们的技术也不是一帆风顺的. 糟糕的老方法 我们使用的技术是来自 ...
- 用sp_executesql执行动态SQL语句及获得返回值
过去我执行拼凑出来的动态SQL语句,都直接使用EXEC @sql 的方式.有好几次,都看到有资料说,应该尽量使用 sp_executesql. 究其原因,是因为仅仅参数不同的情况下,sp_execut ...
- 弹出框中选项卡的运用(easyUI)
先看一下页面效果: 此处有两个知识点:一个是弹出框的运用,一个是选项卡的运用 分析一下该HTML代码,最外面一个div是弹出框的,默认是关闭状态,可通过ID来控制弹出框的开关,该div的样式是easy ...
- hdu 2586(Tarjan 离线算法)
How far away ? Time Limi ...
- EOJ 3384 食物链
动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1-N 编号.每个动物都是 A,B,C 中的一种,但是我们并不知道它到 ...