zoukankan      html  css  js  c++  java
  • 二值图像连通域标记算法

    二值图像连通域标记算法

    八邻域标记算法:

    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
    ", nMarkRegion);
    	AfxMessageBox(s);
    	return (long)TRUE;
    
    }

    成功识别为6个

    版权声明:

  • 相关阅读:
    朋友
    Music
    Rnadom Teams
    Bone Collector(01背包)
    Common Subsequence LCS
    Copying Books
    Equal Sum Sets
    Checker Challenge
    棋盘问题
    油田(Oil Deposits)
  • 原文地址:https://www.cnblogs.com/walccott/p/4957038.html
Copyright © 2011-2022 走看看