八領域邊界追蹤演算法

BusyMonkey發表於2015-01-08

八領域邊界追蹤 C語言

其中_Binary為輸入的bmp二值化影象,_TracingPtr為輪廓空間點(型別為結構體)

typedef struct Contours
{
	int height;
	int width;
	int stateFlag;
} contour;

void edgeTracing( const unsigned char* _Binary, contour** _TracingPtr, const int _Width, const int _Height )
{
	unsigned char* _Mark = NULL;//標記矩陣
	int direction[8][2] = { 0,-1, -1,-1, -1,0, -1,1, 0,1, 1,1, 1,0, 1,-1 };//方向陣列
	int i = 0, j = 0;//行、列計數
	int start_I = 0, start_J = 0;//輪廓的起始點
	int rec_I = 0, rec_J = 0;//待檢測輪廓點座標
	int mov_I = 0, mov_J = 0;//待檢測輪廓點周圍8領域逆時針旋轉點
	int tmp_I = 0, tmp_J = 0;//臨時點儲存
	int point_Count = 0;//輪廓點計數
	int direc_Count = 0;//方向轉次數計數
	int rot_Direct = 0;//方向陣列計數
	int stepWidth = (_Width + 3) & ~3;//灰度圖行步長
	int contour_Succe_Flag = 0;//輪廓尋找成功標誌位
	int contour_Point_Now = 0;//現已開闢輪廓空間點數量
	int contour_Point_Add = 0;//需要增加的輪廓空間點數量
	contour* ptr_Contour_First = NULL;//輪廓點陣列首地址


	contour_Point_Now = 4 * (_Width+_Height);
	contour_Point_Add = _Width + _Height;
	*_TracingPtr = (  contour *)malloc( contour_Point_Now * sizeof( contour) );//為儲存輪廓點建立記憶體塊
	ptr_Contour_First = *_TracingPtr;
	_Mark = ( unsigned char*)malloc( stepWidth*_Height );//輪廓標記矩陣
	memset(_Mark, 0, stepWidth*_Height);//輪廓標記矩陣清零
	for ( i = 0; i < _Height; i ++ )
	{
		for ( j = 1; j < _Width; j ++ )
		{
			if ( 255 == _Binary[ i*stepWidth + j - 1] && 0 == _Binary[ i*stepWidth + j] && 0 == _Mark[i*stepWidth + j] )
			{
				direc_Count = 0;//方向計數置零
				rot_Direct = 0;//旋轉方向計數置零
				start_I = i;//儲存每個輪廓的起始點
				start_J = j;
				rec_I = i;//儲存第一個b點的行座標
				rec_J = j;//儲存第一個b點的列座標
				mov_I = i;//儲存第一個c點的行座標
				mov_J = j-1;//儲存第一個c點的列座標
				while ( direc_Count < 8 && mov_I > 0 && mov_I < _Height && mov_J > 0 && mov_J < _Width )
				{
					direc_Count ++;
					rot_Direct ++;//順時針方向轉45°
					if ( 8 == rot_Direct) rot_Direct = 0;//使方向迴圈且不溢位
					mov_I = rec_I + direction[rot_Direct][0];//轉完之後c點的行座標
					mov_J = rec_J + direction[rot_Direct][1];//轉完之後c點的列座標
					if ( 0 == _Binary[ mov_I*stepWidth + mov_J]
						&& mov_I > 0 && mov_I < _Height
						&& mov_J > 0 && mov_J < _Width
						&& ( start_I != mov_I || start_J != mov_J ) )
					{
						if ( 0 == rot_Direct ) rot_Direct = 7;//方向回到上一個角度
						else rot_Direct --;
						
						tmp_I = rec_I;//記錄變換前b點行座標
						tmp_J = rec_J;//記錄變換前b點列座標
						rec_I = mov_I;//新的c點座標賦值給b點
						rec_J = mov_J;
						mov_I = tmp_I + direction[rot_Direct][0];//將上一個c點座標賦值給新c點座標
						mov_J = tmp_J + direction[rot_Direct][1];

						direc_Count = 0;//方向次數計數清零

						if ( rec_I > mov_I )//計算現在c點相對b點的方位
							rot_Direct = 2;
						else if ( rec_I < mov_I )
							rot_Direct = 6;
						else if ( rec_J > mov_J )
							rot_Direct = 0;
						else if ( rec_J < mov_J )
							rot_Direct = 4;
					}
					else if ( start_I == mov_I && start_J == mov_J )
					{
						//如果等於起始點,則重新遍歷該輪廓,標記輪廓並儲存輪廓點
						if ( point_Count > contour_Point_Now-1 )
						{
							//若輪廓空間點超出範圍,則加入額外輪廓空間點
							ptr_Contour_First = (  contour *)malloc( contour_Point_Now * sizeof( contour) );
							memcpy( ptr_Contour_First, *_TracingPtr, contour_Point_Now * sizeof( contour) );
							*_TracingPtr = (  contour *)malloc( (contour_Point_Now + contour_Point_Add) * sizeof( contour) );
							memcpy( *_TracingPtr, ptr_Contour_First, contour_Point_Now * sizeof( contour) );
							contour_Point_Now += contour_Point_Add;
							free( ptr_Contour_First);
							ptr_Contour_First = NULL;
							ptr_Contour_First = *_TracingPtr;
						}
						
						ptr_Contour_First[ point_Count].height = start_I;//儲存起始點
						ptr_Contour_First[ point_Count].width = start_J;
						ptr_Contour_First[ point_Count].stateFlag = 1;//狀態標誌位stateFlag:
																	  //1為起始點,0為普通邊緣點,2為結束點
						_Mark[ start_I*stepWidth + start_J] = 255;//標記為已搜尋過的點

						direc_Count = 0;//方向計數置零
						rot_Direct = 0;//旋轉方向計數置零
						rec_I = start_I;//儲存b點的行座標
						rec_J = start_J;//儲存b點的列座標
						mov_I = start_I;//儲存 c點的行座標
						mov_J = start_J-1;//儲存c點的列座標
						while ( direc_Count < 8 && mov_I > 0 && mov_I < _Height && mov_J > 0 && mov_J < _Width )
						{
							direc_Count ++;
							rot_Direct ++;//順時針方向轉45°
							if ( 8 == rot_Direct) rot_Direct = 0;//使方向迴圈且不溢位
							mov_I = rec_I + direction[rot_Direct][0];//轉完之後c點的行座標
							mov_J = rec_J + direction[rot_Direct][1];//轉完之後c點的列座標
							if ( 0 == _Binary[ mov_I*stepWidth + mov_J]
								&& mov_I > 0 && mov_I < _Height
								&& mov_J > 0 && mov_J < _Width
								&& ( start_I != mov_I || start_J != mov_J ) )
							{
								if ( 0 == rot_Direct ) rot_Direct = 7;//方向回到上一個角度
								else rot_Direct --;

								tmp_I = rec_I;//記錄變換前b點行座標
								tmp_J = rec_J;//記錄變換前b點列座標
								rec_I = mov_I;//新的c點座標賦值給b點
								rec_J = mov_J;
								mov_I = tmp_I + direction[rot_Direct][0];//將上一個c點座標賦值給新c點座標
								mov_J = tmp_J + direction[rot_Direct][1];

								direc_Count = 0;//方向次數計數清零

								if ( rec_I > mov_I )//計算現在c點相對b點的方位
									rot_Direct = 2;
								else if ( rec_I < mov_I )
									rot_Direct = 6;
								else if ( rec_J > mov_J )
									rot_Direct = 0;
								else if ( rec_J < mov_J )
									rot_Direct = 4;

								point_Count ++;//壓入新點之前,輪廓計數自加

								if ( point_Count > contour_Point_Now-1 )
								{
									//若輪廓空間點超出範圍,則加入額外輪廓空間點
									ptr_Contour_First = (  contour *)malloc( contour_Point_Now * sizeof( contour) );
									memcpy( ptr_Contour_First, *_TracingPtr, contour_Point_Now * sizeof( contour) );
									*_TracingPtr = (  contour *)malloc( (contour_Point_Now + contour_Point_Add) * sizeof( contour) );
									memcpy( *_TracingPtr, ptr_Contour_First, contour_Point_Now * sizeof( contour) );
									contour_Point_Now += contour_Point_Add;
									free( ptr_Contour_First);
									ptr_Contour_First = NULL;
									ptr_Contour_First = *_TracingPtr;
								}

								ptr_Contour_First[ point_Count].height = rec_I;//儲存新輪廓點
								ptr_Contour_First[ point_Count].width = rec_J;
								ptr_Contour_First[ point_Count].stateFlag = 0;//狀態標誌位stateFlag:
																			  //1為起始點,0為普通邊緣點,2為結束點
								_Mark[ rec_I*stepWidth + rec_J] = 255;//標記為已搜尋過的點
							}
							else if ( start_I == mov_I && start_J == mov_J )
							{
								//如果等於起始點,則把之前最後一個點改為終點,跳出開始新的搜尋
								ptr_Contour_First[ point_Count].stateFlag = 2;//狀態標誌位stateFlag:
																			  //1為起始點,0為普通邊緣點,2為結束點
								contour_Succe_Flag = 1;//輪廓完整,標誌位置一
								point_Count ++;//為下一個輪廓開闢一個新點
								break;
							}
						}						
					}

					if ( contour_Succe_Flag )//如果一個輪廓搜尋並儲存成功,則開始找新的起始點
					{
						contour_Succe_Flag = 0;//輪廓搜尋成功標誌位置零
						break;
					}
				}
			}
		}
	}
	ptr_Contour_First[0].stateFlag = point_Count;//將輪廓點數量儲存在第一個空間點的狀態位
}


相關文章