分水岭算法简单实现

转自http://blog.csdn.net/tt2com/article/details/6321610

一直没看分水岭,vc图像处理的书对该算法的介绍也很少,cximage也找到相关的算法。就看了matlab何opencv。matlab对分水岭的算法是封装的也就没看源码,而opencv的写的实在太高升了,水平差在加没有过opencv,没办法看懂。网上找了几个算法的源码,一个老外用template写的,一个是国人写的。老外写的是NB,看不动,国人写的看了一些也是云里雾里的,最后么办法自己想办法写了个。

 

首先声明,这个只是简单的实现,仅仅针对一些简单的情况。效果还可以但有不足,一些地方的分割有可能将原本的单一前景给分割掉。和我一样的菜鸟,可以看看。高人路过可以指导。

 

先介绍下什么叫分水岭,

定义:

所谓图像分割是指将图像中具有特殊涵义的不同区域区分开来,这些区域是互相不交叉的,每一个区域都满足特定区域的一致性

本代码,没有完全的实现,只能满足现行对工作的需要。

分水岭的原理网上有很多,我仅仅说明下我对这个算法的简单理解:该算法可以解决针对,你可以用分割成前景,但有些干扰或者其他原因,分割出来的前景可以连成一片,无法区分那个前景是那个。这个时候,就可以用这个分水岭算法来帮忙。

(先看几张分水岭原理的图片,这个打字太麻烦,直接看图片吧)

 

算法的理解我花了半天多的时间才看明白。我的理解(可能有错误或者不足)就是M为灰度值最小(或者最大相对背景)为最低的盆地,其实也就是各个前景最为远离背景的地方(这个肯定有不足的理解,希望各位看客指导一下)。根据这个盆地我们不断的加水,使得盆地的水位不断的提升(也就是不断的靠近背景),在个提升地盘的时候,可能会出现和其他盆地相冲突的地方。这个时候我们需要本着和谐的态度来防止这种事情的发生,就是在冲突的地方建设堤坝。这个堤坝其实就是我们需要寻找的东西。

提升盆地水位的时候有三种可能

1:盆地上方实力,也就说这个区域的灰度和该盆地相邻,和其他势力没关系。这种情况,肯定是被吞并的。

2:盆地相邻实力,也就说这个区域的灰度不仅仅和该盆地相邻,也和其他的盆地有关系。这种情况,就是我们需要寻找的堤坝。

3:当水位提到一定高度的时候出现的新盆地,该地区和原始的盆地均不关。该水位点为该区域最低水位。这个时候就有新的盆地生成,也就是新的恶势力出现了。

针对上面的理解,我写了个依据图像距离变化的分水岭,适合一些小部分区域相交叉的情况。

首先我们需要对图像进行二值化,分割出感兴趣的位置区域。二值化,大家随便,只要能分割进行。

