【opencv】图像细化

【opencv】图像细化

2014-02-17 21:03 5404人阅读 评论(14) 收藏 举报
 分类:
opencv(1) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

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

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

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

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

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

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

参考资料

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

    运行效果

    1原图像

    2.运行效果

转载于:https://www.cnblogs.com/donaldlee2008/p/5232035.html

【opencv】图像细化相关推荐

  1. Opencv——基于索引表的图像细化

    图像细化针对的是二值图像  或者用阀值处理的二值图像.基于索引表的细化算法大致是遍历被二值化图像的边缘,根据边缘点的八连通域情况查找索引表以确定该边缘点是否能够被删除.根据一些细化规则我们可以建立索引 ...

  2. 【opencv-c++】cv::ximgproc::thinning图像细化算法

    [opencv-c++]cv::ximgproc::thinning图像细化算法 1.背景 2.示例 3.说明 1.背景 cv::ximgproc::thinning函数官方文档: https://d ...

  3. 图像细化 A fast parallel algorithm for thinning digital patterns

    我这是测试了两个人的代码,似乎有些区别的,第二篇作者贴出来的代码还存在一些bug,我简单修改了一下,实现的效果上似乎是有一下差别,后续看看论文再做评价. 两个方法也都能满足一定的需求. 参考blog: ...

  4. opencv-之图像细化(直线细化)

    平时在我们图像处理的时候会经常用到图像细化或者称为直线细化,在这里我跟大家分享一下我个人的理解,这是我花了一天的时间才搞明白其中的原理,具体的原理大家可以在其他博主的博客里搜索到,偶有详细的解释,我这 ...

  5. OpenCV——图像梯度与边缘检测(python实现)

    OpenCV--图像梯度与边缘检测 6.1 简介 严格的说,梯度计算需要求导数.但是图像梯度的计算,是通过计算像素值的差得到梯度的近似值.图像梯度表示的是图像变化的速度,反映了图像的边缘信息. 边缘是 ...

  6. OpenCV图像梯度(Sobel和Scharr)

    OpenCV图像梯度(Sobel和Scharr) 1 图像梯度是什么? 2 图像梯度的用途 3 图像梯度的使用 参考 这篇博客将介绍图像渐变以及如何使用OpenCV的cv2.Sobel计算Sobel渐 ...

  7. Python,OpenCV图像金字塔cv2.pyrUp(), cv2.pyrDown()

    Python,OpenCV图像金字塔cv2.pyrUp, cv2.pyrDown 1. 效果图 2. 原理 2.1 什么是图像金字塔 2.2 金字塔分类 2.3 应用 3. 源码 参考 这篇博客将介绍 ...

  8. OpenCV图像缩放插值之BiCubic双三次插值

    图像缩放算法简介 在图像的仿射变换中,很多地方需要用到插值运算,常见的插值运算包括最邻近插值,双线性插值,双三次插值(立体插值),兰索思插值等方法,OpenCV提供了很多方法,其中,双线性插值由于折中 ...

  9. python皮同_Python OpenCV 图像的双线性插值算法,全网最细致的算法说明_橡皮擦,一个逗趣的互联网高级网虫-CSDN博客...

    原文作者:梦想橡皮擦 原文标题:Python OpenCV 图像的双线性插值算法,全网最细致的算法说明 发布时间:2021-02-17 20:55:32 Python OpenCV 365 天学习计划 ...

最新文章

  1. Javascript综合应用小案例(续)
  2. python返回负数_在Python中三角函数sin返回负数
  3. anaconda创建新环境_【创建社会主义新农村】怀城街道:转变整治理念 农村人居环境换新颜...
  4. 中国移动发布5G权威测评:华为Mate 20 X 以强劲性能拔得头筹
  5. 美国一公司起诉苹果 指控iPhone中“个人热点”技术侵犯其专利
  6. asp.net mysql helper_asp.net使用SQLHelper操作数据库
  7. NET委托:一个C#睡前故事
  8. Android Studio运行程序出现Session ‘app’: Error Launching activity 解决办法
  9. Maven使用yuicompressor-maven-plugin打包压缩css、js文件
  10. 数据结构与算法经典图书推荐
  11. 为什么 BI 软件都搞不定关联分析?带你分析分析
  12. CTPN - 自然场景文本检测
  13. petalinux 2020.2 安装教程,基于ubuntu20.04.LTS版本
  14. 度度熊保护村庄 HDU - 6080 floyd最小环/叉积判左右
  15. 记录解决Win10底部任务栏转圈圈问题的过程(Windows假死)
  16. element-ul基本使用
  17. 最终幻想7php攻略,最终幻想7图文攻略 PS版FF7全剧情任务攻略(CD1)(6)
  18. windows 模拟按键与鼠标
  19. 11+++各类计算机病毒介绍
  20. php react ssr,React-SSR

热门文章

  1. mysql bytessent_如何对DSQLSERVER、MySQL、Orache语句性能分析
  2. DOM方式进行的XML文件、Document、String之间的相互转换
  3. 五大常用算法之五:分支限界法
  4. 关于eclipse不编译项目的问题解决
  5. frameset用法
  6. 说说云盘背后的黑科技!
  7. PL/SQL编程基础(一):PL/SQL语法简介(匿名PL/SQL块)
  8. 【随感】i'm new here
  9. 使用ASP.NET AJAX异步调用Web Service和页面中的类方法(2):处理异步调用中的异常...
  10. PHP 算法之 -- 计算器设计