【opencv】图像细化
【原文:http://blog.csdn.net/qianchenglenger/article/details/19332011】
在我们进行图像处理的时候,有可能需要对图像进行细化,提取出图像的骨架信息,进行更加有效的分析。
图像细化(Image Thinning),一般指二值图像的骨架化(Image Skeletonization)
的一种操作运算。
所谓的细化就是经过一层层的剥离,从原来的图中去掉一些点,但仍要保持原来的形状,直到得到图像的骨架。骨架,可以理解为图象的中轴。
好的细化算法一定要满足:
- 收敛性;
- 保证细化后细线的连通性;
- 保持原图的基本形状;
- 减少笔画相交处的畸变;
- 细化结果是原图像的中心线;
- 细化的快速性和迭代次数少;
这里,我们对"Zhang并行快速细化算法"进行了实现(注意,该算法为并行算法,而我们在实现过程中并没有并行化处理,所以,效率并没有达到最好)。
参考资料
细化算法
论文 A fast parallel algorithm for thinning digital patterns
[cpp] view plaincopy

- #include <opencv2/opencv.hpp>
- #include <iostream>
- #include <vector>
- #include <limits>
- using namespace cv;
- using namespace std;
- /**
- * @brief 对输入图像进行细化
- 位灰度图像格式,元素中只有0与1,1代表有元素,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.运行效果

【opencv】图像细化的更多相关文章
- OpenCV图像细化的一个例子
转自:http://blog.csdn.net/zfdxx369/article/details/9091953?utm_source=tuicool 本文是zhang的一篇经典图像细化论文,效果很好 ...
- opencv 图像细化
图像细化多用于机器人视觉,OCR字符识别等领域,细化后的图像经过去毛刺就成为了我们常说的图像的骨架. 该图像细化代码依据论文: T. Y. ZHANG and C. Y. SUEN A Fast P ...
- SSE图像算法优化系列三十二:Zhang\Guo图像细化算法的C语言以及SIMD指令优化
二值图像的细化算法也有很多种,比较有名的比如Hilditch细化.Rosenfeld细化.基于索引表的细化.还有Opencv自带的THINNING_ZHANGSUEN.THINNING_GUOHALL ...
- OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放
这篇已经写得很好,真心给作者点个赞.题目都是直接转过来的,直接去看吧. Reference Link : http://blog.csdn.net/poem_qianmo/article/detail ...
- 【OpenCV新手教程之十三】OpenCV图像金字塔:高斯金字塔、拉普拉斯金字塔与图片尺寸缩放
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26157633 作者:毛星云(浅墨) ...
- Opencv 图像叠加 添加水印
Opencv 图像叠加 添加水印 C++: void Mat::copyTo(OutputArray m) const C++: void Mat::copyTo(OutputArray m, Inp ...
- opencv图像读取-imread
前言 图像的读取和保存一定要注意imread函数的各个参数及其意义,尽量不要使用默认参数,否则就像数据格式出现错误(here)一样,很难查找错误原因的: re: 1.opencv图像的读取与保存; 完
- 学习 opencv---(12)OpenCV 图像金字塔:高斯金字塔,拉普拉斯金字塔与图片尺寸缩放
在这篇文章里,我们一起学习下 图像金字塔 的一些基本概念,如何使用OpenCV函数pyrUp和pyrDown 对图像进行向上和向下采样,以及了解专门用于缩放图像尺寸的resize函数的用法.此博文一共 ...
- [OpenCV Qt教程] 在Qt图形界面中显示OpenCV图像的OpenGL Widget(第二部分)
本文译自:http://www.robot-home.it/blog/en/software/tutorial-opencv-qt-opengl-widget-per-visualizzare-imm ...
随机推荐
- 简单3d RPG游戏 之 002 生命条(二)
在游戏中,游戏人物的血条可能会因为受伤或吃血瓶而长度变化,所以需要将血条的长度单独提出来作为一个变量,方便直接修改数值. public float healthBarLength; 改变生命值函数如下 ...
- HTML元素大小和位置相对于谁的问题
一.如果元素的position属性值为absolute,那么此时分为两种情况: 1. 父级(包括直接父级和间接父级)元素中没有定义position属性(即默认属性static),那么该元素会参照页面, ...
- 1071: [SCOI2007]组队 - BZOJ
Description NBA每年都有球员选秀环节.通常用速度和身高两项数据来衡量一个篮球运动员的基本素质.假如一支球队里速度最慢的球员速度为minV,身高最矮的球员高度为minH,那么这支球队的所有 ...
- 1018: [SHOI2008]堵塞的交通traffic - BZOJ
Description 有一天,由于某种穿越现象作用,你来到了传说中的小人国.小人国的布局非常奇特,整个国家的交通系统可以被看成是一个2行C列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一 ...
- python繁体中文到简体中文的转换
处理中文字符串遇到了繁体和简体中文的转换,python版: 1.下载zh_wiki.py及langconv zh_wiki.py:https://github.com/skydark/nstool ...
- PowerDesigner(五)-概念数据模型(CDM生成LDM,PDM和OOM)
概念数据模型 概念数据模型(Conceptual Data Model,CDM):表达的是数据整体逻辑结构,该结构独立于任何软件和数据存储结构,即它只是系统分析人员,应用程序设计人员,维护人员和用户之 ...
- Uploadify 控件上传图片 + 预览
jquery的Uploadify控件上传图片和预览使用介绍. 在简单的servlet系统中和在SSH框架中,后台处理不同的,在三大框架中图片预览时费了不少力气,所以下面将两种情况都介绍一下. 1,前台 ...
- python学习笔记二--列表
一.列表: 1. 任意类型对象的位置相关的有序集合. 2. 没有固定大小. 3. 对偏移量进行赋值及各种方法的调用,修改列表. 4. 列表是序列的一种. 5. 所有对字符串的序列操作对列表均适用. 二 ...
- CentOS 命令随笔
linux下敲命令时:快速删除当前行已经敲的命令: CTR+U 或者 CTR+/ 快速删除当前行刚输入接近鼠标当前位置的单词:CTR+W 以上在XS ...
- linux应用程序问题
---- 1 ----