细化算法它的原理也很简单:

我们对一副二值图像进行骨架提取,就是删除不需要的轮廓点,只保留其骨架点。假设一个像素点,我们定义该点为p1,则它的八邻域点p2->p9位置如下图所示,该算法考虑p1点邻域的实际情况,以便决定是否删除p1点。假设我们处理的为二值图像,背景为黑色,值为0,要细化的前景物体像素值为1。

算法的描述如下:

  

首先复制源图像到目地图像,然后建立一个临时图像,接着执行下面操作:

1. 把目地图像复制给临时图像,对临时图像进行一次扫描,对于不为0的点,如果满足以下四个条件,则在目地图像中删除该点(就是设置该像素为0),这里p2,…,p9是对应位置的像素灰度值(其为1或者0)。

a. 2<= p2+p3+p4+p5+p6+p7+p8+p9<=6

大于等于2会保证p1点不是端点或孤立点,因为删除端点和孤立点是不合理的,小于等于6保证p1点是一个边界点,而不是一个内部点。等于0时候,周围没有等于1的像素,所以p1为孤立点,等于1的时候,周围只有1个灰度等于1的像素,所以是端点(注:端点是周围有且只能有1个值为1的像素)。

b. p2->p9的排列顺序中,01模式的数量为1,比如下面的图中,有p2p3 => 01, p6p7=>01,所以该像素01模式的数量为2。

之所以要01模式数量为1,是要保证删除当前像素点后的连通性。比如下面的图中,01模式数量大于1,如果删除当前点p1,则连通性不能保证。

c. P2*p4*p6 = 0

d. p4*p6*p8 = 0

在第一次子迭代中,只是移去东南的边界点,而不考虑西北的边界点,注意p4,p6出现了2次,就是说它们有一个为0,则c,d就满足。

2. 接下来,把目地图像再次复制到临时图像,接着对临时图像进行一次扫描,如果不为0的点它的八邻域满足以下4个条件,则在目地图像中删除该点(就是设置该像素为0)

a. 2<= p2+p3+p4+p5+p6+p7+p8+p9<=6

b. p2->p9的排列顺序中,01模式的数量(这里假设二值图非零值为1)为1。

c. p2*p4*p8 = 0

d. p2*p6*p8 = 0

第二次迭代则相反,会移去西北的边界点,注意p2,p8出现了2次,就是说它们有一个为0,则c,d就满足。

执行完上面两个步骤后,就完成了一次细化算法,我们可以多次迭代执行上述过程,得到最终的骨架图。

细化算法代码如下:

以上部分原理是转载(迈克老狼2012)http://www.cnblogs.com/mikewolf2002/p/3321732.html

接下来发布的是在我电脑上通过的测试代码:

