基于幅度的阈值分割方法

直接固定阈值法

 

 

  就是选择一个阈值,对图像进行二值化处理,如果当图像中的像素值小于该阈值时,可以置零或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;
}程序运行时截图