图像的骨架提取

细化方法当中,当属经典的Zhang并行快速细化算法,细化之后的轮廓走势与原图保持得相对较好。

参照 http://blog.csdn.net/byxdaz/article/details/5642669 , 代码改造结果如下,

//将 DEPTH_8U型二值图像进行细化  经典的Zhang并行快速细化算法
void thin(const Mat &src, Mat &dst, const int iterations)
{
    const int height =src.rows -1;
    const int width  =src.cols -1;

    //拷贝一个数组给另一个数组
    if(src.data != dst.data)
    {
        src.copyTo(dst);
    }

    int n = 0,i = 0,j = 0;
    Mat tmpImg;
    uchar *pU, *pC, *pD;
    BOOL isFinished =FALSE;

     for(n=0; n<iterations; n++)
     {
         dst.copyTo(tmpImg);
         isFinished =FALSE;   //一次 先行后列扫描 开始
         //扫描过程一 开始
         for(i=1; i<height;  i++)
        {
            pU = tmpImg.ptr<uchar>(i-1);
            pC = tmpImg.ptr<uchar>(i);
            pD = tmpImg.ptr<uchar>(i+1);
           for(int j=1; j<width; j++)
           {
            if(pC[j] > 0)
            {
                 int ap=0;
                 int p2 = (pU[j] >0);
                 int p3 = (pU[j+1] >0);
                 if (p2==0 && p3==1)
                 {
                  ap++;
                 }
                 int p4 = (pC[j+1] >0);
                 if(p3==0 && p4==1)
                 {
                  ap++;
                 }
                 int p5 = (pD[j+1] >0);
                 if(p4==0 && p5==1)
                 {
                  ap++;
                 }
                 int p6 = (pD[j] >0);
                 if(p5==0 && p6==1)
                 {
                  ap++;
                 }
                 int p7 = (pD[j-1] >0);
                 if(p6==0 && p7==1)
                 {
                  ap++;
                 }
                 int p8 = (pC[j-1] >0);
                 if(p7==0 && p8==1)
                 {
                  ap++;
                 }
                 int p9 = (pU[j-1] >0);
                 if(p8==0 && p9==1)
                 {
                  ap++;
                 }
                 if(p9==0 && p2==1)
                 {
                  ap++;
                 }
                 if((p2+p3+p4+p5+p6+p7+p8+p9)>1 && (p2+p3+p4+p5+p6+p7+p8+p9)<7)
                 {
                      if(ap==1)
                      {
                           if((p2*p4*p6==0)&&(p4*p6*p8==0))
                           {
                                dst.ptr<uchar>(i)[j]=0;
                                isFinished =TRUE;
                           }

                        //   if((p2*p4*p8==0)&&(p2*p6*p8==0))
                       //    {
                       //         dst.ptr<uchar>(i)[j]=0;
                       //         isFinished =TRUE;
                       //    }

                     }
                }
            }

           } //扫描过程一 结束

         dst.copyTo(tmpImg);
         //扫描过程二 开始
         for(i=1; i<height;  i++)  //一次 先行后列扫描 开始
        {
            pU = tmpImg.ptr<uchar>(i-1);
            pC = tmpImg.ptr<uchar>(i);
            pD = tmpImg.ptr<uchar>(i+1);
           for(int j=1; j<width; j++)
           {
            if(pC[j] > 0)
            {
                 int ap=0;
                 int p2 = (pU[j] >0);
                 int p3 = (pU[j+1] >0);
                 if (p2==0 && p3==1)
                 {
                  ap++;
                 }
                 int p4 = (pC[j+1] >0);
                 if(p3==0 && p4==1)
                 {
                  ap++;
                 }
                 int p5 = (pD[j+1] >0);
                 if(p4==0 && p5==1)
                 {
                  ap++;
                 }
                 int p6 = (pD[j] >0);
                 if(p5==0 && p6==1)
                 {
                  ap++;
                 }
                 int p7 = (pD[j-1] >0);
                 if(p6==0 && p7==1)
                 {
                  ap++;
                 }
                 int p8 = (pC[j-1] >0);
                 if(p7==0 && p8==1)
                 {
                  ap++;
                 }
                 int p9 = (pU[j-1] >0);
                 if(p8==0 && p9==1)
                 {
                  ap++;
                 }
                 if(p9==0 && p2==1)
                 {
                  ap++;
                 }
                 if((p2+p3+p4+p5+p6+p7+p8+p9)>1 && (p2+p3+p4+p5+p6+p7+p8+p9)<7)
                 {
                      if(ap==1)
                      {
                        //   if((p2*p4*p6==0)&&(p4*p6*p8==0))
                        //   {
                       //         dst.ptr<uchar>(i)[j]=0;
                       //         isFinished =TRUE;
                       //    }

                           if((p2*p4*p8==0)&&(p2*p6*p8==0))
                           {
                                dst.ptr<uchar>(i)[j]=0;
                                isFinished =TRUE;
                           }

                     }
                }
            }

           }

          } //一次 先行后列扫描完成
        //如果在扫描过程中没有删除点,则提前退出
         if(isFinished ==FALSE)
         {
            break;
         }
        }

    }
}

