www.pudn.com > watershed(VC++).rar > watershed.cpp


//算法实现代码如下  
 
/*==================================================================== 
函数名:          Watershed 
功能:            用标记-分水岭算法对输入图像进行分割 
算法实现:        无 
输入参数说明:    OriginalImage --输入图像(灰度图,0~255) 
                  SeedImage     --标记图像(二值图,0-非标记,1-标记) 
                  LabelImage    --输出图像(1-第一个分割区域,2-第二个分割区域,...) 
                  row           --图像行数 
                  col           --图像列数 
返回值说明:      无         
====================================================================*/ 
void WINAPI CDib::Watershed(unsigned char **OriginalImage, char** SeedImage, int **LabelImage, int row, int col) 
{ 
// using namespace std; 
  
 //标记区域标识号,从1开始 
 int Num=0; 
 int i,j; 
  
 //保存每个队列种子个数的数组 
 vector SeedCounts; 
 //临时种子队列 
 queue quetem; 
 //保存所有标记区域种子队列的数组,里面放的是种子队列的指针 
 vector*> vque; 
  
 int* array; 
 //指向种子队列的指针 
 queue *pque; 
 POINT temp; 
  
 for(i=0;i[256]; 
    //加入到队列数组中,对应的是本标记号Num的 
    vque.push_back(pque); 
    //当前点放入本标记区域的临时种子队列中 
    temp.x=i; 
    temp.y=j; 
    quetem.push(temp); 
    //当前点标记为已处理 
    LabelImage[i][j]=Num; 
    SeedImage[i][j]=127;//表示已经处理过 
     
    //让临时种子队列中的种子进行生长直到所有的种子都生长完毕 
    //生长完毕后的队列信息保存在vque中,包括区域号和灰阶,对应点数存储在seedcounts中 
    while(!quetem.empty()) 
    { 
     up=down=right=left=FALSE; 
     upleft=upright=downleft=downright=FALSE; 
     //队列中取出一个种子 
     temp=quetem.front(); 
     m=temp.x; 
     n=temp.y; 
     quetem.pop(); 
     //注意到127对扫描过程的影响,影响下面的比较,但是不影响while语句中的扫描 
      
     if(m>0) 
     { 
      //上方若为可生长点则加为新种子 
      if(SeedImage[m-1][n]==1) 
      { 
       temp.x=m-1; 
       temp.y=n; 
       quetem.push(temp);//如果这样的话,那么这些标记过的区域将再次在while循环中被扫描到,不会,因为值是127 
       //新种子点标记为已淹没区域,而且是当前区域,并记录区域号到labelImage 
       LabelImage[m-1][n]=Num; 
       SeedImage[m-1][n]=127; 
      } 
      else//否则上方为不可生长 
      { 
       up=TRUE; 
      } 
     } 
     if(m>0&&n>0) 
     { 
      if(SeedImage[m-1][n-1]==1)//左上方若为可生长点则加为新种子 
      { 
       temp.x=m-1; 
       temp.y=n-1; 
       quetem.push(temp); 
       //新种子点标记为已淹没区域,即下一个循环中以127来标识不再扫描,而且是当前区域 
       LabelImage[m-1][n-1]=Num; 
       SeedImage[m-1][n-1]=127; 
      } 
      else//否则左上方为不可生长 
      { 
       upleft=TRUE; 
      } 
     } 
      
     if(m0&&n<(col-1)) 
     { 
      if(SeedImage[m-1][n+1]==1)//右上方若为可生长点则加为新种子 
      { 
       temp.x=m-1; 
       temp.y=n+1; 
       quetem.push(temp); 
       //新种子点标记为已淹没区域,而且是当前区域 
       LabelImage[m-1][n+1]=Num; 
       SeedImage[m-1][n+1]=127; 
      } 
      else//否则右上方为不可生长 
      { 
       upright=TRUE; 
      } 
     } 
      
     if(n>0) 
     { 
      if(SeedImage[m][n-1]==1)//左方若为可生长点则加为新种子 
      { 
       temp.x=m; 
       temp.y=n-1; 
       quetem.push(temp); 
       //新种子点标记为已淹没区域 
       LabelImage[m][n-1]=Num; 
       SeedImage[m][n-1]=127; 
      } 
      else//否则左方为不可生长 
      { 
       left=TRUE; 
      } 
     } 
     if(m<(row-1)&&n>0) 
     { 
      if(SeedImage[m+1][n-1]==1)//左下方若为可生长点则加为新种子 
      { 
       temp.x=m+1; 
       temp.y=n-1; 
       quetem.push(temp); 
       //新种子点标记为已淹没区域 
       LabelImage[m+1][n-1]=Num; 
       SeedImage[m+1][n-1]=127; 
      } 
      else//否则左方为不可生长 
      { 
       downleft=TRUE; 
      } 
     } 
      
     //上下左右只要有一点不可生长,那么本点为初始种子队列中的一个 
     //这里可否生长是由seedimage中的值来决定的。 
     if(up||down||right||left|| 
      upleft||downleft||upright||downright) 
     { 
      temp.x=m; 
      temp.y=n; 
      //下面这个矢量数组:第一维是标记号;第二维是该图像点的灰度级 
      //m,n点对应的是while循环里面扫描的像素点。 
      //Num是当前的区域号 
      //这样这个二维信息就表示了,某个区域中对应某个灰度级对应的成员点的集合与个数 
      //分别由下面两个量来表达 
      vque[Num-1][OriginalImage[m][n]].push(temp);//这两句中我把Num-1改成了Num...pan's codes... 
      SeedCounts[Num-1][OriginalImage[m][n]]++; 
     }      
                }//while结束,扫描到quetem为空而止。也就是对应所有的节点都得到不可生长为止(或者是周围的点要么不可生长,要么已生长) 
            }//if结束 
  // if(Num==5) 
   // return; 
        } 
    } 
 //在上述过程中,如果标记的点为0则表示,没有扫描到的点,或者表明不是输入的种子点 
 //这里相当于是找seedimage传过来的初始区域的分水岭界线的所有的点;并且用标号记录每个区域,同时集水盆的边缘点进入队列。 
 //上面是找集水盆的程序。同时也是连通区域。  
 
