其中_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;//将轮廓点数量保存在第一个空间点的状态位
}