另一种实现方法,细化结果不大好,但看起来好像是简洁些,适合一般的简单应用。

BOOL isContourPoint(const int x, const int y, const Mat& bwImg)
{
  BOOL p[10] ={0}; //记录当前点的8邻域的有无情况

  const BYTE *pU= bwImg.ptr(y-1, x);  //上一行
  const BYTE *pC= bwImg.ptr(y, x);    //当前行
  const BYTE *pD= bwImg.ptr(y+1, x);  //下一行

   p[2]=*(pU) ? true:false;
   p[3]=*(pU+1) ? true:false;
   p[4]=*(pC+1) ? true:false;
   p[5]=*(pD+1) ? true:false;
   p[6]=*(pD) ? true:false;
   p[7]=*(pD-1) ? true:false;
   p[8]=*(pC-1) ? true:false;
   p[9]=*(pU-1) ? true:false;

  int Np=0;//邻域不为零节点总数
  int Tp=0;//邻域节点由0变成1的次数
  for (int i=2; i<10; i++)
  {
  Np += p[i];
  int k= (i<9) ? (i+1) : 2;
  if ( p[k] -p[i]>0)
  {
  Tp++;
  }
  }
  int p246= p[2] && p[4] && p[6];
  int p468= p[4] && p[6] && p[8];

  int p24= p[2] && !p[3] && p[4] && !p[5] && !p[6] && !p[7] && !p[8] && !p[9];
  int p46= !p[2] && !p[3] && p[4] && !p[5] && p[6] && !p[7] && !p[8] && !p[9];
  int p68= !p[2] && !p[3] && !p[4] && !p[5] && p[6] && !p[7] && p[8] && !p[9];
  int p82= p[2] && !p[3] && !p[4] && !p[5] && !p[6] && !p[7] && p[8] && !p[9];

  int p782= p[2] && !p[3] && !p[4] && !p[5] && !p[6] && p[7] && p[8] && !p[9];
  int p924= p[2] && !p[3] && p[4] && !p[5] && !p[6] && !p[7] && !p[8] && p[9];
  int p346= !p[2] && p[3] && p[4] && !p[5] && p[6] && !p[7] && !p[8] && !p[9];
  int p568= !p[2] && !p[3] && !p[4] && p[5] && p[6] && !p[7] && p[8] && !p[9];

  int p689= !p[2] && !p[3] && !p[4] && !p[5] && p[6] && !p[7] && p[8] && p[9];
  int p823= p[2] && p[3] && !p[4] && !p[5] && !p[6] && !p[7] && p[8] && !p[9];
  int p245= p[2] && !p[3] && p[4] && p[5] && !p[6] && !p[7] && !p[8] && !p[9];
  int p467= !p[2] && !p[3] && p[4] && !p[5] && p[6] && p[7] && !p[8] && !p[9];

  int p2468= p24 || p46 || p68 || p82;
  int p3333= p782 || p924 || p346 || p568 || p689 || p823 || p245 || p467;

  //判定条件第一个由数字图像处理上得到,由于结果不够满意,又加上两个条件
  return ( !p246 && !p468 && (Np<7) && (Np>1) && (Tp==1) ) || p2468 || p3333;
}

//细化二值图像,得到单像素连通域
void thin(Mat& bwImg)
{
  const int imgRows=bwImg.rows -1;
  const int imgCols=bwImg.cols -1;

  int Remove_Num;
  int i, j;
  do //循环调用,直至没有可以去掉的点
  {
    Remove_Num=0;
    for (j = 1; j < imgRows; j++)
    {
      for(i = 1; i < imgCols; i++)
      {

        if ( *bwImg.ptr(j, i) && isContourPoint( i, j, bwImg))//符合条件,去掉
        {
          *bwImg.ptr(j, i)=0;
          Remove_Num++;
        }  //if
      }  //for
    }  //for
  } while( Remove_Num);
}
时间: 2024-10-22 03:54:23

图像的骨架提取的相关文章

python数字图像处理(19):骨架提取与分水岭算法

骨架提取与分水岭算法也属于形态学处理范畴,都放在morphology子模块内. 1.骨架提取 骨架提取,也叫二值图像细化.这种算法能将一个连通区域细化成一个像素的宽度,用于特征提取和目标拓扑表示. morphology子模块提供了两个函数用于骨架提取,分别是Skeletonize()函数和medial_axis()函数.我们先来看Skeletonize()函数. 格式为:skimage.morphology.skeletonize(image) 输入和输出都是一幅二值图像. 例1: from s