编译环境为win7+VS2015+OpenCV3.0

 void cvThin(IplImage* src, IplImage* dst, int iterations)
{
//此时的src是一个二值化的图片
CvSize size = cvGetSize(src);
cvCopy(src, dst); int n = , i = , j = ;
for (n = ; n < iterations; n++)//开始进行迭代
{
IplImage* t_image = cvCloneImage(dst);
for (i = ; i < size.height; i++)
{
for (j = ; j < size.width; j++)
{
if (CV_IMAGE_ELEM(t_image, uchar, i, j) == )
{
int ap = ;
int p2 = (i == ) ? : CV_IMAGE_ELEM(t_image, uchar, i - , j);
int p3 = (i == || j == size.width - ) ? : CV_IMAGE_ELEM(t_image, uchar, i - , j + );
if (p2 == && p3 == )
{
ap++;
} int p4 = (j == size.width - ) ? : CV_IMAGE_ELEM(t_image, uchar, i, j + );
if (p3 == && p4 == )
{
ap++;
} int p5 = (i == size.height - || j == size.width - ) ? : CV_IMAGE_ELEM(t_image, uchar, i + , j + );
if (p4 == && p5 == )
{
ap++;
} int p6 = (i == size.height - ) ? : CV_IMAGE_ELEM(t_image, uchar, i + , j);
if (p5 == && p6 == )
{
ap++;
} int p7 = (i == size.height - || j == ) ? : CV_IMAGE_ELEM(t_image, uchar, i + , j - );
if (p6 == && p7 == )
{
ap++;
} int p8 = (j == ) ? : CV_IMAGE_ELEM(t_image, uchar, i, j - );
if (p7 == && p8 == )
{
ap++;
} int p9 = (i == || j == ) ? : CV_IMAGE_ELEM(t_image, uchar, i - , j - );
if (p8 == && p9 == )
{
ap++;
}
if (p9 == && p2 == )
{
ap++;
} if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) > && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) < )
{
if (ap == )
{
if (!(p2 && p4 && p6))
{
if (!(p4 && p6 && p8))
{
CV_IMAGE_ELEM(dst, uchar, i, j) = ;//设置目标图像中像素值为0的点
}
}
}
} }
}
} cvReleaseImage(&t_image); t_image = cvCloneImage(dst);
for (i = ; i < size.height; i++)
{
for (int j = ; j < size.width; j++)
{
if (CV_IMAGE_ELEM(t_image, uchar, i, j) == )
{
int ap = ;
int p2 = (i == ) ? : CV_IMAGE_ELEM(t_image, uchar, i - , j);
int p3 = (i == || j == size.width - ) ? : CV_IMAGE_ELEM(t_image, uchar, i - , j + );
if (p2 == && p3 == )
{
ap++;
}
int p4 = (j == size.width - ) ? : CV_IMAGE_ELEM(t_image, uchar, i, j + );
if (p3 == && p4 == )
{
ap++;
}
int p5 = (i == size.height - || j == size.width - ) ? : CV_IMAGE_ELEM(t_image, uchar, i + , j + );
if (p4 == && p5 == )
{
ap++;
}
int p6 = (i == size.height - ) ? : CV_IMAGE_ELEM(t_image, uchar, i + , j);
if (p5 == && p6 == )
{
ap++;
}
int p7 = (i == size.height - || j == ) ? : CV_IMAGE_ELEM(t_image, uchar, i + , j - );
if (p6 == && p7 == )
{
ap++;
}
int p8 = (j == ) ? : CV_IMAGE_ELEM(t_image, uchar, i, j - );
if (p7 == && p8 == )
{
ap++;
}
int p9 = (i == || j == ) ? : CV_IMAGE_ELEM(t_image, uchar, i - , j - );
if (p8 == && p9 == )
{
ap++;
}
if (p9 == && p2 == )
{
ap++;
}
if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) > && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) < )
{
if (ap == )
{
if (p2*p4*p8 == )
{
if (p2*p6*p8 == )
{
CV_IMAGE_ELEM(dst, uchar, i, j) = ;
}
}
}
}
} } }
cvReleaseImage(&t_image);
} }

细化结果因为我的图不方便展示,大家可以参考迈克老狼2012的哦,效果是很像的,他采用的mat类,而我采用的iplimage类。