第二进行距离变换,代码来自一个vc图像算法原理和实现,大概这个书名吧

  1. //计算二值化图像的距离值  
  2. bool processing::distance(BYTE *&pdata)  
  3. {  
  4.       
  5.       
  6.     // 存储象素值的数组  
  7.     if (pdata!=NULL)  
  8.     {  
  9.         return false;  
  10.     }  
  11.     int * pnBinary = new int[m_size];  
  12.     int * pnStore    = new int[m_size];   
  13.     pdata = new BYTE[m_size];  
  14.     // 将图象二值化  
  15.     for (int j = 0; j < m_height; ++j)  
  16.     {  
  17.         for(int i = 0; i < m_biwidth; ++i)  
  18.         {  
  19.             // 白色象素为背景,存成0  
  20.             if(m_pbinary[i+j*m_biwidth])  
  21.             {  
  22.                 pnBinary[i+j*m_biwidth] = 0;  
  23.                 pnStore [i+j*m_biwidth] = 0;  
  24.             }  
  25.             // 黑象素存成1  
  26.             else  
  27.             {  
  28.                 pnBinary[i+j*m_biwidth] = 1;  
  29.                 pnStore [i+j*m_biwidth] = 1;  
  30.             }  
  31.         }         
  32.     }  
  33.   
  34.     int s = 1;  
  35.   
  36.     while(s == 1)  
  37.     {  
  38.         s = 0;  
  39.   
  40.         // 进行距离的累加  
  41.         for (int j = 1; j < m_height - 1; ++j)  
  42.         {  
  43.             for(int i = 1; i < m_biwidth - 1; ++i)  
  44.             {  
  45.   
  46.                 // 如果是背景,进行下一个循环  
  47.                 if(pnBinary[i+j*m_biwidth]== 0)  
  48.                     continue;  
  49.   
  50.                 // 如果当前象素值和四周的值一样,象素值增加一  
  51.                 if(pnBinary[i+j*m_biwidth]==pnBinary[m_biwidth* (j-1) + i] && pnBinary[i+j*m_biwidth]==pnBinary[m_biwidth * (j+1) + i])  
  52.                     if(pnBinary[i+j*m_biwidth]==pnBinary[m_biwidth * j + i-1] && pnBinary[i+j*m_biwidth]==pnBinary[m_biwidth* j + i+1])  
  53.                     {  
  54.                         pnStore[m_biwidth * j + i]++;  
  55.                         s=1;                                  
  56.                     }  
  57.             }  
  58.         }  
  59.   
  60.         // 在进行下一轮循环前将当前的结果储存  
  61.         for (int j = 0; j < m_height; j++)  
  62.             for(int i = 1; i < m_biwidth; i++)  
  63.                 pnBinary[m_biwidth * j + i] = pnStore[m_biwidth * j + i];  
  64.     }  
  65.   
  66.     for (int j = 0; j < m_height; ++j)  
  67.     {  
  68.         for(int i = 0; i < m_biwidth; ++i)  
  69.         {  
  70.             //pdata[i+j*m_biwidth] =max(0,((25 - pnStore[j * m_biwidth + i]) * 10 + 5)); //  
  71.             pdata[i+j*m_biwidth]=max(0,255 - min(255,pnBinary[i+j*m_biwidth]));  
  72.         }         
  73.     }  
  74.   
  75.     delete pnStore;  
  76.     delete pnBinary;  
  77.   
  78.     return true;  
  79. }  

