二值图像的边缘追踪(内边缘与外边缘)
近段时间来,在学习图像处理的课程。使用Lab Windows/CVI为平台。自己学过一些C语言,不过对于CVI可是第一次接触,虽说它是C语言环境,可不是纯C的。举个例子:变量的定义不可以在switch...case语句里面。
对图像进行处理,首先要先将其二值化。二值化的关键是在于阈值的选择。CVI中利用IMAQ里的IPI_Threshold可以很容易实现二值化,阈值的选择可以根据图像的直方图选择其峰谷的值的作为阈值。在此不多说,有关资料查IMAQ。
图像二值化后(目标灰度值为128,背景灰度值为0),要对其进行边缘追踪。首先要对其进行区域标记,不同的区域标记不同的值(非背景与目标灰度值)。
区域标记方生长法,可以对8领域进行标记也可以,使用4领域也行。使用4领域的算法如下:
int CVICALLBACK Labeling_image (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
int x,y,startx,starty;
int width,height;
float gray_level;
IPIImageInfo Image_Info;
switch (event)
{
case EVENT_COMMIT:
IPI_GetImageInfo(TempImage,&Image_Info);
width=Image_Info.width;
height=Image_Info.height;
LabelledImage=TempImage;
for (y=0;y<height;y++)
for(x=0;x<width;x++)
{
IPI_GetPixelValue(TempImage,x,y,&gray_level); //得到原图像点(x,y)的像素值
if(gray_level==128.0)
{
startx=x;
starty=y;
FloodFill4(startx,starty,gray_level,20+10*AreaNum);//4领域区域生长法
AreaNum++;//连通区域数目
}
}
IPI_SetWindowAttribute(3,ATTR_LEFT,100);
IPI_SetWindowAttribute(3,ATTR_TOP,15);
IPI_WindDraw(LabelledImage,3,"标记图像",TRUE);
break;
}
return 0;
}
区域标记完后,就可以进行边界追踪。外边界追踪比较容易,日前已经有不少算法,如虫随法,光栅扫描法。本人 使用的是虫随法。而对于内边界追踪则比较难,在网上没有找到有用的算法。所以本人自己写算法。经过我的研究,我对内边界追踪也使用虫随法。思想如下:先找 到目标区域的最小外接矩形,然后在这个矩形区域里面搜索内边界点(这是最难的,我花费最久时间在这方面),找到后,由于内边界是被目标区域包围的,所以这 时把目标区域看成背景,把被内边界包围的区域看成目标,这样用虫随法就可以很容易的把内边界追踪出来。另外还有一个内边界追踪算法,如下:
内边界跟踪算法
通过对二值图像进行图像腐蚀合相减处理, 生成了目标物
新体内部的零域。根据封闭空间的连通性及下述的内边界点跟踪
原则, 在当前点八邻域内寻找邻接“1”点的“0”点, 进行跟踪搜
索并标记为“- 1”, 为下一步外边界跟踪做准备。
(1) 根据外边界起始点在其八邻域内寻找内部“0”点作为
内边界起始点 Ei1 并标记;
e)各候选方向点权值
(2) 在当前点八邻域内按顺时针寻找首个顺序邻接“1”点
图 2 边界跟踪步骤演示
的“0”点作为下一个内边界点 Eik;
(3) 若新边界点 Eik=Ei1,即回到了内边界起始点,当次跟踪结束;
(4) 若新边界点 Eik≠Ei1,则以 Eik 作为当前点,对其标记,
象,然后转(2)。
程序代码如下:
int CVICALLBACK MultiTrace (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
IPIImageInfo Image_Info;
int x,y,k,startx,starty,secondx,secondy,width,height;
int flag,nowx,nowy,lastx,lasty,nextx,nexty,direction,nextdirection;
int ddirection;//内边界方向
int x1,x2,y1,y2;//矩形区域四角
int firstdirection;
float gray_level;
int ii,i,j,now_x,now_y; //循环计数
float gray_leveled,bounded[8];
int num=0,black=0,white=0,gray=0;
flag=0;
switch (event)
{
case EVENT_COMMIT:
IPI_SetWindowAttribute(2,ATTR_LEFT,150);
IPI_SetWindowAttribute(2,ATTR_TOP,250);
IPI_GetImageInfo(LabelledImage,&Image_Info);
width=Image_Info.width;
height=Image_Info.height;
// IPI_SetImageSize(MultiTraceImage,width,height);
MultiTraceImage=LabelledImage;
for (k=0;k<AreaNum;k++)
{//区域个数
for (y=0;y<height;y++)
{
for(x=0;x<width;x++)
{
IPI_GetPixelValue(LabelledImage,x,y,&gray_level);
if(gray_level==20+10*k)
{
startx=x;
starty=y;
flag=1;
break;
}
}
if(flag) break;
} //找到起始边界点
IPI_SetPixelValue(MultiTraceImage,x,y,255);
lastx=startx; lasty=starty;
direction=2;
do
{
flag=0;
do
{
direction=(direction+1)%8;
nowx=lastx+direction_code[direction].dx;
nowy=lasty+direction_code[direction].dy;
flag=flag+1;
if(startx==nowx&&starty==nowy) flag=8;
if(flag==8) break;
IPI_GetPixelValue(LabelledImage,nowx,nowy,&gray_level);
}while (gray_level==0.0);
if(flag==8) break;
direction=(direction+5)%8;
secondx=lastx;
secondy=lasty;
lastx=nowx;
lasty=nowy;
IPI_SetPixelValue(MultiTraceImage,nowx,nowy,255);
IPI_WindDraw(MultiTraceImage,2,"多区域边缘追踪图像",TRUE);
}
while(!(startx==secondx&&starty==secondy&&nextx==nowx&&nexty==nowy));
//追踪完外边界后
//扫描整副图像,找出目标的矩形区域
for (y=0;y<height;y++)
for(x=0;x<width;x++)
{
IPI_GetPixelValue(MultiTraceImage,x,y,&gray_level);
if(gray_level==20+10*k)
{ y2=y;break;}
}
for (y=height;y>0;y--)
for(x=0;x<width;x++)
{
IPI_GetPixelValue(MultiTraceImage,x,y,&gray_level);
if(gray_level==20+10*k)
{ y1=y;break;}
}
for(x=0;x<width;x++)
for (y=0;y<height;y++)
{
IPI_GetPixelValue(MultiTraceImage,x,y,&gray_level);
if(gray_level==20+10*k)
{ x2=x;break;}
}
for(x=width;x>0;x--)
for (y=0;y<height;y++)
{
IPI_GetPixelValue(MultiTraceImage,x,y,&gray_level);
if(gray_level==20+10*k)
{ x1=x;break;}
}
// 结束矩形区域的查找
//追踪内边界
flag=0; //
for (j=y1;j<y2;j++)
for(i=x1;i<x2;i++)
{
black=0;white=0;gray=0;
IPI_GetPixelValue(MultiTraceImage,i,j,&gray_leveled);
/////////// //判断点(x,y)是不是内部点
if(gray_leveled==20+10*k)
{
for (ii=0;ii<8;ii++)
{
now_x=i+direction_code[ii].dx;
now_y=j+direction_code[ii].dy;
IPI_GetPixelValue(MultiTraceImage,now_x,now_y,&bounded[ii]);
if(bounded[ii]==0) black=1;
else if(bounded[ii]==20+10*k) gray=1;
else if(bounded[ii]==255) white=1;
}
if(black==1&&gray==1&&white!=1) //如果某点的8领域有背景点(blace)和目标点(gray)且没有外边界点(white),则它为内边界点
{
startx=i;
starty=j;
flag=1;
IPI_SetPixelValue(MultiTraceImage,startx,starty,255);
lastx=startx; lasty=starty;
ddirection=2;//设定搜索起始方向为2
do
{ flag=0;
do
{
ddirection=(ddirection+1)%8;
nowx=lastx+direction_code[ddirection].dx;
nowy=lasty+direction_code[ddirection].dy;
flag=flag+1;
if(startx==nowx&&starty==nowy) flag=8;
if(flag==8) break;
IPI_GetPixelValue(MultiTraceImage,nowx,nowy,&gray_level);
}while (gray_level==20+10*k);
if(flag==8) break;
ddirection=ddirection+5;
secondx=lastx;
secondy=lasty;
lastx=nowx;
lasty=nowy;
IPI_SetPixelValue(MultiTraceImage,nowx,nowy,255);
IPI_WindDraw(MultiTraceImage,2,"多区域边缘追踪图像",TRUE);
} while(!(startx==secondx&&starty==secondy&&nextx==nowx&&nexty==nowy));
} // if(black==1&&gray==1&&white!=1)
} // if(gray_leveled==20+10*k) 判断内部点结束
} // //追踪内边界结束
flag=0;
} //for (k=0;k<AreaNum;k++)
break;
}
return 0;
}