OpenCV细化算法简单解析的更多相关文章

  1. 手指静脉细化算法过程原理解析 以及python实现细化算法

    原文作者:aircraft 原文地址:https://www.cnblogs.com/DOMLX/p/8672489.html 文中的一些图片以及思想很多都是参考https://www.cnblogs ...

  2. OpenCV学习(18) 细化算法(6)

    本章我们在学习一下基于索引表的细化算法. 假设要处理的图像为二值图,前景值为1,背景值为0. 索引表细化算法使用下面的8邻域表示法: 一个像素的8邻域,我们可以用8位二进制表示,比如下面的8邻域,表示 ...

  3. OpenCV学习(13) 细化算法(1)

    程序编码参考经典的细化或者骨架算法文章: T. Y. Zhang and C. Y. Suen, "A fast parallel algorithm for thinning digita ...

  4. OpenCV学习(17) 细化算法(5)

    本章我们看下Pavlidis细化算法,参考资料http://www.imageprocessingplace.com/downloads_V3/root_downloads/tutorials/con ...

  5. OpenCV学习(16) 细化算法(4)

    本章我们学习Rosenfeld细化算法,参考资料:http://yunpan.cn/QGRjHbkLBzCrn 在开始学习算法之前,我们先看下连通分量,以及4连通性,8连通性的概念: http://w ...

  6. OpenCV学习(14) 细化算法(2)

          前面一篇教程中,我们实现了Zhang的快速并行细化算法,从算法原理上,我们可以知道,算法是基于像素8邻域的形状来决定是否删除当前像素.还有很多与此算法相似的细化算法,只是判断的条件不一样. ...

  7. c++opencv中线条细化算法

    要达到的效果就是将线条尽量细化成单像素,按照论文上的Hilditch算法试了一下,发现效果不好,于是自己尝试着写了一下细化的算法,基本原理就是从上下左右四个方向向内收缩. 1.先是根据图片中的原则确定 ...

  8. Adaboost 算法实例解析

    Adaboost 算法实例解析 1 Adaboost的原理 1.1 Adaboost基本介绍 AdaBoost,是英文"Adaptive Boosting"(自适应增强)的缩写,由 ...

  9. 2. Attention Is All You Need(Transformer)算法原理解析

    1. 语言模型 2. Attention Is All You Need(Transformer)算法原理解析 3. ELMo算法原理解析 4. OpenAI GPT算法原理解析 5. BERT算法原 ...

随机推荐

  1. delphi xe6 调用java GPS的方法

    如果用xe6自带的LocationSensor控件,默认优先使用网络位置,网络位置定位精度不准确,不能满足高精度定位的要求.但xe6自带的LocationSensor控件不能指定网络定位优先还是GPS ...

  2. 不应该使用Connected属性作为Socket是否连接上的依据

    最近在做一个接口,用到了Socket异步通信. 调试了3天了,一直将Socket的Connected属性作为客户端和服务器端是否连接上的依据.今天发现我错了. 下面是从一个csdn博友写的,很好. h ...

  3. SQL server 提取字符中第一次和最后一次出现的数字

    CREATE FUNCTION [dbo].[StringExtractNumber(FirstOrLast)](@address nvarchar(max),@firstOrLast INT) re ...

  4. linux线程切换和进程切换

    进程切换分两步: 1.切换页目录以使用新的地址空间 2.切换内核栈和硬件上下文 对于linux来说,线程和进程的最大区别就在于地址空间,对于线程切换,第1步是不需要做的,第2是进程和线程切换都要做的. ...

  5. Qt 学习之路 2(22):事件总结

    Qt 学习之路 2(22):事件总结 豆子 2012年10月16日 Qt 学习之路 2 47条评论 Qt 的事件是整个 Qt 框架的核心机制之一,也比较复杂.说它复杂,更多是因为它涉及到的函数众多,而 ...

  6. C语言中typedef的解释_2

    typedef工具是一个高级数据特性.利用typedef可以为某一类型自定义一个新的名称.这样可以提高程序的可读性,可移植性,向用户表明特定用途. typedef没有创建任何新的类型,它只是为某个已存 ...

  7. P4824 [USACO15FEB]Censoring (Silver) 审查(银)

    传送门 一个串的匹配肯定考虑KMP 那就暴力KMP 记录一下到每个字符时匹配的位置 找到一个符合的串就标记然后暴力回跳 感觉好像太暴力了... #include<iostream> #in ...

  8. ST表(查询区间最值问题)

    ST表与线段树相比,这是静态的,无法改动,但是他的查询速度比线段树要快,这是牺牲空间换时间的算法. O(nlogn)预处理,O(1)查询.空间O(nlogn). ][]; ]; void rmq_in ...

  9. The MathType DLL cannot be found 一劳永逸的方法

    可能会看到下面的情况,然后实际上我们也能用过外部打开直接使用,那要你何用? 于是,我们找到这个文件,删除就OK 反正我写完论文就卸载了...

  10. Office 下载地址

    Office Professional Plus 2013 64位简体中文版文件名: cn_office_professional_plus_2013_x64_dvd_1134006.iso语言: C ...