【opencv】图像细化
2016-02-25 10:38
239 查看
http://blog.csdn.net/qianchenglenger/article/details/19332011
分类:
opencv(1)
版权声明:本文为博主原创文章,未经博主允许不得转载。
在我们进行图像处理的时候,有可能需要对图像进行细化,提取出图像的骨架信息,进行更加有效的分析。
图像细化(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.运行效果
【opencv】图像细化
2014-02-17 21:03 5333人阅读 评论(14) 收藏 举报分类:
opencv(1)
版权声明:本文为博主原创文章,未经博主允许不得转载。
在我们进行图像处理的时候,有可能需要对图像进行细化,提取出图像的骨架信息,进行更加有效的分析。
图像细化(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.运行效果
相关文章推荐
- Linux常用命令
- linux下设置SSH服务开机启动
- mac上的终端bash命令(一) Bourne-Again Shell简介\mac上的终端bash命令(二)基础
- 【Developer Log】ProGuard扰码web项目(WAR)
- Docker部署JavaWeb项目实战
- linux一些不常用单很有用的命令
- Linux 下Nagios的安装和配置
- Nginx Rewrite 规则
- 《Linux系统基础优化16条知识汇总》
- hadoop
- JS + shell 批量下载 喜马拉雅FM 的音频
- 在Xcode中搭建openCV开发环境
- 最全列表: 80 多个 Linux 系统管理员必备的监控工具
- NDK环境变量的配置以及编译方法(linux环境下)
- 要看的网站
- 【小镇的技术天梯】Linux shell的标准输入、输出和错误
- linux PATH变量
- mysql数据库插入汉字显示???(linux)问题解决
- 网站搭建【2】-云服务器购买
- SSH连接下复制远程linux服务器文件到本地的命令