二值图像连通域标记算法

二值图像连通域标记算法

八邻域标记算法:

1)   判断此点八邻域中的最左,左上,最上,上右点的情况。如果都没有点,则表示一个新的区域的开始。
2)   如果此点八邻域中的最左有点,上右都有点,则标记此点为这两个中的最小的标记点,并修改大标记为小标记。
3)   如果此点八邻域中的左上有点,上右都有点,则标记此点为这两个中的最小的标记点,并修改大标记为小标记。
4)   否则按照最左,左上,最上,上右的顺序,标记此点为四个中的一个。


BOOL  CImageColorProcess::ConnectedLabelTwoPass(LPBYTE lpSrc, LPBYTE lpDst, int nSrcCount, int nW, int nH)
{
	if (nSrcCount != 24)
	{
		AfxMessageBox("非rgb图像,不处理!");
		return false;
	} 
	LPBYTE m_lpImgBitsMove;
	int * m_lpnMark;            //标记数组指针 
	int * m_lpnMarkMove;       //标记数组移动指针 
	int m_nMarkNumbers;       //标记的区域个数 
	m_lpnMark = NULL;
	if (m_lpnMark == NULL)
	{
		m_lpnMark = new int[nW*nH];
		ASSERT(m_lpnMark != NULL);
		m_lpnMarkMove = m_lpnMark;
	}
	::memset((LPBYTE)m_lpnMark, 0, nW*nH * 4);


	int nMarkValue = 1;//每次标识的值,nMarkValue会在后边递增,来表示不同的区域,从1开始标记。 
	int nMaxMarkValue = 0;     //记录最大的标识的值   
	int i, j;                 //循环控制变量   


	/* 定义存放等价对的链表,其元素是 EqualMark类型,
	定义list是为了节约存储空间。要使用Clist,
	应该#include <Afxtempl.h>。  */
	CList < EqualMark, EqualMark > lEqualMark;


	//初始化图像移动指针   
	m_lpImgBitsMove = lpDst;


	/*进行第一次扫描,将所得的等价对(EqualMark类型)加到lEqualMark链表中。
	使用nMarkValue来进行每一次新的标记,标记之后将其值加1。
	由于版面关系,这部分代码也同样略去不写。作者提出以下几点编程时要注意
	的地方。
	Note1:图像的四周像素并不会有8个相邻的像素。这时就要根据上、下、左、
	右四种不同的情况做不同的寻找等价对的判断。
	Note2:可以先对等价对进行排序,每次都保证MarkValue1<MarkValue2,
	这样易于管理等价对。
	Note3:在实际工作中,连续寻找出的等价对很容易重复,将本次找出的等价对
	和链表中保存的最后一个等价对相比较,如果不相等的话再存入等价对链表,
	这样可以大大降低链表中等价对的重复。
	Note4:第一次扫描之后,nMarkValue-1即为nMaxMarkValue。 */


	/************************************************************************/
	//下面为补充代码,完成对图象的第一次扫描   


	//初始化图象数组和标识数组的指针   
	int nEqualNum = 0;
	EqualMark tempEqualMark;    //用以暂时存放每次找到的等价关系   
	m_lpnMarkMove = m_lpnMark;
	//m_lpImgBitsMove = m_lpImgBits;


	int bObjectGray = 0;
	//标记图象的第一行、第一列的象素(只有这一个象素)   
	if (*m_lpImgBitsMove == bObjectGray)
	{
		*m_lpnMarkMove = nMarkValue++;
	}
	m_lpnMarkMove++;
	m_lpImgBitsMove++;


	//标记图象的第一行,此时不会出现等价的情况   
	for (i = 1; i <= nW; i++)
	{
		//需要标记的情况   
		if (*m_lpImgBitsMove == bObjectGray)
		{
			//前面没有被标记过,则开始一个新的标记   
			if (*(m_lpnMarkMove - 1) == 0)
			{
				*m_lpnMarkMove = nMarkValue++;
			}
			//前面被标记过,则跟随前一个标记   
			else
			{
				*m_lpnMarkMove = *(m_lpnMarkMove - 1);
			}
		}
		m_lpnMarkMove++;
		m_lpImgBitsMove++;
	}


	//除第一行之外的标记,此时会出现等价的关系   
	for (j = 1; j <= nH; j++)
	{
		m_lpImgBitsMove = lpDst + j*nW;
		m_lpnMarkMove = m_lpnMark + j*nW;


		//对每行的第一个点做处理,总体就是对图象的最左列做处理   
		//只需要检视上,右上两个点    
		if (*m_lpImgBitsMove == bObjectGray)
		{
			//<上>位置被标记过   
			if (*(m_lpnMarkMove - nW) != 0)
			{
				//跟随<上>标记   
				*m_lpnMarkMove = *(m_lpnMarkMove - nW);
				if (*(m_lpnMarkMove - nW) != *(m_lpnMarkMove - nW + 1) && *(m_lpnMarkMove - nW + 1) != 0)
				{
					//<上><右上>等价标记   
					AttachEqualMark(tempEqualMark, *(m_lpnMarkMove - nW),
						*(m_lpnMarkMove - nW + 1), nEqualNum, lEqualMark);
				}


			}
			//<上>没有标记,此时一定不会存在等价关系   
			else
			{
				if (*(m_lpnMarkMove - nW + 1) != 0)
				{
					*m_lpnMarkMove = *(m_lpnMarkMove - nW + 1);   //跟随<右上>标记   
				}
				//<上>、<右上>都没有标记,则开始新的标记   
				else
				{
					*m_lpnMarkMove = nMarkValue++;
				}
			}
		}
		m_lpnMarkMove++;
		m_lpImgBitsMove++;


		//对每行的中间点做标记处理,此时存在<左>、<左上>、<上>、<右上> 4种情况   
		for (i = 1; i <= nW - 1; i++)
		{
			//需要标记   
			if ((*m_lpImgBitsMove) == bObjectGray)
			{
				//<左>被标记过   
				if (*(m_lpnMarkMove - 1) != 0)
				{
					*m_lpnMarkMove = *(m_lpnMarkMove - 1);          //跟随<左>   


					if (*(m_lpnMarkMove - 1) != *(m_lpnMarkMove - nW - 1) && *(m_lpnMarkMove - nW - 1) != 0)
					{
						//标记<左>、<左上>等价   
						AttachEqualMark(tempEqualMark, *(m_lpnMarkMove - 1),
							*(m_lpnMarkMove - nW - 1), nEqualNum, lEqualMark);
					}


					if (*(m_lpnMarkMove - 1) != *(m_lpnMarkMove - nW) && *(m_lpnMarkMove - nW) != 0)
					{
						//标记<左>、<上>等价   
						AttachEqualMark(tempEqualMark, *(m_lpnMarkMove - 1),
							*(m_lpnMarkMove - nW), nEqualNum, lEqualMark);
					}


					if (*(m_lpnMarkMove - 1) != *(m_lpnMarkMove - nW + 1) && *(m_lpnMarkMove - nW + 1) != 0)
					{
						//标记<左>、<右上>等价   
						AttachEqualMark(tempEqualMark, *(m_lpnMarkMove - 1),
							*(m_lpnMarkMove - nW + 1), nEqualNum, lEqualMark);
					}
				}
				//<左>未被标记过   
				else
				{
					//<左上>被标记过   
					if (*(m_lpnMarkMove - nW - 1) != 0)
					{
						*m_lpnMarkMove = *(m_lpnMarkMove - nW - 1);


						if (*(m_lpnMarkMove - nW - 1) != *(m_lpnMarkMove - nW) && *(m_lpnMarkMove - nW) != 0)
						{
							//标记<左上>、<上>等价   
							AttachEqualMark(tempEqualMark, *(m_lpnMarkMove - nW - 1),
								*(m_lpnMarkMove - nW), nEqualNum, lEqualMark);
						}


						if (*(m_lpnMarkMove - nW - 1) != *(m_lpnMarkMove - nW + 1) && *(m_lpnMarkMove - nW + 1) != 0)
						{
							//标记<左上>、<右上>等价   
							AttachEqualMark(tempEqualMark, *(m_lpnMarkMove - nW - 1),
								*(m_lpnMarkMove - nW + 1), nEqualNum, lEqualMark);
						}




					}
					//<左>、<左上>未标记过   
					else
					{
						if (*(m_lpnMarkMove - nW) != 0)
						{
							*m_lpnMarkMove = *(m_lpnMarkMove - nW);            //跟随<上>标记   


							if (*(m_lpnMarkMove - nW) != *(m_lpnMarkMove - nW + 1) && *(m_lpnMarkMove - nW + 1) != 0)
							{
								//标记<上>和<右上>等价   
								AttachEqualMark(tempEqualMark, *(m_lpnMarkMove - nW),
									*(m_lpnMarkMove - nW + 1), nEqualNum, lEqualMark);
							}
						}
						//<左>、<左上>、<上>未标记过,此时不存在等价关系   
						else
						{
							if (*(m_lpnMarkMove - nW + 1) != 0)
							{
								*m_lpnMarkMove = *(m_lpnMarkMove - nW + 1);      //跟随<右上>标记   
							}
							//<左>、<左上>、<上>、<右上>未标记过,则开始新的标记值   
							else
							{
								*m_lpnMarkMove = nMarkValue++;
							}


						}    //<左>、<左上>、<上>未标记过结束   
					}   //<左>、<左上>未标记过结束   
				}  //<左>未被标记过结束   
			}     // else 不需要标记   


			m_lpnMarkMove++;
			m_lpImgBitsMove++;
		}       //中间点处理的结束   


		//对每行的最后一个点做处理,总体就是对图象的最左列做处理   
		//此时存在<左>、<左上>、<上> 3种情况   


		//需要标记   
		if ((*m_lpImgBitsMove) == bObjectGray)
		{
			//<左>被标记过   
			if (*(m_lpnMarkMove - 1) != 0)
			{
				*m_lpnMarkMove = *(m_lpnMarkMove - 1);


				if (*(m_lpnMarkMove - 1) != *(m_lpnMarkMove - nW - 1) && *(m_lpnMarkMove - nW - 1) != 0)
				{
					//标记<左>、<左上>等价   
					AttachEqualMark(tempEqualMark, *(m_lpnMarkMove - 1),
						*(m_lpnMarkMove - nW - 1), nEqualNum, lEqualMark);
				}


				if (*(m_lpnMarkMove - 1) != *(m_lpnMarkMove - nW) && *(m_lpnMarkMove - nW) != 0)
				{
					//标记<左>、<上>等价   
					AttachEqualMark(tempEqualMark, *(m_lpnMarkMove - 1),
						*(m_lpnMarkMove - nW), nEqualNum, lEqualMark);
				}


			}
			//<左>未被标记过   
			else
			{
				if (*(m_lpnMarkMove - nW - 1) != 0)
				{
					*m_lpnMarkMove = *(m_lpnMarkMove - nW - 1);    //跟随<左上>   


					if (*(m_lpnMarkMove - nW - 1) != *(m_lpnMarkMove - nW) && *(m_lpnMarkMove - nW) != 0)
					{
						//标记<左上>、<上>等价   
						AttachEqualMark(tempEqualMark, *(m_lpnMarkMove - nW - 1),
							*(m_lpnMarkMove - nW), nEqualNum, lEqualMark);
					}


				}
				//<左>、<左上>未标记过   
				else
				{
					if (*(m_lpnMarkMove - nW) != 0)
					{
						*m_lpnMarkMove = *(m_lpnMarkMove - nW);    //跟随<上>标记   
					}
					//<左>、<左上>、<上>未标记过,则开始新的标记值   
					else
					{
						*m_lpnMarkMove = nMarkValue++;
					}


				}
			}
		}  //对每行的最后一个点做处理,总体就是对图象的最左列做处理   


	}     //"除第一行之外的标记"的结束   


	//因为在每次标记完之后,nMarkValue都会自动++   
	//所以要通过(-1)操作来记录所标记的最大的个数   


	nMaxMarkValue = nMarkValue - 1;
	/************************************************************************/
	/* 定义双层链表的外层链表,它的元素是一个指向内层链表的指针。
	内层链表的型别也是CptrList,其元素是标记值。 */
	CPtrList exList;
	CPtrList * pInnerList;
	POSITION posExElem;


	if (lEqualMark.GetCount() != 0)
	{
		// pInnerListAdd,每次向exList中添加的新元素   
		CPtrList * pInnerListAdd = new CPtrList;
		ASSERT(pInnerListAdd != NULL);


		/* 添加第一个等价对到exList的第一个元素所指向的InnerList中。  */
		pInnerListAdd->AddTail((void *)lEqualMark.GetHead().MarkValue1);
		pInnerListAdd->AddTail((void *)lEqualMark.GetHead().MarkValue2);
		exList.AddTail((void *)pInnerListAdd);
		lEqualMark.RemoveHead();


		/* 定义pFindValue1和pFindValue2, 存放在所有内层链表中找到特定值
		的某个内层链表的头指针,也就是外层链表的某个元素值。 */
		CPtrList * pFindValue1 = NULL;
		CPtrList * pFindValue2 = NULL;


		//整理剩余的等价对   
		while (!lEqualMark.IsEmpty())
		{
			posExElem = exList.GetHeadPosition();
			pFindValue1 = NULL;
			pFindValue2 = NULL;


			while (posExElem)
			{
				pInnerList = (CPtrList *)exList.GetAt(posExElem);
				if (pInnerList->Find((void *)lEqualMark.GetHead().MarkValue1))
				{
					pFindValue1 = pInnerList;
				}
				if (pInnerList->Find((void *)lEqualMark.GetHead().MarkValue2))
				{
					pFindValue2 = pInnerList;
				}
				exList.GetNext(posExElem);
			}


			//该等价对中两个值都在已经整理过的等价关系中   
			if (pFindValue1 && pFindValue2)
			{
				//当两个地址不一样时,对链表进行调整   
				if (pFindValue1 != pFindValue2)
				{
					pFindValue1->AddTail(pFindValue2);
					/* 清除链表元素,通过new得到的CptrList 类型,
					必须采用delete进行删除,否则会造成内存泄露。*/
					POSITION posDelete = exList.Find((void *)pFindValue2);
					pFindValue2->RemoveAll();
					delete pFindValue2;
					exList.RemoveAt(posDelete);
				}
			}
			/* 只在已经整理过的等价关系中找到Value1,
			那么将Vaule2加到Value1所在的链表中。 */
			else if (pFindValue1)
			{
				pFindValue1->AddTail((void *)lEqualMark.GetHead().MarkValue2);
			}
			else if (pFindValue2)
			{
				pFindValue2->AddTail((void *)lEqualMark.GetHead().MarkValue1);
			}
			/* 等价对中两个值在整理过的等价关系中都
			没有找到,则在exList中增加新元素。 */
			else
			{
				CPtrList * pInnerListAdd = new CPtrList;
				pInnerListAdd->AddTail((void *)lEqualMark.GetHead().MarkValue1);
				pInnerListAdd->AddTail((void *)lEqualMark.GetHead().MarkValue2);
				exList.AddTail((void *)pInnerListAdd);
			}
			//去掉此时等价对的头元素   
			lEqualMark.RemoveHead();
		}  // while ( !lEqualMark.IsEmpty() )循环结束   
	}  // if ( lEqualMark.GetCount() !=0 )语句结束   
	/* 等价对链表大小为0,说明第一次扫描之后没有产生等价对,标记已经完成。 */
	else
	{
		return TRUE;
	}


	/*等价关系整理完成,下面建立第一次扫描的标记值和
	第二次扫描的标记值之间的映射关系。*/


	int nTotalEqualNum = 0; //列入等价关系的标记个数   
	int nMarkRegion = 0;   //图像中连通区域个数   


	posExElem = exList.GetHeadPosition();
	while (posExElem)
	{
		pInnerList = (CPtrList *)exList.GetAt(posExElem);
		nTotalEqualNum += pInnerList->GetCount();
		exList.GetNext(posExElem);
	}
	nMarkRegion = nMaxMarkValue - nTotalEqualNum + exList.GetCount();


	/* 定义第一次扫描和第二次扫描之间的映射向量,要使用vector,
	应该#include <vector>并且使用std命名空间。 */
	vector<MarkMapping> vMarkMap(nMaxMarkValue);


	//初始化映射向量,令其做自身映射   
	for (i = 0; i < nMaxMarkValue; i++)
	{
		vMarkMap[i].nOriginalMark = i + 1;
		vMarkMap[i].nMappingMark = i + 1;
	}


	POSITION posInnerElem; //InnerList中元素的位置   
	int nMin;              //InnerList中最小值   
	int nIndex = 0;


	posExElem = exList.GetHeadPosition();
	/* while循环实现了如下功能:找到每个等价组中最小的标记值,
	然后将映射向量中nMappingMark设定为其所在等价组的最小的标记值。*/
	while (posExElem)
	{
		pInnerList = (CPtrList *)exList.GetAt(posExElem);
		nMin = (int)pInnerList->GetHead();
		posInnerElem = pInnerList->GetHeadPosition();
		pInnerList->GetNext(posInnerElem);


		while (posInnerElem)
		{
			if ((int)pInnerList->GetAt(posInnerElem) < nMin)
			{
				nMin = (int)pInnerList->GetAt(posInnerElem);
			}
			pInnerList->GetNext(posInnerElem);
		}


		/* 根据每组等价关系中的最小的标记值对Mapping向量做出调整。 */
		posInnerElem = pInnerList->GetHeadPosition();
		while (posInnerElem)
		{
			nIndex = (int)pInnerList->GetAt(posInnerElem) - 1;
			vMarkMap[nIndex].nMappingMark = nMin;
			pInnerList->GetNext(posInnerElem);
		}
		exList.GetNext(posExElem);
	}


	/* 将映射向量nMappingMark中不重复的部分找出并对其进行排序。
	使用find()和sort()这两种泛型算法,应该#include <algorithm>。*/
	vector <int> vSortMark(nMarkRegion); //排序向量   
	nIndex = 0;


	for (i = 0; i < nMaxMarkValue; i++)
	{
		if (find(vSortMark.begin(), vSortMark.end(), vMarkMap[i].nMappingMark)
			== vSortMark.end())
		{
			vSortMark[nIndex++] = vMarkMap[i].nMappingMark;
		}
	}
	sort(vSortMark.begin(), vSortMark.end());


	/* 根据排序后的标记在vSortMark向量中的位置,对映射向量做出重新调整。 */
	vector<int>::iterator itFind;
	vector<int>::iterator itBegin;
	itBegin = vSortMark.begin();


	for (i = 0; i < nMaxMarkValue; i++)
	{
		itFind = find(vSortMark.begin(), vSortMark.end(), vMarkMap[i].nMappingMark);
		vMarkMap[i].nMappingMark = (itFind - itBegin + 1);
	}


	//根据映射向量对标记数组进行调整   
	for (j = 0; j < nH; j++)
	{
		m_lpnMarkMove = m_lpnMark + j*nW;
		for (i = 0; i < nW; i++)
		{
			if (*m_lpnMarkMove != 0)
			{
				*m_lpnMarkMove = vMarkMap[*m_lpnMarkMove - 1].nMappingMark;
			}
			m_lpnMarkMove++;
		}
	}


	//删除链表结构中通过new得到的元素   
	posExElem = exList.GetHeadPosition();
	while (posExElem)
	{
		pInnerList = (CPtrList *)exList.GetAt(posExElem);
		pInnerList->RemoveAll();
		delete pInnerList;
		exList.GetNext(posExElem);
	}
	exList.RemoveAll();


	//通过类成员变量来记录连通区域的个数   
	m_nMarkNumbers = nMarkRegion;
	CString s;
	s.Format("连通区域个数为%d\n", nMarkRegion);
	AfxMessageBox(s);
	return (long)TRUE;

}

成功识别为6个

版权声明:

posted on 2015-05-31 22:21  moffis  阅读(344)  评论(0编辑  收藏  举报

导航