图像的连通域寻找在直觉上可使用递归的方法,进而可以使用堆栈数据结构进行改进。本文描述了一个图像寻找连通域的堆栈方法,四连通域和八联通域的选择可以使用一个参数来确定。
以下是代码:
//根据种子点寻找8连通域//使用两遍扫描//查找所有的连通域
//划分为前景、背景、和无关位置
//一个点 也必须当做连通域
bool CD2DetectInPic::searchConBy8Con(
const cv::Mat& _binImg, cv::Mat& _lableImg,
float valueForeB,float valueForeUp, int GlintSizeLowerBound, int GlintSizeUpperBound,
std::vector<std::vector<cv::Point > > &foreAreas)
{
// connected component analysis (8-component)
// use seed filling algorithm
// 1. begin with a foreground pixel and push its foreground neighbors into a stack;
// 2. pop the top pixel on the stack and label it with the same label until the stack is empty
if (_binImg.channels()>1)
{
cv::cvtColor(_binImg,_lableImg,cv::COLOR_BGR2GRAY);
}
foreAreas.resize(0);
寻找4连通域
//int xNum[4] = {1,0,-1,0};
//int yNum[4] = {0,1,0,-1};
//寻找8连通域
int xNum[8] = {1,1,0,-1,-1,-1,0,1};
int yNum[8] = {0,1,1,1,0,-1,-1,-1};
//一遍扫描,得出前景和背景点,进行标记
//背景点标记为0,前景点标记为1,其他标记为255
//cv::imshow("",_lableImg);//cv::waitKey(0);
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 = 1;
}
else
{
*pI = 255;
}
++pI;
}
}
//对label图像进行遍历,寻找连通域
//对Mark矩阵,进行修改,不修改标识矩阵
cv::Mat imageMark(&imageLabel);
cv::Mat imageMarkRe = imageMark.clone();
#if SHOW_TEMP
cv::imshow("imageMarkRe",imageMarkRe);//cv::waitKey(0);
#endif
std::stack<std::pair<int,int> > neighborPixels;
//对图片每个点进行寻找连通域,必须遍历
for (int m =0; m<imageMark.rows; ++m )
{
for (int n=0; n< imageMark.cols; ++n)
{
//亮点,使用数组取代条件查找。。。
CvPoint seedT;
if (imageMarkRe.at<uchar>(m,n) <120 )//遍历前景
{
std::vector<cv::Point > foreArea;
int x = seedT.x = n; //遍历当前点
int y = seedT.y = m;
neighborPixels.push(std::pair<int,int>(x,y) ) ;
cv::Point P(x,y);
foreArea.push_back(P);//要把第一个点当做连通域
while (!neighborPixels.empty())
{
// get the top pixel on the stack and label it with the same label
std::pair<int,int> curPixel = neighborPixels.top() ;
int curX = seedT.x = curPixel.first ;
int curY = seedT.y = curPixel.second ;
neighborPixels.pop(); //标记取出,同时标记已遍历
imageMarkRe.at<uchar>(curY,curX) = 255;//标记为已遍历
//寻找前景8连通域
//if (imageMark.at<uchar>(seedT.y,seedT.x) <120 && imageMarkRe.at<uchar>(seedT.y,seedT.x) <120)
{
for(int k=0 ;k<8 ;k++) //四联通要修改为4
{
int yy = curY + yNum[k];
int xx = curX + xNum[k];
if (yy <0 || xx <0 ||yy >= imageMarkRe.rows || xx >= imageMarkRe.cols )
{
continue;
}
else
{
if (imageMark.at<uchar>(yy,xx) <120 && imageMarkRe.at<uchar>(yy,xx) <120 )
{
cv::Point P(xx,yy);
std::pair<int,int> seedCur;
seedCur.first =seedT.x = xx;
seedCur.second =seedT.y = yy;
foreArea.push_back(P);
neighborPixels.push(seedCur);
imageMarkRe.at<uchar>(yy,xx) = 255;//标记为已遍历
}
}
}
}
}
if (foreArea.size()>=GlintSizeLowerBound && foreArea.size()<=GlintSizeUpperBound)
{
foreAreas.push_back(foreArea);
}
}
}
}
return true;
}
二值化图像的结果:
除去小的边缘的效果: