【opencv】图像细化
【opencv】图像细化
【opencv】图像细化
![](http://static.blog.csdn.net/images/category_icon.jpg)
![](http://static.blog.csdn.net/images/arrow_triangle%20_down.jpg)
版权声明:本文为博主原创文章,未经博主允许不得转载。
在我们进行图像处理的时候,有可能需要对图像进行细化,提取出图像的骨架信息,进行更加有效的分析。
图像细化(Image Thinning),一般指二值图像的骨架化(Image Skeletonization) 的一种操作运算。
所谓的细化就是经过一层层的剥离,从原来的图中去掉一些点,但仍要保持原来的形状,直到得到图像的骨架。骨架,可以理解为图象的中轴。
好的细化算法一定要满足:
- 收敛性;
- 保证细化后细线的连通性;
- 保持原图的基本形状;
- 减少笔画相交处的畸变;
- 细化结果是原图像的中心线;
- 细化的快速性和迭代次数少;
这里,我们对“Zhang并行快速细化算法”进行了实现(注意,该算法为并行算法,而我们在实现过程中并没有并行化处理,所以,效率并没有达到最好)。
参考资料
- 细化算法
- 论文 A fast parallel algorithm for thinning digital patterns
[cpp] view plain copy
- #include <opencv2/opencv.hpp>
- #include <iostream>
- #include <vector>
- #include <limits>
- using namespace cv;
- using namespace std;
- /**
- * @brief 对输入图像进行细化
- * @param[in] src为输入图像,用cvThreshold函数处理过的8位灰度图像格式,元素中只有0与1,1代表有元素,0代表为空白
- * @param[out] dst为对src细化后的输出图像,格式与src格式相同,调用前需要分配空间,元素中只有0与1,1代表有元素,0代表为空白
- * @param[in] maxIterations限制迭代次数,如果不进行限制,默认为-1,代表不限制迭代次数,直到获得最终结果
- */
- void thinImage(IplImage* src,IplImage* dst,int maxIterations = -1 )
- {
- CvSize size = cvGetSize(src);
- cvCopy(src,dst);//将src中的内容拷贝到dst中
- int count = 0; //记录迭代次数
- while (true)
- {
- count++;
- if(maxIterations!=-1 && count > maxIterations) //限制次数并且迭代次数到达
- break;
- //std::cout << count << ' ';输出迭代次数
- vector<pair<int,int> > mFlag; //用于标记需要删除的点
- //对点标记
- for (int i=0; i<size.height; ++i)
- {
- for (int j=0; j<size.width; ++j)
- {
- //如果满足四个条件,进行标记
- // p9 p2 p3
- // p8 p1 p4
- // p7 p6 p5
- int p1 = CV_IMAGE_ELEM(dst,uchar,i,j);
- int p2 = (i==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j);
- int p3 = (i==0 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j+1);
- int p4 = (j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i,j+1);
- int p5 = (i==size.height-1 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j+1);
- int p6 = (i==size.height-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j);
- int p7 = (i==size.height-1 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j-1);
- int p8 = (j==0)?0:CV_IMAGE_ELEM(dst,uchar,i,j-1);
- int p9 = (i==0 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j-1);
- if ((p2+p3+p4+p5+p6+p7+p8+p9)>=2 && (p2+p3+p4+p5+p6+p7+p8+p9)<=6)
- {
- int ap=0;
- if (p2==0 && p3==1) ++ap;
- if (p3==0 && p4==1) ++ap;
- if (p4==0 && p5==1) ++ap;
- if (p5==0 && p6==1) ++ap;
- if (p6==0 && p7==1) ++ap;
- if (p7==0 && p8==1) ++ap;
- if (p8==0 && p9==1) ++ap;
- if (p9==0 && p2==1) ++ap;
- if (ap==1)
- {
- if (p2*p4*p6==0)
- {
- if (p4*p6*p8==0)
- {
- //标记
- mFlag.push_back(make_pair(i,j));
- }
- }
- }
- }
- }
- }
- //将标记的点删除
- for (vector<pair<int,int> >::iterator i=mFlag.begin(); i!=mFlag.end(); ++i)
- {
- CV_IMAGE_ELEM(dst,uchar,i->first,i->second) = 0;
- }
- //直到没有点满足,算法结束
- if (mFlag.size()==0)
- {
- break;
- }
- else
- {
- mFlag.clear();//将mFlag清空
- }
- //对点标记
- for (int i=0; i<size.height; ++i)
- {
- for (int j=0; j<size.width; ++j)
- {
- //如果满足四个条件,进行标记
- // p9 p2 p3
- // p8 p1 p4
- // p7 p6 p5
- int p1 = CV_IMAGE_ELEM(dst,uchar,i,j);
- if(p1!=1) continue;
- int p2 = (i==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j);
- int p3 = (i==0 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j+1);
- int p4 = (j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i,j+1);
- int p5 = (i==size.height-1 || j==size.width-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j+1);
- int p6 = (i==size.height-1)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j);
- int p7 = (i==size.height-1 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i+1,j-1);
- int p8 = (j==0)?0:CV_IMAGE_ELEM(dst,uchar,i,j-1);
- int p9 = (i==0 || j==0)?0:CV_IMAGE_ELEM(dst,uchar,i-1,j-1);
- if ((p2+p3+p4+p5+p6+p7+p8+p9)>=2 && (p2+p3+p4+p5+p6+p7+p8+p9)<=6)
- {
- int ap=0;
- if (p2==0 && p3==1) ++ap;
- if (p3==0 && p4==1) ++ap;
- if (p4==0 && p5==1) ++ap;
- if (p5==0 && p6==1) ++ap;
- if (p6==0 && p7==1) ++ap;
- if (p7==0 && p8==1) ++ap;
- if (p8==0 && p9==1) ++ap;
- if (p9==0 && p2==1) ++ap;
- if (ap==1)
- {
- if (p2*p4*p8==0)
- {
- if (p2*p6*p8==0)
- {
- //标记
- mFlag.push_back(make_pair(i,j));
- }
- }
- }
- }
- }
- }
- //删除
- for (vector<pair<int,int> >::iterator i=mFlag.begin(); i!=mFlag.end(); ++i)
- {
- CV_IMAGE_ELEM(dst,uchar,i->first,i->second) = 0;
- }
- //直到没有点满足,算法结束
- if (mFlag.size()==0)
- {
- break;
- }
- else
- {
- mFlag.clear();//将mFlag清空
- }
- }
- }
- int main(int argc, char*argv[])
- {
- //获取图像
- if (argc!=2)
- {
- cout << "参数个数错误!"<<endl;
- return -1;
- }
- IplImage *pSrc = cvLoadImage(argv[1],CV_LOAD_IMAGE_GRAYSCALE);
- if (!pSrc)
- {
- cout << "读取文件失败!" << endl;
- return -1;
- }
- IplImage *pTemp = cvCreateImage(cvGetSize(pSrc),pSrc->depth,pSrc->nChannels);
- IplImage *pDst = cvCreateImage(cvGetSize(pSrc),pSrc->depth,pSrc->nChannels);
- //将原图像转换为二值图像
- cvThreshold(pSrc,pTemp,128,1,CV_THRESH_BINARY);
- //图像细化
- thinImage(pTemp,pDst);
- for (int i=0; i<pDst->height; ++i)
- {
- for (int j=0; j<pDst->width; ++j)
- {
- if(CV_IMAGE_ELEM(pDst,uchar,i,j)==1)
- CV_IMAGE_ELEM(pDst,uchar,i,j)= 255;
- }
- }
- namedWindow("src",CV_WINDOW_AUTOSIZE);
- namedWindow("dst",CV_WINDOW_AUTOSIZE);
- cvShowImage("src",pSrc);
- cvShowImage("dst",pDst);
- waitKey(0);
- }
运行效果
1原图像
2.运行效果
转载于:https://www.cnblogs.com/donaldlee2008/p/5232035.html
【opencv】图像细化相关推荐
- Opencv——基于索引表的图像细化
图像细化针对的是二值图像 或者用阀值处理的二值图像.基于索引表的细化算法大致是遍历被二值化图像的边缘,根据边缘点的八连通域情况查找索引表以确定该边缘点是否能够被删除.根据一些细化规则我们可以建立索引 ...
- 【opencv-c++】cv::ximgproc::thinning图像细化算法
[opencv-c++]cv::ximgproc::thinning图像细化算法 1.背景 2.示例 3.说明 1.背景 cv::ximgproc::thinning函数官方文档: https://d ...
- 图像细化 A fast parallel algorithm for thinning digital patterns
我这是测试了两个人的代码,似乎有些区别的,第二篇作者贴出来的代码还存在一些bug,我简单修改了一下,实现的效果上似乎是有一下差别,后续看看论文再做评价. 两个方法也都能满足一定的需求. 参考blog: ...
- opencv-之图像细化(直线细化)
平时在我们图像处理的时候会经常用到图像细化或者称为直线细化,在这里我跟大家分享一下我个人的理解,这是我花了一天的时间才搞明白其中的原理,具体的原理大家可以在其他博主的博客里搜索到,偶有详细的解释,我这 ...
- OpenCV——图像梯度与边缘检测(python实现)
OpenCV--图像梯度与边缘检测 6.1 简介 严格的说,梯度计算需要求导数.但是图像梯度的计算,是通过计算像素值的差得到梯度的近似值.图像梯度表示的是图像变化的速度,反映了图像的边缘信息. 边缘是 ...
- OpenCV图像梯度(Sobel和Scharr)
OpenCV图像梯度(Sobel和Scharr) 1 图像梯度是什么? 2 图像梯度的用途 3 图像梯度的使用 参考 这篇博客将介绍图像渐变以及如何使用OpenCV的cv2.Sobel计算Sobel渐 ...
- Python,OpenCV图像金字塔cv2.pyrUp(), cv2.pyrDown()
Python,OpenCV图像金字塔cv2.pyrUp, cv2.pyrDown 1. 效果图 2. 原理 2.1 什么是图像金字塔 2.2 金字塔分类 2.3 应用 3. 源码 参考 这篇博客将介绍 ...
- OpenCV图像缩放插值之BiCubic双三次插值
图像缩放算法简介 在图像的仿射变换中,很多地方需要用到插值运算,常见的插值运算包括最邻近插值,双线性插值,双三次插值(立体插值),兰索思插值等方法,OpenCV提供了很多方法,其中,双线性插值由于折中 ...
- python皮同_Python OpenCV 图像的双线性插值算法,全网最细致的算法说明_橡皮擦,一个逗趣的互联网高级网虫-CSDN博客...
原文作者:梦想橡皮擦 原文标题:Python OpenCV 图像的双线性插值算法,全网最细致的算法说明 发布时间:2021-02-17 20:55:32 Python OpenCV 365 天学习计划 ...
最新文章
- Oracle中比较日期大小
- [archlinux][hardware] 查看SSD的使用寿命
- 【XSY2519】神经元 prufer序列 DP
- Unity3D shader Blending
- java零碎要点---Tesseract 3.0,Java OCR 图像智能字符识别技术,可识别中文
- 磁盘文件排序-编程珠玑
- 前端小报 - 201812 月刊
- linux操作系统常用命令
- QT谷歌拼音输入法的移植
- HDU 1224 DFS
- 带农历和法定节假日的 日历控件_带节日和农历的js日历
- git fetch 出错“error:failed to run repack”
- htmltabl生成html表格并发送企业微信
- imx6q ssi1 配置成CPU为主,codec PCM从模式 SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS
- 【赛纳斯】EC Raman电化学拉曼光谱检测系统推动科研新突破
- CSDN-Adobe Flex/AIR专区——http://flex.csdn.net/ 很多资料
- java基础总结(九十)--AQS
- Windows手动清理C盘
- android xml黑体字_Android开发之字体设置
- Clojure 学习入门(6)—— 函数定义
热门文章
- 管理系统界面设计python_python图形化界面设计(wxpython)一布局管理
- element 修改分页样式_如何给wordpress网站的文章列表,添加分页效果?可以通过2种方式...
- 关于缓存雪崩\穿透\击穿等一些问题
- HTML中行内元素与块级元素的区别
- 用Java读取xml文件内容
- 前端开发环境(2017)
- Python开发基础 day5 函数的定义与使用
- 雅思8分大神叫你如何学习口语
- [家里蹲大学数学杂志]第034期中山大学2008年数学分析考研试题参考解答
- 两个线程,一个线程打印1~52,另一个线程打印字母A-Z,打印顺序为12A34B56C……5152Z...