/*************************************/ 
 //test 这里测试一下剩下的非水盆地的点数。 
 int seednum; 
 for(i=0;i0) 
     { 
      SeedCounts[i][WaterLevel]--;//取出一个点,个数少一 
      temp=vque[i][WaterLevel].front();//取出该区域的一个点,清空这个边缘点,表示当前 
                                      //灰度级该像素已经处理掉了。 
      vque[i][WaterLevel].pop(); 
      m = temp.x; 
      n = temp.y;//当前种子的坐标 
      if(m>0) 
      { 
       if(!LabelImage[m-1][n])//上方若未处理,表示没有标号,应该在输入前已经作过初始化为0 
                           //本函数中在开头也作过初始化 
       { 
        temp.x=m-1; 
        temp.y=n; 
        LabelImage[m-1][n]=i+1;//上方点标记为已淹没区域 
                               //注意到这个标记是与扫描点的区域号相同,一定在这个标号所属的区域吗?是的 
                               //这样在下一轮至少会扫描到这个点,确保不遗漏,但是下一轮的处理会使它合理 
                               //归类吗?问题还有这样标记并没有一定将它加入到种子队列。也就是说它 
                               //只是被淹没而不能向上淹没。只有满足下述可生长条件才行。 
        if(OriginalImage[m-1][n]<=WaterLevel)//上方若为可生长点则加入当前队列,当前高度的队列 
        { 
         vque[i][WaterLevel].push(temp); 
        } 
        else//否则加入OriginalImage[m-1][n]对应的灰度级的队列,为什么? 
        { 
         vque[i][OriginalImage[m-1][n]].push(temp); 
         SeedCounts[i][OriginalImage[m-1][n]]++; 
        } 
       } 
      } 
       
      if(m0) 
      { 
       if(!LabelImage[m][n-1])//左边若未处理 
       { 
        temp.x=m; 
        temp.y=n-1; 
        LabelImage[m][n-1]=i+1;//左边点标记为已淹没区域 
         
        if(OriginalImage[m][n-1]<=WaterLevel)//左边若为可生长点则加入当前队列 
        { 
         vque[i][WaterLevel].push(temp); 
        } 
        else//否则加入OriginalImage[m][n-1]级队列 
        { 
         vque[i][OriginalImage[m][n-1]].push(temp); 
         SeedCounts[i][OriginalImage[m][n-1]]++; 
        } 
       } 
      } 
     }//while循环结束 
     SeedCounts[i][WaterLevel]=vque[i][WaterLevel].size(); 
    }//if结束 
   }//for循环结束 
  }//while循环结束 
    }//for循环结束  
 
 
/********************************** 
//test whether the origional segmentation num is changed... 
 int nonzeronum=Num; 
 for(m=0;m