分享

种子填充-区域增长算法

 启蒙彩魂 2011-04-05
区域增长的算法实现: 1)根据图像的不同应用选择一个或一组种 子,它或者是最亮或最暗的点,或者是位 于点簇中心的点 2...通过像素集合的区域增长 算法实现: 区域A 区域B 种子像素增长.3)增长的规则 4)
结束条件.

*************************************************************************
*
* \\函数名称:
*   RegionGrow()
*
* \\输入参数:
*   CDib * pDib     - 指向CDib类的指针,含有原始图象信息
*   unsigned char * pUnRegion  - 指向区域生长结果的指针
*
* \\返回值:
*   无
*
* \\说明:
*   pUnRegion指针指向的数据区存储了区域生长的结果,其中1(逻辑)表示
*  对应象素为生长区域,0表示为非生长区域
*   区域生长一般包含三个比较重要的问题:
*  1. 种子点的选取
*  2. 生长准则
*  3. 终止条件
*  可以认为,这三个问题需要具体分析,而且每个问题解决的好坏直接关系到
*  区域生长的结果。
*  本函数的种子点选取为图像的中心,生长准则是相邻象素的象素值小于
*  nThreshold, 终止条件是一直进行到再没有满足生长准则需要的象素时为止
*
*************************************************************************
*/
void RegionGrow(CDib * pDib, unsigned char * pUnRegion, int nThreshold)
{
 static int nDx[]={-1,0,1,0};
 static int nDy[]={0,1,0,-1};
 // 遍历图象的纵坐标
// int y;
 // 遍历图象的横坐标
// int x;
 // 图象的长宽大小
 CSize sizeImage  = pDib->GetDimensions();
 int nWidth   = sizeImage.cx  ;
 int nHeight   = sizeImage.cy  ;
 // 图像在计算机在存储中的实际大小
 CSize sizeImageSave = pDib->GetDibSaveDim();
 // 图像在内存中每一行象素占用的实际空间
 int nSaveWidth = sizeImageSave.cx;
 // 初始化
 memset(pUnRegion,0,sizeof(unsigned char)*nWidth*nHeight);
 // 种子点
 int nSeedX, nSeedY;
 // 设置种子点为图像的中心
 nSeedX = nWidth /2 ;
 nSeedY = nHeight/2 ;
 // 定义堆栈,存储坐标
 int * pnGrowQueX ;
 int * pnGrowQueY ;
 
 // 分配空间
 pnGrowQueX = new int [nWidth*nHeight];
 pnGrowQueY = new int [nWidth*nHeight];
 // 图像数据的指针
 unsigned char *  pUnchInput =(unsigned char * )pDib->m_lpImage;
 
 // 定义堆栈的起点和终点
 // 当nStart=nEnd, 表示堆栈中只有一个点
 int nStart ;
 int nEnd   ;
 //初始化
 nStart = 0 ;
 nEnd   = 0 ;
 // 把种子点的坐标压入栈
 pnGrowQueX[nEnd] = nSeedX;
 pnGrowQueY[nEnd] = nSeedY;
 // 当前正在处理的象素
 int nCurrX ;
 int nCurrY ;
 // 循环控制变量
 int k ;
 // 图象的横纵坐标,用来对当前象素的4邻域进行遍历
 int xx;
 int yy;
 while (nStart<=nEnd)
 {
  // 当前种子点的坐标
  nCurrX = pnGrowQueX[nStart];
  nCurrY = pnGrowQueY[nStart];    
  // 对当前点的4邻域进行遍历
  for (k=0; k<4; k++)
  {
   // 4邻域象素的坐标
   xx = nCurrX+nDx[k];
   yy = nCurrY+nDy[k];
  
   // 判断象素(xx,yy) 是否在图像内部
   // 判断象素(xx,yy) 是否已经处理过
   // pUnRegion[yy*nWidth+xx]==0 表示还没有处理
   // 生长条件:判断象素(xx,yy)和当前象素(nCurrX,nCurrY) 象素值差的绝对值
   if ( (xx < nWidth) && (xx>=0) && (yy<nHeight) && (yy>=0)
        && (pUnRegion[yy*nWidth+xx]==0)  && abs(pUnchInput[yy*nSaveWidth+xx] - pUnchInput[nCurrY*nSaveWidth+nCurrX])<nThreshold )
   {
    // 堆栈的尾部指针后移一位
    nEnd++;
    // 象素(xx,yy) 压入栈
    pnGrowQueX[nEnd] = xx;
    pnGrowQueY[nEnd] = yy;
    // 把象素(xx,yy)设置成逻辑1(255)
    // 同时也表明该象素处理过
    pUnRegion[yy*nWidth+xx] = 255 ;
   }
  }
  nStart++;
 }
 // 释放内存
 delete []pnGrowQueX;
 delete []pnGrowQueY;
    pnGrowQueX = NULL ;
 pnGrowQueY = NULL ;
}

