图像的连通域寻找在直觉上可使用递归的方法,进而可以使用堆栈数据结构进行改进。本文描述了一个图像寻找连通域的堆栈方法,四连通域和八联通域的选择可以使用一个参数来确定。
以下是代码:
//根据种子点寻找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; }
二值化图像的结果:
除去小的边缘的效果: