【原文:http://blog.csdn.net/qianchenglenger/article/details/19332011

在我们进行图像处理的时候,有可能需要对图像进行细化,提取出图像的骨架信息,进行更加有效的分析。

     图像细化(Image Thinning),一般指二值图像的骨架化(Image Skeletonization)
的一种操作运算。

     所谓的细化就是经过一层层的剥离,从原来的图中去掉一些点,但仍要保持原来的形状,直到得到图像的骨架。骨架,可以理解为图象的中轴。

     好的细化算法一定要满足:

  • 收敛性;
  • 保证细化后细线的连通性;
  • 保持原图的基本形状;
  • 减少笔画相交处的畸变;
  • 细化结果是原图像的中心线;
  • 细化的快速性和迭代次数少;

    这里,我们对"Zhang并行快速细化算法"进行了实现(注意,该算法为并行算法,而我们在实现过程中并没有并行化处理,所以,效率并没有达到最好)。

    参考资料

细化算法

论文 A fast parallel algorithm for thinning digital patterns

[cpp] view plaincopy

  1. #include <opencv2/opencv.hpp>  
  2. #include <iostream>  
  3. #include <vector>  
  4. #include <limits>  
  5.   
     
  6. using namespace cv;  
  7. using namespace std;  
  8.   
     
  9. /** 
  10.  * @brief 对输入图像进行细化 
  11. 位灰度图像格式,元素中只有0与1,1代表有元素,0代表为空白 
  12. 与1,1代表有元素,0代表为空白 
  13.  * @param[in] maxIterations限制迭代次数,如果不进行限制,默认为-1,代表不限制迭代次数,直到获得最终结果 
  14.  */  
  15. void thinImage(IplImage* src,IplImage* dst,int maxIterations = -1 )  
  16. {  
  17.     CvSize size = cvGetSize(src);  
  18.     cvCopy(src,dst);//将src中的内容拷贝到dst中  
  19.     int count = 0;  //记录迭代次数  
  20.     while (true)  
  21.     {  
  22.         count++;  
  23.         if(maxIterations!=-1 && count > maxIterations) //限制次数并且迭代次数到达  
  24.             break;  
  25.         //std::cout << count << ' ';输出迭代次数  
  26.         vector<pair<int,int> > mFlag; //用于标记需要删除的点  
  27.         //对点标记  
  28.         for (int i=0; i<size.height; ++i)  
  29.         {  
  30.             for (int j=0; j<size.width; ++j)  
  31.             {  
  32.                 //如果满足四个条件,进行标记  
  33.                 //  p9 p2 p3  
  34.                 //  p8 p1 p4  
  35.                 //  p7 p6 p5  
  36.                 int p1 = CV_IMAGE_ELEM(dst,uchar,i,j);  
  37.                 int p2 = (i==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j);  
  38.                 int p3 = (i==0 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j+1);  
  39.                 int p4 = (j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i,j+1);  
  40.                 int p5 = (i==size.height-1 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j+1);  
  41.                 int p6 = (i==size.height-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j);  
  42.                 int p7 = (i==size.height-1 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j-1);  
  43.                 int p8 = (j==0)?0:CV_IMAGE_ELEM(dst,uchar,i,j-1);  
  44.                 int p9 = (i==0 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j-1);  
  45.   
     
  46.                 if ((p2+p3+p4+p5+p6+p7+p8+p9)>=2 && (p2+p3+p4+p5+p6+p7+p8+p9)<=6)  
  47.                 {  
  48.                     int ap=0;  
  49.                     if (p2==0 && p3==1) ++ap;  
  50.                     if (p3==0 && p4==1) ++ap;  
  51.                     if (p4==0 && p5==1) ++ap;  
  52.                     if (p5==0 && p6==1) ++ap;  
  53.                     if (p6==0 && p7==1) ++ap;  
  54.                     if (p7==0 && p8==1) ++ap;  
  55.                     if (p8==0 && p9==1) ++ap;  
  56.                     if (p9==0 && p2==1) ++ap;  
  57.                       
     
  58.                     if (ap==1)  
  59.                     {  
  60.                         if (p2*p4*p6==0)  
  61.                         {  
  62.                             if (p4*p6*p8==0)  
  63.                             {  
  64.                                 //标记  
  65.                                 mFlag.push_back(make_pair(i,j));  
  66.                             }  
  67.                         }  
  68.                     }  
  69.                 }  
  70.             }  
  71.         }  
  72.   
     
  73.         //将标记的点删除  
  74.         for (vector<pair<int,int> >::iterator i=mFlag.begin(); i!=mFlag.end(); ++i)  
  75.         {  
  76.             CV_IMAGE_ELEM(dst,uchar,i->first,i->second) = 0;  
  77.         }  
  78.   
     
  79.         //直到没有点满足,算法结束  
  80.         if (mFlag.size()==0)  
  81.         {  
  82.             break;  
  83.         }  
  84.         else  
  85.         {  
  86.             mFlag.clear();//将mFlag清空  
  87.         }  
  88.   
     
  89.         //对点标记  
  90.         for (int i=0; i<size.height; ++i)  
  91.         {  
  92.             for (int j=0; j<size.width; ++j)  
  93.             {  
  94.                 //如果满足四个条件,进行标记  
  95.                 //  p9 p2 p3  
  96.                 //  p8 p1 p4  
  97.                 //  p7 p6 p5  
  98.                 int p1 = CV_IMAGE_ELEM(dst,uchar,i,j);  
  99.                 if(p1!=1) continue;  
  100.                 int p2 = (i==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j);  
  101.                 int p3 = (i==0 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j+1);  
  102.                 int p4 = (j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i,j+1);  
  103.                 int p5 = (i==size.height-1 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j+1);  
  104.                 int p6 = (i==size.height-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j);  
  105.                 int p7 = (i==size.height-1 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j-1);  
  106.                 int p8 = (j==0)?0:CV_IMAGE_ELEM(dst,uchar,i,j-1);  
  107.                 int p9 = (i==0 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j-1);  
  108.   
     
  109.                 if ((p2+p3+p4+p5+p6+p7+p8+p9)>=2 && (p2+p3+p4+p5+p6+p7+p8+p9)<=6)  
  110.                 {  
  111.                     int ap=0;  
  112.                     if (p2==0 && p3==1) ++ap;  
  113.                     if (p3==0 && p4==1) ++ap;  
  114.                     if (p4==0 && p5==1) ++ap;  
  115.                     if (p5==0 && p6==1) ++ap;  
  116.                     if (p6==0 && p7==1) ++ap;  
  117.                     if (p7==0 && p8==1) ++ap;  
  118.                     if (p8==0 && p9==1) ++ap;  
  119.                     if (p9==0 && p2==1) ++ap;  
  120.   
     
  121.                     if (ap==1)  
  122.                     {  
  123.                         if (p2*p4*p8==0)  
  124.                         {  
  125.                             if (p2*p6*p8==0)  
  126.                             {  
  127.                                 //标记  
  128.                                 mFlag.push_back(make_pair(i,j));  
  129.                             }  
  130.                         }  
  131.                     }  
  132.                 }  
  133.             }  
  134.         }  
  135.         //删除  
  136.         for (vector<pair<int,int> >::iterator i=mFlag.begin(); i!=mFlag.end(); ++i)  
  137.         {  
  138.             CV_IMAGE_ELEM(dst,uchar,i->first,i->second) = 0;  
  139.         }  
  140.   
     
  141.         //直到没有点满足,算法结束  
  142.         if (mFlag.size()==0)  
  143.         {  
  144.             break;  
  145.         }  
  146.         else  
  147.         {  
  148.             mFlag.clear();//将mFlag清空  
  149.         }  
  150.     }  
  151. }  
  152.   
     
  153. int main(int argc, char*argv[])  
  154. {  
  155.     //获取图像  
  156.     if (argc!=2)  
  157.     {  
  158.         cout << "参数个数错误!"<<endl;  
  159.         return -1;  
  160.     }  
  161.     IplImage *pSrc = cvLoadImage(argv[1],CV_LOAD_IMAGE_GRAYSCALE);  
  162.     if (!pSrc)  
  163.     {  
  164.         cout << "读取文件失败!" << endl;  
  165.         return -1;  
  166.     }  
  167.     IplImage *pTemp = cvCreateImage(cvGetSize(pSrc),pSrc->depth,pSrc->nChannels);  
  168.     IplImage *pDst = cvCreateImage(cvGetSize(pSrc),pSrc->depth,pSrc->nChannels);  
  169.       
     
  170.     //将原图像转换为二值图像  
  171.     cvThreshold(pSrc,pTemp,128,1,CV_THRESH_BINARY);   
  172.     //图像细化  
  173.     thinImage(pTemp,pDst);  
  174.   
     
  175.     for (int i=0; i<pDst->height; ++i)  
  176.     {  
  177.         for (int j=0; j<pDst->width; ++j)  
  178.         {  
  179.             if(CV_IMAGE_ELEM(pDst,uchar,i,j)==1)  
  180.                 CV_IMAGE_ELEM(pDst,uchar,i,j)= 255;  
  181.         }  
  182.     }  
  183.   
     
  184.     namedWindow("src",CV_WINDOW_AUTOSIZE);  
  185.     namedWindow("dst",CV_WINDOW_AUTOSIZE);  
  186.     cvShowImage("src",pSrc);  
  187.     cvShowImage("dst",pDst);  
  188.   
     
  189.     waitKey(0);  
  190. }  

    运行效果

