区域分离与合并

上一篇博客讨论的是由一组种子点来生长区域。另一种方法是首先将一幅图像细分为一组任意的不相交的区域,然后聚合或者分离这些区域。

这里先介绍区域分割的基本概念,虽然抽象,但仔细琢磨一下还是可以理解的。

 

令R表示整幅图像区域,并选择一个属性P.对R进行分割的一种方法是一次将他细分为越来越小的四象限区域,以便对于任何区域Ri,有P(Ri)=TRUE。我们从整个区域开始,如果P(Ri)=TRUE,我们把图像分割为四象限区域。若对于每个象限区域P为FALSE,则将该象限区域再细分为四个子象限区域,以此类推。这种特殊的分离技术可方便地表示为四叉树的形式,即每个节点正好有四个后代。

注意:树根M对应于整幅图像,而每个节点对应于该节点的四个细分后代节点。上图只细分了M3.

前面的讨论可以总结为如下过程:

1、将满足P(Ri)=FALSE的任何区域Ri,分离为4个不相连的象限区域

2、当不可能进一步分离时,聚合满足条件P(R_{i}\ \bigcup R_{j})=TRUE的任意两个相邻区域Ri与Rj

3、当无法进一步聚合时停止操作

 

/ 代码
// 区域分裂合并的图像分割
// nOffSetLne是行偏移量
 
// 由于分裂的层数太多了, 使用递归将使内存空间堆栈溢出
// 解决方法是使用一个堆栈对要分裂的块入栈
// 使用堆栈的方法类似在"区域生长"的实现方法
#include <stack>
struct SplitStruct
{
     unsigned int nWidth;                 // 这一块图像的宽度
     unsigned int nHeigh;                 // 这一块图像的高度
     unsigned int nOffSetWidth;           // 相对源图像数据的偏移宽度
     unsigned int nOffSetHeigh;           // 相对源图像数据的偏移高度
};
void AreaSplitCombineEx(BYTE* image0,              // 源图像数据
                         unsigned int nAllWidth,        // 源图像的宽度
                         unsigned int nAllHeigh,        // 源图像的高度
                         unsigned int w,                // 这一块图像的宽度
                         unsigned int h,                // 这一块图像的高度
                         unsigned int nOffSetWidth,     // 相对源图像数据的偏移宽度
                         unsigned int nOffSetHeigh)     // 相对源图像数据的偏移高度
{
 
     std::stack<SplitStruct> nMyStack;
 
     SplitStruct splitStruct, splitStructTemp;
     splitStruct.nWidth          = w;
     splitStruct.nHeigh          = h;
     splitStruct.nOffSetWidth    = nOffSetWidth;
     splitStruct.nOffSetHeigh    = nOffSetHeigh;
 
     nMyStack.push(splitStruct);
    
     int i, j;
     int nValueS[2][2];     // 用于存储块图像的属性值(该属性值= 该块图像的所有像素灰度值之和除以该块图像所有像素点的数量)
     int nAV;
     int nWidthTemp[3], nHeightTemp[3], nTemp;
     int nWidth, nHeigh;
     int n, m, l;
     double dOver;
 
 
     while(!nMyStack.empty())
     {
         splitStruct = nMyStack.top();
         nMyStack.pop();
 
 
         n = (splitStruct.nOffSetHeigh * nAllWidth + splitStruct.nOffSetWidth);     // 该块图像的左上角
         // 1. 把图像分成2 * 2 块,
         nWidthTemp[0] = 0;
         nWidthTemp[2] = (splitStruct.nWidth + 1) / 2;
         nWidthTemp[1] = splitStruct.nWidth - nWidthTemp[2];
 
         nHeightTemp[0] = 0;
         nHeightTemp[2] = (splitStruct.nHeigh + 1) / 2;
         nHeightTemp[1] = splitStruct.nHeigh - nHeightTemp[2];
 
         // 计算每一块图像的属性值
         int nValue;
         int nValueTemp;
         nAV = 0;
         for(i = 1; i < 3; ++i)
         {
              for(j = 1; j < 3; ++j)
              {
                   nValue = 0;
                   m = (n + nAllWidth * nHeightTemp[i - 1] + nWidthTemp[j - 1]);
                   for(nHeigh = 0; nHeigh < nHeightTemp[i]; ++nHeigh)
                   {
                       for(nWidth = 0; nWidth < nWidthTemp[j]; ++nWidth)
                       {
                            l = (m + nAllWidth * nHeigh + nWidth) * 4;
                            nValueTemp = (0.299 * image0[l] + 0.587 * image0[l + 1] + 0.114 * image0[l + 2]);
                            // 灰度值之和
                            nValue += nValueTemp;
                       }
                   }
 
                   if(nHeightTemp[i] * nWidthTemp[j] == 0)
                   {
                       continue;
                   }
                   if(nHeightTemp[i] * nWidthTemp[j] == 1)
                   {
                       l = m * 4;
                       if((0.299 * image0[l] + 0.587 * image0[l + 1] + 0.114 * image0[l + 2]) < 125)
// 这个值可以动态设定
                       {
                            image0[l] = image0[l + 1] = image0[l + 2] = 0;
                            image0[l + 3] = 255;
                       }
                       else
                       {
                            image0[l] = image0[l + 1] = image0[l + 2] = 255;
                            image0[l + 3] = 255;
                       }
                       continue;
                   }
 
                   // 各块图像的灰度平均值(每一块图像的属性值)
                   nValueS[i - 1][j - 1] = nValue / (nHeightTemp[i] * nWidthTemp[j]);
                   // 2. 对每一块进行判断是否继续分裂(注意分裂的原则)
                   // 我这里的分裂原则是: 图像的属性值在属性值平均值的误差范围之内就不分裂
                   if(nValueS[i - 1][j - 1]  < 220)     // 灰度平均值少于200 需要继续分裂 // 这里就是分裂准则了
                   {
                       splitStructTemp.nWidth           = nWidthTemp[j];
                       splitStructTemp.nHeigh           = nHeightTemp[i];
                       splitStructTemp.nOffSetWidth     = splitStruct.nOffSetWidth + nWidthTemp[j - 1];
                       splitStructTemp.nOffSetHeigh     = splitStruct.nOffSetHeigh + nHeightTemp[i - 1];
                       nMyStack.push(splitStructTemp);
                   }
                   else                                      // 合并(直接填充该块图像为黑色)
                   {
                       // 3. 如果不需要分裂, 则进行合并
                       for(nHeigh = 0; nHeigh < nHeightTemp[i]; ++nHeigh)
                       {
                            for(nWidth = 0; nWidth < nWidthTemp[j]; ++nWidth)
                            {
                                 l = (m + nAllWidth * nHeigh + nWidth) * 4;
                                 image0[l] = image0[l + 1] = image0[l + 2] = 255;
                                 image0[l + 3] = 255;
                            }
                       }
                   }
              }
         }
     }
 
     return;
}

参考

https://blog.csdn.net/qq_20823641/article/details/52191210

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值