基于红黑树的骨架提取Java

前面有提到基于Mat变换的骨架提取,然而在实际的应用中处理稍微大点的图片的时候耗时较长就是个问题了,于是针对这个问题寻找了另外一种方法--基于红黑树的骨架提取,这种方法明显处理速度要快一些. 基于红黑树的骨架提取的思路如下: 1,对输入的二值图像进行延拓(直白的说就是在图像的外边界加一圈白点),得到二值图像P: 2,针对骨架提取有8种结构类型(S0~S7)的待删除点.骨架提取的过程实际上就是不断的找符合这8种结构的待删除点,删除待删除点.初始化8个空的红黑树结构T0~T7,初始化current=

基于Mat变换的骨架提取Java

针对一副二值图像,区域内的点只有背景点(白点,0值)和前景点(黑点,1值).对于给定区域的像素点逐次应用两个基本步骤,以提取骨架: step1,如果一个像素点满足下列4个条件,那么将它标记为要删除的点: (1)2<=N(p1)<=6,其中N(p1)=p2+p3+p4+...+p8+p9; (2)T(p1)=1,其中T(p1)是以p2,p3,...,p8,p9,p2的次序旋转,从0到1的变换次数; (3)p2*p4*p6=0; (4)p4*p6*p8=0. step2,条件(1)(2)不变,但是

图像特征的提取

1.  HOG特征 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子.它通过计算和统计图像局部区域的梯度方向直方图来构成特征.Hog特征结合SVM分类器已经被广泛应用于图像识别中,尤其在行人检测中获得了极大的成功. 主要思想:--获取轮廓信息 在一副图像中,局部目标的表象和形状(appearance and shape)能够被梯度或边缘的方向密度分布很好地描述.(本质:梯度的统计信息,而梯度主要

毕业课题之------------图像的直线提取--hough变换

图像处理程序中经常要用到直线检测,常用的直线检测方法是Hough变换.Hough变换是图像处理中从图像中识别几何形状的基本方法之一.Hough变换的基本原理在于 利用点与线的对偶性,将原始图像空间的给定的曲线通过曲线表达形式变为参数空间的一个点.这样就把原始图像中给定曲线的检测问题转化为寻找参数空间中的峰值问 题.也即把检测整体特性转化为检测局部特性.比如直线.椭圆.圆.弧线等. 简而言之,Hough变换思想为:在原始图像坐标系下的一个点对应了参数坐标系中的一条直线,同样参数坐标系的一条直线对应

图像特征点提取

一.图像变化的类型: 二.特征点作用: 三.重要特征点---角点及其检测方法: 参考文献: http://blog.jobbole.com/83919/ http://blog.csdn.net/crzy_sparrow/article/details/7391511

手撸kmeans(c++)实现,用于图像的主色调提取(1)

想到那天头条面试时,让我手撸kmeans,奈何好久不用c++,好多都忘了==淡淡的忧伤 这次刚好赶上机会,可以再试试了,我写成项目了,有多个文件 首先:base.h #ifndef BASE_H #define BASE_H #include<iostream> #include<opencv2/opencv.hpp> #include<cassert> #include<stdlib.h> class Baseofgeo{ public: float co

【数字图像处理】图像细化处理

图像细化 细化技术:把一个平面区域简化成图的结构形状表示法骨架:一种细化结构,它是目标的重要拓扑描述,具有非常广泛的应用.在图像识别或数据压缩时,经常用细化结构.例如:在识别字符之前,往往要先对字符作细化处理,求出字符的细化结构.细化的作用:目的将图像的骨架提取出来的同时,保持图像细小部分的连通性,对被处理的图像进行细化有助于突出形状特点和减少冗余信息量. 细化算法 细化算法:采取逐次去除边界的方法进行的,不能破化图像的连通性.通常选择一组结构元素对,不断在这些结构对中循环,如果所得结果不再变化

OpenCV与EmguCV中的图像轮廓提取

轮廓是图像中表示边界的一系列点的集合. 虽然边缘检测算法可以根据像素间的差异检查出轮廓边界的像素,但是它并没有把轮廓做为一个整体表示出来.所以下一步工作是把这些边缘检测出来的像素组装成轮廓. openCV中可以用findContours()函数来从二值图像中提取轮廓. openCV中一般用序列来存储轮廓信息.序列中的每一个元素是曲线中一个点的位置. 函数findContours()从二值图像中寻找轮廓.findContours()处理的图像可以是Canny()后得到的有边缘像素的的图像,也可以是