这个函数是写在一个类里面的所以,一些图像的基本参数为类的成员。对这个函数我就不再说明了。

  1. //分水岭处理二值化距离图像  
  2. bool processing::Watershed(BYTE* Data,int * hist)  
  3. {  
  4.     //修改成try机制  
  5.     if (Data==NULL||hist==NULL)  
  6.     {  
  7.         return false;  
  8.     }  
  9.     queue<POINT > *statistic = new queue<POINT>[255];//记录同级盆地队列,背景点不处理  
  10.     int * wdata = new int[m_size];   
  11.     memset(wdata,255,m_size*sizeof(int));  
  12.     bool cue = true;//变化标记  
  13.     int start_queue=0;//标记起始队列  
  14.     int lab_cout=256;//盆地标号  
  15.   
  16.     POINT pt;  
  17.       
  18.     const int WSHED = 255;//水坝  
  19.     //所有数据压入队列//边间点不进行处理  
  20.     for (int i=1;i<m_biwidth-1;++i)  
  21.     {  
  22.         for (int j=1;j<m_height-1;++j)  
  23.         {  
  24.             wdata[i+j*m_biwidth]=Data[i+j*m_biwidth];  
  25.             if (Data[i+j*m_biwidth]<255)  
  26.             {  
  27.                 pt.x=i;  
  28.                 pt.y=j;  
  29.                 statistic[Data[i+j*m_biwidth]].push(pt);  
  30.             }             
  31.         }  
  32.     }  
  33.     //完成寻找第一低位盆地起始水位  代码说明1  
  34.     for(;start_queue<256&&hist[start_queue]==0;++start_queue);  
  35.       
  36.     //完成寻找所有起始水位盆地      代码说明2  
  37.     while (hist[start_queue]>0)  
  38.     {  
  39.         pt=statistic[start_queue].front();  
  40.         statistic[start_queue].pop();  
  41.         wdata[pt.x+pt.y*m_biwidth] = lab_cout;  
  42.         --hist[start_queue];  
  43.         cue = true;  
  44.         while(cue)  
  45.         {  
  46.             cue = false;  
  47.             int size = (int)statistic[start_queue].size();  
  48.             for (int i=0;i<size;++i)  
  49.             {  
  50.                 int t;  
  51.                 pt=statistic[start_queue].front();  
  52.                 statistic[start_queue].pop();  
  53.                 //8个方向寻找lab  
  54.                 t = wdata[pt.x+(pt.y-1)*m_biwidth];  
  55.                 if (t>255)  
  56.                 {  
  57.                     wdata[pt.x+pt.y*m_biwidth]=lab_cout;  
  58.                     --hist[start_queue];  
  59.                     cue = true;  
  60.                     continue;  
  61.                 }  
  62.                 t = wdata[pt.x-1+(pt.y-1)*m_biwidth];  
  63.                 if (t>255)  
  64.                 {  
  65.                     wdata[pt.x+pt.y*m_biwidth]=lab_cout;  
  66.                     --hist[start_queue];  
  67.                     cue = true;  
  68.                     continue;  
  69.                 }  
  70.                 t = wdata[pt.x-1+pt.y*m_biwidth];  
  71.                 if (t>255)  
  72.                 {  
  73.                     wdata[pt.x+pt.y*m_biwidth]=lab_cout;  
  74.                     --hist[start_queue];  
  75.                     cue = true;  
  76.                     continue;  
  77.                 }  
  78.                 t = wdata[pt.x-1+(pt.y+1)*m_biwidth];  
  79.                 if (t>255)  
  80.                 {  
  81.                     wdata[pt.x+pt.y*m_biwidth]=lab_cout;  
  82.                     --hist[start_queue];  
  83.                     cue = true;  
  84.                     continue;  
  85.                 }  
  86.                 t = wdata[pt.x+(pt.y+1)*m_biwidth];  
  87.                 if (t>255)  
  88.                 {  
  89.                     wdata[pt.x+pt.y*m_biwidth]=lab_cout;  
  90.                     --hist[start_queue];  
  91.                     cue = true;  
  92.                     continue;  
  93.                 }  
  94.                 t = wdata[pt.x+1+(pt.y+1)*m_biwidth];  
  95.                 if (t>255)  
  96.                 {  
  97.                     wdata[pt.x+pt.y*m_biwidth]=lab_cout;  
  98.                     --hist[start_queue];  
  99.                     cue = true;  
  100.                     continue;  
  101.                 }  
  102.                 t = wdata[pt.x+1+(pt.y)*m_biwidth];  
  103.                 if (t>255)  
  104.                 {  
  105.                     wdata[pt.x+pt.y*m_biwidth]=lab_cout;  
  106.                     --hist[start_queue];  
  107.                     cue = true;  
  108.                     continue;  
  109.                 }  
  110.                 t = wdata[pt.x+1+(pt.y-1)*m_biwidth];  
  111.                 if (t>255)  
  112.                 {  
  113.                     wdata[pt.x+pt.y*m_biwidth]=lab_cout;  
  114.                     --hist[start_queue];  
  115.                     cue = true;  
  116.                     continue;  
  117.                 }  
  118.                 statistic[start_queue].push(pt);//没有处理到继续压入队列  
  119.             }  
  120.               
  121.               
  122.         }  
  123.         ++lab_cout;  
  124.     }  
  125.     //完成寻找盆地和涨水过程//背景不处理   
  126.       
  127.     for (int k=start_queue+1;k<255;++k)  
  128.     {  
  129.           
  130.         while (hist[k]>0)  
  131.         {  
  132.             cue = true;  
  133.             while(cue)  
  134.             {  
  135.                 cue = false;  
  136.                 int size = (int)statistic[k].size();  
  137.                 for (int i=0;i<size;++i)  
  138.                 {  
  139.                     int t;  
  140.                     int lab=0;  
  141.                     pt=statistic[k].front();  
  142.                     statistic[k].pop();  
  143.                     //8个方向寻找lab  
  144.                     t = wdata[pt.x+(pt.y-1)*m_biwidth];  
  145.                     if (t>255)  
  146.                     {  
  147.                         if (lab == 0) lab = t;  
  148.                         else if( t != lab ) lab = WSHED;  
  149.                           
  150.                     }  
  151.                     t = wdata[pt.x-1+(pt.y-1)*m_biwidth];  
  152.                     if (t>255)  
  153.                     {  
  154.                         if (lab == 0) lab = t;  
  155.                         else if( t != lab ) lab = WSHED;  
  156.                           
  157.                     }  
  158.                     t = wdata[pt.x-1+pt.y*m_biwidth];  
  159.                     if (t>255)  
  160.                     {  
  161.                         if (lab == 0) lab = t;  
  162.                         else if( t != lab ) lab = WSHED;  
  163.                           
  164.                     }  
  165.                     t = wdata[pt.x-1+(pt.y+1)*m_biwidth];  
  166.                     if (t>255)  
  167.                     {  
  168.                         if (lab == 0) lab = t;  
  169.                         else if( t != lab ) lab = WSHED;  
  170.                           
  171.                     }  
  172.                     t = wdata[pt.x+(pt.y+1)*m_biwidth];  
  173.                     if (t>255)  
  174.                     {  
  175.                         if (lab == 0) lab = t;  
  176.                         else if( t != lab ) lab = WSHED;  
  177.                           
  178.                     }  
  179.                     t = wdata[pt.x+1+(pt.y+1)*m_biwidth];  
  180.                     if (t>255)  
  181.                     {  
  182.                         if (lab == 0) lab = t;  
  183.                         else if( t != lab ) lab = WSHED;  
  184.                           
  185.                     }  
  186.                     t = wdata[pt.x+1+(pt.y)*m_biwidth];  
  187.                     if (t>255)  
  188.                     {  
  189.                         if (lab == 0) lab = t;  
  190.                         else if( t != lab ) lab = WSHED;  
  191.                           
  192.                     }  
  193.                     t = wdata[pt.x+1+(pt.y-1)*m_biwidth];  
  194.                     if (t>255)  
  195.                     {  
  196.                         if (lab == 0) lab = t;  
  197.                         else if( t != lab ) lab = WSHED;  
  198.                           
  199.                     }  
  200.                     if (lab == WSHED)  
  201.                     {  
  202.                         --hist[k];  
  203.                         wdata[pt.x+pt.y*m_biwidth]=WSHED;  
  204.                         continue;  
  205.                     }  
  206.                     else  
  207.                     {  
  208.                         if (lab == 0)  
  209.                         {  
  210.                             statistic[k].push(pt);//没有处理到继续压入队列  
  211.                         }  
  212.                         if (lab>255)  
  213.                         {  
  214.                             wdata[pt.x+pt.y*m_biwidth]=lab;  
  215.                             --hist[k];  
  216.                             cue = true;  
  217.                         }  
  218.                     }  
  219.                       
  220.                 }  
  221.             }  
  222.             if (!statistic[k].size())  
  223.             {  
  224.                 pt=statistic[k].front();  
  225.                 statistic[k].pop();  
  226.                 wdata[pt.x+pt.y*m_biwidth] = ++lab_cout;;  
  227.                 --hist[k];  
  228.             }  
  229.               
  230.         }  
  231.   
  232.           
  233.     }  
  234.       
  235.     for (int i=0;i<m_biwidth;++i)  
  236.     {  
  237.         for (int j=0;j<m_height;++j)  
  238.         {  
  239.             if (wdata[i+j*m_biwidth]>255)  
  240.             {  
  241.                 Data[i+j*m_biwidth] = 0;  
  242.             }  
  243.             else  
  244.                 Data[i+j*m_biwidth] = wdata[i+j*m_biwidth];  
  245.         }  
  246.     }  
  247.     delete []statistic;  
  248.     return true;  
  249. }  

这个分水岭有二个参数,第一是有二值得到的距离数据,第二个是这个距离数据的直方图统计。

对直方图的统计来决定循环的次数,当一个灰度被处理,如被水位吞没或者成为堤坝,均认为被处理。

说明1:首先找到直方图统计中最低的灰度做为起点水位

说明2:根据这个水位来寻找所以的盆地。此时的盆地就2中情况。一独立,二和相关的盆地相连接。由于是根据直方图统计来做的所以不知道是否连通。所以会出现如下情况,第一在8个方向能找到起始的盆地,那么我们将这个点淹没做为盆地,并将记录标记设置为真。二8方向没有盆地标记,那么其有可能是此刻盆地的势力范围,但暂时没有发展过来,或者和此刻的盆地无关。那么当标记为真的情况均处理完毕的时候,说明低水位的第一个盆地已经找完了。剩下的是这个水位下的其他盆地。此时我们增加一个盆地标记,开始寻找第二个盆地。如何循环直到所有的初始水位盆地均被找出来。

...(太晚了明天再接着写)

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值