1原图像

2.运行效果

【opencv】图像细化的更多相关文章

  1. OpenCV图像细化的一个例子

    转自:http://blog.csdn.net/zfdxx369/article/details/9091953?utm_source=tuicool 本文是zhang的一篇经典图像细化论文,效果很好 ...

  2. opencv 图像细化

    图像细化多用于机器人视觉,OCR字符识别等领域,细化后的图像经过去毛刺就成为了我们常说的图像的骨架. 该图像细化代码依据论文: T. Y. ZHANG and C. Y. SUEN  A Fast P ...

  3. SSE图像算法优化系列三十二:Zhang\Guo图像细化算法的C语言以及SIMD指令优化

    二值图像的细化算法也有很多种,比较有名的比如Hilditch细化.Rosenfeld细化.基于索引表的细化.还有Opencv自带的THINNING_ZHANGSUEN.THINNING_GUOHALL ...

  4. OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放

    这篇已经写得很好,真心给作者点个赞.题目都是直接转过来的,直接去看吧. Reference Link : http://blog.csdn.net/poem_qianmo/article/detail ...

  5. 【OpenCV新手教程之十三】OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26157633 作者:毛星云(浅墨) ...

  6. Opencv 图像叠加 添加水印

    Opencv 图像叠加 添加水印 C++: void Mat::copyTo(OutputArray m) const C++: void Mat::copyTo(OutputArray m, Inp ...

  7. opencv图像读取-imread

    前言 图像的读取和保存一定要注意imread函数的各个参数及其意义,尽量不要使用默认参数,否则就像数据格式出现错误(here)一样,很难查找错误原因的: re: 1.opencv图像的读取与保存; 完

  8. 学习 opencv---(12)OpenCV 图像金字塔:高斯金字塔,拉普拉斯金字塔与图片尺寸缩放

    在这篇文章里,我们一起学习下 图像金字塔 的一些基本概念,如何使用OpenCV函数pyrUp和pyrDown 对图像进行向上和向下采样,以及了解专门用于缩放图像尺寸的resize函数的用法.此博文一共 ...

  9. [OpenCV Qt教程] 在Qt图形界面中显示OpenCV图像的OpenGL Widget(第二部分)

    本文译自:http://www.robot-home.it/blog/en/software/tutorial-opencv-qt-opengl-widget-per-visualizzare-imm ...

随机推荐

  1. ExtJs 4.2.1 复选框数据项动态加载(更新一下)

    最近在做博客项目,后台管理用的是ExtJs4.2.1版本,因为是初学所以在使用的时候也遇到不少的这样或那样的问题,也写了不少这方面的博客,今天要写的博客是关于复选框数据项动态的加载功能,以前也没用过, ...

  2. flash链接需要后台调用时的插入flash方法

    <!--<textarea id="syslink" style="display:none;">// 1:打宝塔 2:山寨玩法 3:跨服竞技 ...

  3. asp.net单点登录(SSO)解决方案

    前些天一位朋友要我帮忙做一单点登录,其实这个概念早已耳熟能详,但实际应用很少,难得最近轻闲,于是决定通过本文来详细描述一个SSO解决方案,希望对大家有所帮助.SSO的解决方案很多,但搜索结果令人大失所 ...

  4. [转载]线程间操作无效: 从不是创建控件“ListBox1”的线程访问它

    解决方法有两种: 1.Control.CheckForIllegalCrossThreadCalls = false 2.用委托解决线程安全问题

  5. [转载]Log4net学习笔记

    Log4net 学习笔记: 主要是根据apache站点整理的: 原文链接:http://logging.apache.org/log4net/release/sdk/ http://logging.a ...

  6. P​H​P​ ​5​.​3​连​接​s​q​l​ ​s​e​r​v​e​r​ ​2​0​0​8​ ​R​2

    我的机器为: xp sp3 sql server 2008 developer apache 2.2.2 php 5.3  从5.3开始,php就不再提供mssql.dll了,所以要php连接sql  ...

  7. EhCache 分布式缓存/缓存集群(转)

    开发环境: System:Windows JavaEE Server:tomcat5.0.2.8.tomcat6 JavaSDK: jdk6+ IDE:eclipse.MyEclipse 6.6 开发 ...

  8. MyEclipse下查看Java API帮助文档

    每次重装JDK或者升级JDK时,都会忘了如何使MyEclipse关联帮助文档.然后,再花十几分钟重新google搜索,麻烦! 首先下载Javadoc api帮助文档,google搜一下就行了. MyE ...

  9. ios loading视图动画(模仿58同城)

    最近看了58同城的加载视图,感觉很不错,如下图: 所以想模仿写一个,下载58同城的app,解压,发现它用的是图片来实现的动画效果, 并不是绘制出来的,所以这就相对简单些了,其实整个动画的逻辑不复杂,无 ...

  10. Servlet的一些细节问题

    Servlet的细节问题 1.一个已经注册的Servlet可以被多次映射即: <servlet> <!-- servlet的注册名 --> <servlet-name&g ...