边缘检测的方法有
(1)使用边缘检测算子
(2)形态学操作,使用原图像和开闭运算得到的结果进行异或运算
(3)边缘跟踪,可以直接对灰度图片进行边缘跟踪
第一种情况使用边缘检测算子得到的边沿具有一定的厚度。形态学操作得到的边界具有单像素宽的优点。他们都可以对图片进行全局处理。但是无论哪种情况都要进行轮廓的跟踪来得到轮廓的数据才能进行下面的计算。
边缘跟踪我看的是陆宗骐的一本书,里面对边缘跟踪讲解的很详细。我大概说一下
边缘的记录有链码表和线段表2种,链码表适合计算周长,线段表容易计算面积以及相关的,他们之间可以相互的转换。
跟踪的原理我就不写了,太长,下面的文档里面有讲
二值图像目标邻域点法边界跟踪算法.PDF
二值图像中目标物体轮廓的边界跟踪算法.PDF
基于二值图像的目标边界跟踪算法及应用.PDF
一种基于优先搜索方向的边界跟踪算法.PDF
基于边界跟踪的区域面积计算.PDF
图像测量中的边界跟踪算法改进.PDF
网上的代码;
http://loren.cqthx.com/browse.aspx?Group=1下面是我的代码:写了2天的时间才完善点不过还有一下问题?:1.在一些极值的情况下,例如边界刚好和边缘重合的情况。 在这种情况下,需要对边缘方向进行判断。 如果只是想刚才那样进行极值处理,肯定只会在远处循环2.对于北京比较暗、边界亮度也不太高的图片需要预处理的图片的检测,如果还是用上面的处理方法,检测不到点的。要进行预处理才行3.增加4邻域 8邻域 左右看4.需改BOOL ImgDrawTrace(BITMAPINFO* pbmpinfo,BYTE* pbmpdata,int *code,int black_or_white)函数,通过最后一个参数来设置线条颜色遗留问题:搜索方向的改变以及搜索算法没有进行优化,可能会出现某个部分检测不到或者在某个角落里面进行死循环的问题。还有就是阈值背景比较暗的话不行/*******************************************************************************
边界跟踪寻找第一个边界点
图像信息
图像数据
阈值:默认检测的是边界具有低灰度,小于阈值确认是边界
///////
寻找方向从走到右 从上到下
///////
返回值 :失败时返回(0,0)
*******************************************************************************/
ImgDot ImgFindFirstDot(BITMAPINFO* pbmpinfo,BYTE* pbmpdata,int threshold)
{
LONG img_width=pbmpinfo->bmiHeader.biWidth;
LONG img_heigth=pbmpinfo->bmiHeader.biHeight;
LONG Linebyte =( pbmpinfo->bmiHeader.biBitCount * img_width +31)/32*4;
LONG ImageSize=Linebyte * img_heigth;
ImgDot temp;
temp.cx=0;
temp.cy=0;
if (pbmpdata == NULL)
{
return temp;
}
BYTE *psrc= pbmpdata;
for (LONG heigth = 0 ; heigth < img_heigth ;heigth++ )
{
for (LONG width = 0 ; width < img_width ; width++)
{
psrc = (pbmpdata + (ImageSize -Linebyte- heigth * Linebyte) + width);
if ((*psrc) <=threshold)
{
temp.cx = width;
temp.cy = heigth;
return temp;
}
}
}
return temp;
}
/***************************************************************************
轮廓跟踪之寻找下一个边界点
当前点坐标
得到的下一个点坐标,开始的方向必须指向背景像素
搜索下一个点的初始方向
阈值:默认检测的是边界具有低灰度,小于阈值确认是边界
使用的链码,是4链码还是8链码
搜索的方向
//返回值:
direction 是从当前点到下一个点的方向
TRUE :找到新的边界点
FALSE:该点为孤立点
****************************************************************************/
BOOL ImgNextDot(BITMAPINFO* pbmpinfo,BYTE* pbmpdata,ImgDot *current , ImgDot *next , char *direction,int threshold,int chain,int search_direction)
{
int inc[8][2]={{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1},{0,1},{1,1}}; //搜索方向与坐标
BOOL flag=FALSE;//搜索是否成功成功标志
int ns=*direction;
int temp_direction=0;
int gain=1;
int temp=0;
int x=current->cx;
int y=current->cy;
if (chain==IMG_CHAIN_4)
{
gain = 2;
}
if (chain==IMG_CHAIN_8)
{
gain = 1;
}
if (search_direction==IMG_SEARCH_DIR_RIGHT)//向右看齐
{
for (int nx=ns + 8; nx > ns ; nx -= gain)
{
temp_direction = nx % 8 ;
//得到方向
//这里出现的问题:当输入的点是0的时候,因为ImgDot里面定义的是无符号类型,就出现了溢出
//还没有解决极限情况
/*
3月8号
出现极值情况的时候,
*/
next->cx = x+ inc[temp_direction][0];
next->cy = y+ inc[temp_direction][1];
temp = ImgGetPixel(pbmpinfo,pbmpdata,*next);
if(temp == -1)
{
return FALSE;
}
if ( temp <= threshold)//检测到新边界点
break;
else
continue;
}
}
if (search_direction==IMG_SEARCH_DIR_LIGHT)//向左看齐
{
for (int nx = ns; nx < ns + 8 ; nx += gain)
{
temp_direction = nx % 8 ;
//得到方向
//这里出现的问题:当输入的点是0的时候,因为ImgDot里面定义的是无符号类型,就出现了溢出
//还没有解决极限情况
/*
3月8号
出现极值情况的时候,
*/
next->cx = x+ inc[temp_direction][0];
next->cy = y+ inc[temp_direction][1];
temp = ImgGetPixel(pbmpinfo,pbmpdata,*next);
if(temp == -1)
{
return FALSE;
}
if ( temp <= threshold)//检测到新边界点
break;
else
continue;
}
}
//这个判断必须是在初始化指针指向背景方向为前提
//3月9号
//如果使用4邻域的话会出现进入初始的方向指向的就是边界的情况,如果进行下面的判断,返回的值是0
//
// if (temp_direction != ns )
// {
*direction = temp_direction;
flag = TRUE; //搜索成功
// }
return flag;
}
/****************************************************************************************
单区域跟踪
参数:
图片信息
图片数据
初始化点
初始方向
链码数据
使用的链码,是4链码还是8链码
搜索的方向
///////
链码:
前两个数据是初始化坐标,第三个是链表总数,下面的是链码
/////
返回值:搜索到的点数 ,所有失败返回-1
****************************************************************************************/
int ImgSingleTrace(BITMAPINFO* pbmpinfo,BYTE* pbmpdata,ImgDot *Begin_Dot,char Begin_direction,int *code,int chain,int search_direction)
{
BOOL flag = TRUE;
code[0] = Begin_Dot->cx; //初始点x坐标
code[1] = Begin_Dot->cy; //初始点y坐标
code[2] = 3; //初始链表点数
code[3] = -1 ;
ImgDot *current_dot ;
ImgDot *next_dot ;
current_dot = new ImgDot;
next_dot = new ImgDot;
memcpy(current_dot,Begin_Dot,sizeof(ImgDot));
char temp_direction = Begin_direction;
int code_num=3;
while(flag)
{
if (ImgNextDot(pbmpinfo,pbmpdata,current_dot,next_dot,&temp_direction,200,chain,search_direction))
{
if (current_dot->cx == Begin_Dot->cx && current_dot->cy==Begin_Dot->cy && temp_direction==code[3] )//检测结束标志
{
break;
}
code[code_num++] = temp_direction ; //存储链表数据
memcpy(current_dot,next_dot,sizeof(ImgDot));
//current_dot = next_dot; //把这次检测到的数据作为下次检测的当前点
if (search_direction==IMG_SEARCH_DIR_RIGHT)//向右看齐
{
if (chain == IMG_CHAIN_4)
{
temp_direction =( temp_direction + 2) % 8;
}
if (chain == IMG_CHAIN_8)
{
temp_direction =( temp_direction + 3) % 8;
}
}
if (search_direction==IMG_SEARCH_DIR_LIGHT)//向右看齐
{
if (chain == IMG_CHAIN_4)
{
temp_direction =(temp_direction + 6) % 8;
}
if (chain == IMG_CHAIN_8)
{
temp_direction = (temp_direction + 6) % 8;
}
}
/* if (temp_direction%2) //改变方向???????????????????
{
temp_direction = (temp_direction + 1) % 8;
}
else
temp_direction = (temp_direction + 2) % 8;*/
}
else
{
return -1;
}
}
code[2] = code_num - 3;
delete current_dot;
delete next_dot;
//存储链表中链码的数目
return code[2];
}
/*************************************************************************************
画出轮廓
图像信息
图像数据
链码
线条的颜色
/////该函数不改变数据信息
***************************************************************************************/
BOOL ImgDrawTrace(BITMAPINFO* pbmpinfo,BYTE* pbmpdata,int *code,int black_or_white)
{
int inc[8][2]={{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1},{0,1},{1,1}}; //搜索方向与坐标
UINT img_width=pbmpinfo->bmiHeader.biWidth;
UINT img_heigth=pbmpinfo->bmiHeader.biHeight;
LONG Linebyte =( pbmpinfo->bmiHeader.biBitCount * img_width +31)/32*4;
LONG ImageSize=Linebyte * img_heigth;
if (black_or_white==IMG_BLACK)
{
memset(pbmpdata,255,ImageSize);
}
else
{
memset(pbmpdata,0,ImageSize);
}
int code_num = code[2];
ImgDot current_dot = {code[0],code[1]};
int temp;
for (int i = 0 ;i < code_num ; i++)
{
if (black_or_white==IMG_BLACK)
{
ImgSetPixel(pbmpinfo,pbmpdata,current_dot,0);
if (ImgGetPixel(pbmpinfo,pbmpdata,current_dot) != 0)
{
AfxMessageBox("!!");
return FALSE;
}
}
else
{
ImgSetPixel(pbmpinfo,pbmpdata,current_dot,255);
if (ImgGetPixel(pbmpinfo,pbmpdata,current_dot) != 255)
{
AfxMessageBox("!!");
return FALSE;
}
}
temp = code[i+3];
current_dot.cx = current_dot.cx + inc[temp][0];
current_dot.cy = current_dot.cy + inc[temp][1];
}
return TRUE;
}
tengjingshu_112148725 2009-3-10 09:03