对于2D图象的组织增长,使用递归也是一种不错的选择,但需要注意栈空间需要设大一些。
而在3D数据场上,递归几乎是不可行的,栈空间经常会出现溢出的情况,因此不具备实用性。
2D组织增长伪代码如下
组织增长(Image* pImage, int i, ing j, byte* mask)
{
   if 不满足增长条件(pImage, i,j, mask)
       return;
   设置标记(mask, i, j);
   组织增长(pImage, i-1, j, mask);
   组织增长(pImage, i+1, j, mask);
   组织增长(pImage, i, j-1, mask);
   组织增长(pImage, i, j+1, mask);
}
至于将递归程序改为迭代程序,可以看一看《程序设计方法学》
区域增长算法递归实现
void RegionGrowTwo(int nSeedX, int nSeedY, BYTE * pUnchInput,BYTE * D, int nWidth, int nHeight, BYTE * pUnRegion,int &iLeft,int & iRight,int & iTop,int & iBottom)
{
  int nDx[] = {-1,1,0,0};
 int nDy[] = {0,0,-1,1}; 
 int k=0;
 int nCurrX ;
 int nCurrY ;
 int xx=0,yy=0;
 nCurrX = nSeedX;
 nCurrY = nSeedY;
 if(nCurrX<iLeft)
  iLeft = nCurrX;
 if(nCurrX>iRight)
  iRight = nCurrX;
 if(nCurrY<iTop)
  iTop = nCurrY;
 if(nCurrY>iBottom)
  iBottom = nCurrY;
 
//  pUnRegion[nCurrY*nWidth+nCurrX] = 255 ;
     // 对当前点的4邻域进行遍历
     int times = 0;
  for (k=0; k<4; k++)
  {
   // 4邻域象素的坐标
   xx = nCurrX+nDx[k];
   yy = nCurrY+nDy[k];
   // 判断象素(xx,yy) 是否在图像内部
   // 判断象素(xx,yy) 是否已经处理过
   // pUnRegion[yy*nWidth+xx]==0 表示还没有处理
   // 生长条件:判断象素(xx,yy)和当前象素(nCurrX,nCurrY) 象素值差的绝对值
   if ( (xx < nWidth) && (xx>=0) && (yy>=0) && (yy<nHeight)
   && (pUnRegion[yy*nWidth+xx]==0) && (pUnchInput[yy*nWidth+xx]==1))
   {
    // 同时也表明该象素处理过
    pUnRegion[yy*nWidth+xx] = 255 ;
    if(xx<iLeft)
     iLeft = xx;
    if(xx>iRight)
     iRight = xx;
    if(yy<iTop)
     iTop = yy;
    if(yy>iBottom)
     iBottom = yy;
                
    RegionGrowTwo(xx,yy,pUnchInput,D,nWidth,nHeight,pUnRegion,iLeft,iRight,iTop,iBottom); 
   }
   else
    times++;
  }
}
/*
*  区域增长,递归实现
* S,源图象
  D,目标图象
 ImageWidth,ImageHeight,表示图象的宽、高
*/
void  RegionGrowOne(BYTE *S,BYTE *D,int ImageWidth,int ImageHeight)
{
 int iLeft=0,iRight=0,iTop=0,iBottom=0;
 int k1,k2,k3,k4,ii1=0,off=0;
 int i=0,j=0;
 LPBYTE lpFlag = new BYTE[ImageWidth*ImageHeight];
 memset(lpFlag,0,ImageWidth*ImageHeight);
 memcpy(D,S,ImageWidth*ImageHeight);
 for (i=0; i<ImageHeight; i++)
 {
  for (j=0; j<ImageWidth; j++)
  {
   if (S[i*ImageWidth+j] == 1 && lpFlag[i*ImageWidth+j] == 0)
   {
    iLeft=65535,iRight=0,iTop=65535,iBottom=0;
    RegionGrowTwo(j,i,S,D,ImageWidth,ImageHeight,lpFlag,iLeft,iRight,iTop,iBottom);
    if((iRight-iLeft)>40 && (iBottom-iTop)>40)  //表示区域大于40*40时就画出边框
    {
     //画边框
     k1 = (iLeft -1 )<0 ?0:(iLeft -1 );
     k2 = (iRight+1)>=ImageWidth?(ImageWidth-1):(iRight+1);
     k3 = (iTop-1)<0?0:(iTop-1);
     k4 = (iBottom+1)>=ImageHeight?(ImageHeight-1):(iBottom+1);
     for(ii1 = k1;ii1 <= k2;ii1++)
     {
      off = ii1 + k3*ImageWidth;
      D[off] = 11;
      off = ii1 + k4*ImageWidth;
      D[off] = 11;
     }
     for(ii1 = k3 ;ii1<=k4;ii1++)
     {
      off = ii1 * ImageWidth + k1;
      D[off] = 11;
      off = ii1 * ImageWidth + k2;
      D[off] = 11;
     }
     /////////////////////////////////////////////////
    }
   }
  }
 
 }
 if(lpFlag!=NULL)
 {
  delete []lpFlag;
  lpFlag = NULL;
 }
}
                                                       标准源码

