基于幅度的阈值分割方法
直接固定阈值法
就是选择一个阈值,对图像进行二值化处理,如果当图像中的像素值小于该阈值时,可以置零或255,相反的,当图像中的像素值大于该阈值时,可以置255或0. 总之,图像分割后的图像是二值的,就是只有0和255.
自适应阈值法
基本思路就是,对图像中的每个像素,选取以它为中心的一个领域窗口(比如8邻域窗口),对这个窗口的像素灰度按照一定的准则来选取阈值,以此判断该处理像素是属于目标还是背景。而选取阈值的方法还有很多的,比如可以选择窗口中各像素的灰度的最大值和最小值的均值,或窗口内所有像素灰度的均值等。
基于区域生长的阈值分割方法
主要就是有一个生长门限,然后把当前点和它的四邻域点相比较,如果四邻域点中有和当前点的灰度值相差小于生长门限的,那么这个点将会成为下一个生长的起始点。当然,这个生长的起始点我们都叫做种子点,如果种子点选取的不适当的话,生长的结果将会差别很大的。后面的截图中会有更加明显的区别。
Visual C++6.0的程序实现直接固定阈值法
可以写出下面的函数:
/******************************************************************************
*函数功能:实现固定阈值分割算法
*函数声明:
BOOL FixedThreshold(
BYTE* image, -指向源图像的像素数据的指针
LONG imageWidth, -源图像的宽度
LONG imageHeight, -源图像的高度
int threshold, -分割门限
int nBitCount -源图像的位数
)
******************************************************************************/
BOOL FixedThreshold(BYTE* image,LONG imageWidth,LONG imageHeight,int threshold,
int nBitCount=8)
{
if(nBitCount==8)
{
for(int i=0;i<imageHeight;i++)
{
for(int j=0;j<imageWidth;j++)
{
BYTE gray=*(image+imageWidth*i+j);
if(gray<=threshold)
{
*(image+imageWidth*i+j)=0;
}
else
{
*(image+imageWidth*i+j)=255;
}
}
}
}
else
{
AfxMessageBox("暂时只能处理8位位图");
return false;
}
return true;
}
自适应阈值分割法
程序如下:
/******************************************************************************
*函数功能:实现自适应阈值分割算法
*函数声明:
BOOL AutoThreshold(
BYTE* image, -指向源图像的像素数据的指针
LONG imageWidth, -源图像的宽度
LONG imageHeight, -源图像的高度
int nBitCount -源图像的位数
)
******************************************************************************/
BOOL AutoThreshold(BYTE* image,LONG imageWidth,LONG imageHeight,int nBitCount=8)
{
if(nBitCount==8)
{
//新建一个缓存的图像内存
BYTE* dst=new BYTE[imageWidth*imageHeight];
memset(dst,255,imageWidth*imageHeight);
//保存像素点的8个相邻的像素点的值
BYTE nw,n,ne,w,e,sw,s,se;
//保存3×3窗口中所有元素的均值
int avg;
//保存处理的像素点的地址
unsigned char* src;
//为防止越界,除去图形的上下左右各1列
//采用的是8邻域窗口,取8个像素的均值作为处理点的门限
for(int i=1;i<imageHeight-1;i++)
{
avg=0;
for(int j=1;j<imageWidth-1;j++)
{
src=image+imageWidth*i+j;
BYTE gray=*(src); //要处理的像素点
n=*(src+imageWidth); //上邻点
nw=*(src+imageWidth-1); //左上邻点
ne=*(src+imageWidth+1); //右上邻点
w=*(src-1); //左邻点
e=*(src+1); //右邻点
s=*(src-imageWidth); //下邻点
sw=*(src-imageWidth-1); //左下邻点
se=*(src-imageWidth+1); //右下邻点
avg=(n+nw+ne+w+e+s+sw+se)/8;
//判断当前点的像素值
if(gray<=avg)
{
*(dst+imageWidth*i+j)=0;
}
}
}
memcpy(image,dst,imageWidth*imageHeight);
delete[] dst;
}
else
{
AfxMessageBox("只能处理8位位图");
return false;
}
return true;
}
区域生长的分割法
/******************************************************************************
*函数功能:实现区域生长的分割算法
*函数声明:
BOOL RegionGrow(
BYTE* srcImage, -指向源图像的像素数据的指针
LONG imageWidth, -源图像的宽度
LONG imageHeight, -源图像的高度
int nThreshold, -区域生长的门限
int seedx, -区域生长种子的横坐标
int seedy, -区域生长种子的纵坐标
int mode, -区域生长种子的选取类型
int nBitCount -源图像的位数
)
******************************************************************************/
BOOL RegionGrow(BYTE* srcImage,LONG imageWidth,LONG imageHeight,int nThreshold,
int seedx,int seedy,int mode=0,int nBitCount=8)
{
//设置用于处理思邻域的数组,便于下面的循环运算
static int Dx[]={-1,0,1,0};
static int Dy[]={0,1,0,-1};
if(nBitCount==8)
{
//新分配一块内存
BYTE* dstImage=new BYTE[imageHeight*imageWidth];
//全部置255(白色)
memset(dstImage,255,imageHeight*imageWidth);
//判断是何种种子选取类型
CPoint seed;
if(mode==0)
{
//默认以图像中心为种子点
seed.x=imageWidth/2;
seed.y=imageHeight/2;
}
else
{
//自定义种子点
if(seedx>imageWidth || seedy>imageHeight)
{
AfxMessageBox("种子点在图像之外");
return false;
}
else
{
seed.x=seedx;
seed.y=seedy;
}
}
//定义存储X坐标的堆栈
int* GrowX=new int[imageHeight*imageWidth];
//定义存储Y坐标的堆栈
int* GrowY=new int[imageWidth*imageHeight];
//定义堆栈的起始点
int start=0;
//定义堆栈的终止点
int end=0;
GrowX[end]=seed.x;
GrowY[end]=seed.y;
CPoint current;
int xx,yy;
while(start<=end)
{
current.x=GrowX[start];
current.y=GrowY[start];
//对当前点的四邻域进行判断
for(int k=0;k<4;k++)
{
xx=current.x+Dx[k];
yy=current.y+Dy[k];
unsigned char* lpSrc=srcImage+imageWidth*(imageHeight-yy)+xx;
unsigned char* lpSrc1=srcImage+imageWidth*(imageHeight-current.y)+current.x;
unsigned char* lpDst=dstImage+imageWidth*(imageHeight-yy)+xx;
if(xx<imageWidth && xx>=0 && yy<imageHeight && yy>=0
&& *lpDst==255 && abs(*lpSrc-*lpSrc1)<nThreshold)
{
end++;
GrowX[end]=xx;
GrowY[end]=yy;
*lpDst=0;
}
}
start++;
}
//复制目的图像到源图像中
memcpy(srcImage,dstImage,imageHeight*imageWidth);
//删除目的图像
delete[] dstImage;
//删除堆栈
delete[] GrowY;
delete[] GrowX;
}
else if(nBitCount==24)
{
AfxMessageBox("暂时不能处理24位位图");
return false;
}
else
{
AfxMessageBox("暂时只能处理8位位图");
return false;
}
return true;
}
程序运行时截图
原始图片:
附加说明由于我在自适应阈值分割时采用的3×3的窗口,所以分割后可以看出明显的矩形。但是为了更平滑的话,你可以选择将图片划分为左上、左下、右上和右下等四个区域,然后取每个区域内所有像素灰度的均值作为该区域的门限,这样进行的阈值分割会有不错的效果。当然图片不一样,我们应当采用适当的方法。轮廓提取算法:如果源图像中有一点为黑,且它的8个相邻点都是黑色时,则将该点删除。当然了,处理前,如果图像不是二值的图像需要先将图像二值化,然后进行轮廓提取。Visual C++6.0算法实现/******************************************************************************
*函数功能:实现二值图像的轮廓提取
*函数声明:
* BOOL OutLine(
* BYTE* image, -指向源图像的像素数据的指针
* LONG imageWidth, -源图像的宽度(必须是4的倍数)
* LONG imageHeight, -源图像的高度
* int nBitCount -源图像的位数(默认为8位)
* )
******************************************************************************/
BOOL OutLine(BYTE* srcImage,LONG imageWidth,LONG imageHeight,int nBitCount=8)
{
//新建一个和源图像大小相同的内存空间
BYTE* dstImage=new BYTE[imageWidth*imageHeight];
//初始化新的内存空间为全255
memset(dstImage,(BYTE)255,imageWidth*imageHeight);
if(nBitCount==8)
{
//如果不是二值图像,需先转换成二值图像,选定的变换阈值是120
for(int i=0;i<imageHeight;i++)
{
for(int j=0;j<imageWidth;j++)
{
BYTE gray=*(srcImage+imageWidth*i+j);
if(gray>=120)
{
*(srcImage+imageWidth*i+j)=255;
}
else
{
*(srcImage+imageWidth*i+j)=0;
}
}
} //轮廓提取:如果源图像中有一点为黑,且它的8个相邻的点都是黑色时,则将此点删除
//为防止越界,宽度和高度的上下左右各留出一行
//存放处理像素的八个相邻的像素点
unsigned char n,e,s,w,ne,se,nw,sw;
for(i=1;i<imageHeight-1;i++)
{
for(int j=1;j<imageWidth-1;j++)
{
unsigned char* src=srcImage+imageWidth*i+j;
unsigned char* dst=dstImage+imageWidth*i+j;
if(*(src)==0)
{
*dst=0; //目标图像的相应像素也设置为黑点
w=*(src-1); //左邻点
if(w!=0) continue;
e=*(src+1); //右邻点
if(e!=0) continue;
nw=*(src+imageWidth-1); //左上邻点
if(nw!=0) continue;
n=*(src+imageWidth); //上邻点
if(n!=0) continue;
ne=*(src+imageWidth+1); //右上邻点
if(ne!=0) continue;
sw=*(src-imageWidth-1); //左下邻点
if(sw!=0) continue;
s=*(src-imageWidth); //下邻点
if(s!=0) continue;
se=*(src-imageWidth+1); //右下邻点
if(se!=0) continue;
*dst=255;
}
}
} //复制轮廓提取后的图像
memcpy(srcImage,dstImage,imageWidth*imageHeight);
//删除目的图像
delete[] dstImage;
}
else if(nBitCount==24)
{
AfxMessageBox("对不起,这部分还没做完");
return false;
}
else
{
AfxMessageBox("只能处理8或24位位图");
return false;
} return true;
}程序运行时截图