图像的连通域检测的堆栈算法

    图像的连通域寻找在直觉上可使用递归的方法,进而可以使用堆栈数据结构进行改进。本文描述了一个图像寻找连通域的堆栈方法,四连通域和八联通域的选择可以使用一个参数来确定。

     以下是代码:

//根据种子点寻找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;
}

二值化图像的结果:

   


除去小的边缘的效果:

    

posted @ 2016-03-20 10:28  wishchin  阅读(344)  评论(0编辑  收藏  举报