BOOL RegionGrow(int nSeedX, int nSeedY, BYTE * pUnchInput,int nWidth, int nHeight, BYTE * pUnRegion,CRect &R)
{
  int nDx[] = {-1,1,0,0};
  int nDy[] = {0,0,-1,1};
 int nSaveWidth = nWidth;
 
 // 定义堆栈,存储坐标
 int * pnGrowQueX ;
 int * pnGrowQueY ;
 // 分配空间
 pnGrowQueX = new int [nWidth*nHeight];
 pnGrowQueY = new int [nWidth*nHeight];
 // 定义堆栈的起点和终点
 // 当nStart=nEnd, 表示堆栈中只有一个点
 int nStart ;
 int nEnd ;
 //初始化
 nStart = 0 ;
 nEnd = 0 ;
 // 把种子点的坐标压入栈
 pnGrowQueX[nEnd] = nSeedX;
 pnGrowQueY[nEnd] = nSeedY;
 // 当前正在处理的象素
 int nCurrX ;
 int nCurrY ;
 // 循环控制变量
 int k ;
 // 图象的横纵坐标,用来对当前象素的8邻域进行遍历
 int xx;
 int yy;
 while (nStart<=nEnd)
 {
  // 当前种子点的坐标
  nCurrX = pnGrowQueX[nStart];
  nCurrY = pnGrowQueY[nStart];
  // 对当前点的4邻域进行遍历
  for (k=0; k<4; k++)
  {
   // 4邻域象素的坐标
   xx = nCurrX+nDx[k];
   yy = nCurrY+nDy[k];
   // 判断象素(xx,yy) 是否在图像内部
   // 判断象素(xx,yy) 是否已经处理过
   // pUnRegion[yy*nWidth+xx]==0 表示还没有处理
   // 生长条件:判断象素(xx,yy)和当前象素(nCurrX,nCurrY) 象素值差的绝对值
   if ( (xx < nWidth) && (xx>=0) && (yy>=0) && (yy<nHeight)
   && (pUnRegion[yy*nWidth+xx]==0) && (pUnchInput[yy*nSaveWidth+xx]==1))
   {
    // 堆栈的尾部指针后移一位
    nEnd++;
    // 象素(xx,yy) 压入栈
    pnGrowQueX[nEnd] = xx;
    pnGrowQueY[nEnd] = yy;
    // 把象素(xx,yy)设置成逻辑1(255)
    // 同时也表明该象素处理过
    pUnRegion[yy*nWidth+xx] = 255 ;
   }
  }
  nStart++;
 }
   
 
 //找出区域的范围
    int nMinx=pnGrowQueX[0], nMaxx=pnGrowQueX[0], nMiny=pnGrowQueY[0], nMaxy = pnGrowQueY[0];
    for (k=0; k<nEnd; k++)
 {
        if (pnGrowQueX[k] > nMaxx)
             nMaxx = pnGrowQueX[k];
       if (pnGrowQueX[k] < nMinx)
            nMinx = pnGrowQueX[k];
       if (pnGrowQueY[k] > nMaxy)
            nMaxy = pnGrowQueY[k];
       if (pnGrowQueY[k] < nMiny)
           nMiny = pnGrowQueY[k];
 }
    if ((nMaxy - nMiny) > 40 && (nMaxx - nMinx) > 40)
 {
    R.left = nMinx;
    R.right = nMaxx;
    R.top = nMiny;
    R.bottom = nMaxy;
       return TRUE;
 }
    // 释放内存
 delete []pnGrowQueX;
 delete []pnGrowQueY;
 pnGrowQueX = NULL ;
 pnGrowQueY = NULL ;
 return FALSE;
}
//调用方法
void OnButton(LPBYTE S,int ImageWidth,int ImageHeight)
{
 int i=0,j=0;
CRect rect;
 LPBYTE lpFlag = new BYTE[ImageWidth*ImageHeight];
 memset(lpFlag,0,ImageWidth*ImageHeight);
 for (i=0; i<ImageHeight; i++)
 {
  for (j=0; j<ImageWidth; j++)
  {
   if (S[i*ImageWidth+j] == 1 && lpFlag[i*ImageWidth+j] == 0)
   {
    RegionGrow(j, i, S, ImageWidth, ImageHeight, lpFlag,rect);
   }
  }
 
 }
 if(lpFlag!=NULL)
 {
  delete []lpFlag;
  lpFlag = NULL;
 }
}
 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多