昨天不是说同学问我怎么绘制出轮廓的中心线。然后我上网查了一下其实这个有专门的算法叫做细化算法。用专业术语去描述绘制出轮廓的中心线叫做(提取图像的骨架)。然后这一篇博客呢是我对这个细化算法的解读与实操~

一、thinning algorithm算法描述

图像细化(Image Thinning),一般指二值图像的骨架化(Image Skeletonization)的一种操作运算。切记:前提条件一定是二值图!
所谓的细化就是经过一层层的剥离,从原来的图中去掉一些点,但仍要保持原来的形状,直到得到图像的骨架。骨架,可以理解为图象的中轴。


细化算法有很多,我这里就着重讲一下ZHANG Algorithm

1.1 图像知识前提补充

对于二值图来说,我们可以看成由0,1组成的图像(也可以理解为矩阵)。在一副图中我们要去除多余的边界点保留连接点,端点,孤立点这些重要的图像节点。算法的整体思想就是用9个格子表示这些重要点,并去掉不必要的点。

1.2 细化算法步骤讲解

模板如下:

step1:

扫描图像,遇到为1的点时用上述模板,把为1的点作为P1(P1为前景点)如果同时满足以下4个条件,则另P1=0 (即删除P1点)。

  1. 2≤B(P1)≤62\leq B(P_1)\leq 62≤B(P1​)≤6
  2. A(P1)=1A(P_1)=1A(P1​)=1
  3. P2∗P4∗P6=0P_2*P_4*P_6=0P2​∗P4​∗P6​=0
  4. P4∗P6∗P8=0P_4*P_6*P8=0P4​∗P6​∗P8=0

A(P1)是以P2,P3.....P9为序时,这些点从0−>1变化的次数A_(P_1)是以P_2,P_3.....P_9为序时,这些点从0 ->1变化的次数A(​P1​)是以P2​,P3​.....P9​为序时,这些点从0−>1变化的次数
B(P1)是P2,P3.....P9,非零(即1)的个数B_(P_1)是P_2,P_3.....P_9,非零(即1)的个数B(​P1​)是P2​,P3​.....P9​,非零(即1)的个数

被判定为删除的点暂不删除,但要加以记录。等所有边界点都被判断完后,再一起将所有标记了的点删除,接下来进入第二阶段的删除步骤。


为什么要满足上述条件:
1、条件1为控制在端点和边界点之间。刚好端点时B(P1)=2条件1为控制在端点和边界点之间。刚好端点时B(P_1)=2条件1为控制在端点和边界点之间。刚好端点时B(P1​)=2,边界点最大B(P1)=8B(P_1)=8B(P1​)=8。所以当B(P1)∈[2,6]B(P_1)\in[2,6]B(P1​)∈[2,6]时不是关键点,可以考虑进一步条件用以删除。
2、当A(P1)>1A(P_1)>1A(P1​)>1时很有可能就是连接点,所以一定要保证A(P1)=1A(P_1)=1A(P1​)=1 。
如下面这个就是A(P1)>1A(P_1)> 1A(P1​)>1就是连接点。

3、 P2∗P4∗P6=0P_2*P_4*P_6=0P2​∗P4​∗P6​=0 和 P4∗P6∗P8=0P_4*P_6*P8=0P4​∗P6​∗P8=0 这两个条件我是这样理解的

step2:

按照如下条件进行第二阶段的删除,条件为

  1. 2≤B(P1)≤62\leq B(P_1)\leq 62≤B(P1​)≤6
  2. A(P1)=1A(P_1)=1A(P1​)=1
  3. P2∗P4∗P6=0P_2*P_4*P_6=0P2​∗P4​∗P6​=0
  4. P2∗P4∗P8=0P_2*P_4*P8=0P2​∗P4​∗P8=0
    同第一步一样,判定要删除的点只是加以记录而暂不删除,等待最后同时删除!对一副图像反复执行第一步与第二步的算法步骤,知道都没有可删除的点为止!!

第二阶段与第一阶段不同的就在最后一个条件,因为第一阶段只是移去东南的边界点,而不考虑西北的边界点,注意p4,p6出现了2次,就是说它们有一个为0,则c,d就满足。(第一阶段P4,P6P_4,P_6P4​,P6​出现了两次所以东南方向的点更有可能被删除)

第二次时P2,P8P_2,P_8P2​,P8​出现了两次所以西北方向的点更有可能被删除$

三、源代码

# 将char类型的01的图转为int的二位list
def intarray(binstring):'''Change a 2D matrix of 01 chars into a list of lists of ints'''return [[1 if ch == '1' else 0 for ch in line]for line in binstring.strip().split()]def toTxt(intmatrix):'''Change a 2d list of lists of 1/0 ints into lines of '#' and '.' chars'''return '\n'.join(''.join(('#' if p else '.') for p in row) for row in intmatrix)# 定义像素周围的8领域
#   P9 P2 P3
#   P8 P1 P4
#   P7 P6 P5
def neighbours(x, y, image):'''Return 8-neighbours of point p1 of picture, in order'''i = imagex1, y1, x_1, y_1 = x + 1, y - 1, x - 1, y + 1# print ((x,y))return [i[y1][x], i[y1][x1], i[y][x1], i[y_1][x1],  # P2,P3,P4,P5i[y_1][x], i[y_1][x_1], i[y][x_1], i[y1][x_1]]  # P6,P7,P8,P9# 计算领域中像素从0-1变化的次数
def transitions(neighbours):n = neighbours + neighbours[0:1]  # P2, ... P9, P2return sum((n1, n2) == (0, 1) for n1, n2 in zip(n, n[1:]))def zhangSuen(image):changing1 = changing2 = [(-1, -1)]while changing1 or changing2:# Step 1  循环所有前景像素点,符合条件的像素点标记为删除changing1 = []for y in range(1, len(image) - 1):for x in range(1, len(image[0]) - 1):P2, P3, P4, P5, P6, P7, P8, P9 = n = neighbours(x, y, image)if (image[y][x] == 1 and  # (Condition 0)P4 * P6 * P8 == 0 and  # Condition 4P2 * P4 * P6 == 0 and  # Condition 3transitions(n) == 1 and  # Condition 22 <= sum(n) <= 6):  # Condition 1changing1.append((x, y))# 步骤一结束后删除changing1中所有标记的点for x, y in changing1: image[y][x] = 0# Step 2 重复遍历图片,标记所有需要删除的点changing2 = []for y in range(1, len(image) - 1):for x in range(1, len(image[0]) - 1):P2, P3, P4, P5, P6, P7, P8, P9 = n = neighbours(x, y, image)if (image[y][x] == 1 and  # (Condition 0)P2 * P6 * P8 == 0 and  # Condition 4P2 * P4 * P8 == 0 and  # Condition 3transitions(n) == 1 and  # Condition 22 <= sum(n) <= 6):  # Condition 1changing2.append((x, y))# 步骤二结束后删除changing2中所有标记的点for x, y in changing2: image[y][x] = 0# 不断重复当changing1,changing2都为空的时候返回图像print("c1: ", changing1)print("c2", changing2)return imagerc01 = '''\
00000000000000000000000000000000000000000000000000000000000
01111111111111111100000000000000000001111111111111000000000
01111111111111111110000000000000001111111111111111000000000
01111111111111111111000000000000111111111111111111000000000
01111111100000111111100000000001111111111111111111000000000
00011111100000111111100000000011111110000000111111000000000
00011111100000111111100000000111111100000000000000000000000
00011111111111111111000000000111111100000000000000000000000
00011111111111111110000000000111111100000000000000000000000
00011111111111111111000000000111111100000000000000000000000
00011111100000111111100000000111111100000000000000000000000
00011111100000111111100000000111111100000000000000000000000
00011111100000111111100000000011111110000000111111000000000
01111111100000111111100000000001111111111111111111000000000
01111111100000111111101111110000111111111111111111011111100
01111111100000111111101111110000001111111111111111011111100
01111111100000111111101111110000000001111111111111011111100
00000000000000000000000000000000000000000000000000000000000\
'''if __name__ == '__main__':print(rc01)image = intarray(rc01)print(image)print('\nFrom:\n%s' % toTxt(image))after = zhangSuen(image)print('\nTo thinned:\n%s' % toTxt(after))

四、参考链接

参考论文 http://agcggs680.pbworks.com/f/Zhan-Suen_algorithm.pdf

https://rosettacode.org/wiki/Zhang-Suen_thinning_algorithm#C.2B.2B 感觉是作者自己的博客链接,这里有各种其他语言的该算法实现(英文)

https://nayefreza.wordpress.com/2013/05/11/zhang-suen-thinning-algorithm-java-implementation/ 英文

https://nayefreza.wordpress.com/2013/05/11/zhang-suen-thinning-algorithm-java-implementation/ 英文

https://blog.csdn.net/jia20003/article/details/52142992 中文。推荐英语基础稍微弱一点的小伙伴先从这一篇看起,如果还有啥不明白的可以再看上面这几个链接~、

五、总结

其实这个算法我还是有一个地方不是很能弄明白,就是
P2∗P4∗P6=0和P4∗P6∗P8=0P_2*P_4*P_6=0和 P_4*P_6*P8=0P2​∗P4​∗P6​=0和P4​∗P6​∗P8=0 这个地方。虽然我知道他要这样做,但是总是找不到一个比较好的正面解释。找了几篇博客也看了论文,在这个地方都没有解释的很清楚o(╥﹏╥)o
希望如果有小伙伴能更好的解释这个地方的可以给我留言哦_(:з」∠)_

OpenCV-细化算法(thinning algorithm)算法详解——提取二值图的骨架相关推荐

  1. OpenCV参考手册之Mat类详解(二)

    Mat::~Mat Mat的析构函数. C++: Mat::~Mat() 析构函数调用Mat::release(). Mat::operator = 提供矩阵赋值操作. C++: Mat& M ...

  2. UML详解之二——类图

    转载请标明出处:http://blog.csdn.net/xx326664162/article/details/50475352 文章出自:薛瑄的博客 你也可以查看我的其他同类文章,也会让你有一定的 ...

  3. OpenCV参考手册之Mat类详解

    OpenCV参考手册之Mat类详解(一) OpenCV参考手册之Mat类详解(二) OpenCV参考手册之Mat类详解(三)

  4. EM算法(Expectation Maximization Algorithm)详解

    EM算法(Expectation Maximization Algorithm)详解 主要内容 EM算法简介 预备知识  极大似然估计 Jensen不等式 EM算法详解  问题描述 EM算法推导 EM ...

  5. 操作系统:基于页面置换算法的缓存原理详解(下)

    概述: 在上一篇<操作系统:基于页面置换算法的缓存原理详解(上)>中,我们主要阐述了FIFO.LRU和Clock页面置换算法.接着上一篇说到的,本文也有三个核心算法要讲解.分别是LFU(L ...

  6. OpenCV均值漂移meanshift algorithm算法的实例(附完整代码)

    OpenCV均值漂移meanshift algorithm算法的实例 OpenCV均值漂移meanshift algorithm算法的实例 OpenCV均值漂移meanshift algorithm算 ...

  7. 希尔排序基础java代码_java 算法之希尔排序详解及实现代码

    摘要:这篇Java开发技术栏目下的"java 算法之希尔排序详解及实现代码",介绍的技术点是"希尔排序详解.实现代码.希尔排序.Java.实现.代码",希望对大 ...

  8. kmeans python interation flag_机器学习经典算法-logistic回归代码详解

    一.算法简要 我们希望有这么一种函数:接受输入然后预测出类别,这样用于分类.这里,用到了数学中的sigmoid函数,sigmoid函数的具体表达式和函数图象如下: 可以较为清楚的看到,当输入的x小于0 ...

  9. python实验原理_Python实现蒙特卡洛算法小实验过程详解

    蒙特卡洛算法思想 蒙特卡洛(Monte Carlo)法是一类随机算法的统称,提出者是大名鼎鼎的数学家冯·诺伊曼,他在20世纪40年代中期用驰名世界的赌城-摩纳哥的蒙特卡洛来命名这种方法. 通俗的解释一 ...

最新文章

  1. android获取string.xml的值(转)
  2. 深度学习-Tensorflow2.2-深度学习基础和tf.keras{1}-tf.keras函数式API-08
  3. linux/windows中mysql、oracle、dm数据库连接
  4. ONAP发布“阿姆斯特丹”版本,为网络服务自动化制定标准
  5. php读取移动硬盘数据,移动硬盘打不开,数据怎么恢复?
  6. 简单实用的js调试logger组件
  7. 编译php源码错误集与解决
  8. 详解各类以太网标准10BASE-T/100BASE-T4/100BASE-FX/1000BASE-X等
  9. Android开发:《Gradle Recipes for Android》阅读笔记1.3
  10. 程序员面试金典 - 面试题 16.15. 珠玑妙算(map计数)
  11. 视频编码技术---压缩感知编码---匹配跟踪算法
  12. python 自动复制分类_leetcode python 常见分类问题模板(复制粘贴就能用) 更新中......
  13. 关于字符匹配所引起的的问题
  14. 智能问答:LSTM 句子相似度分析
  15. python自动化运维工程师面试题_运维面试题(含答案)
  16. JS中clientHeight、scrollHeight、offsetHeight、scrollTop、offsetTop的定义
  17. android模拟触控power键
  18. wordpress tittle 烦人的书名号
  19. RuntimeError: generator raised StopIteration
  20. 【弄nèng - Activiti6】Activiti6入门篇(二十一)—— 事务子流程

热门文章

  1. Telegram、Telethon
  2. 第五人格显示连接服务器失败怎么办,第五人格提示重新连接服务器怎么办 连接服务器失败解决方法...
  3. 学网络必备50个知识点
  4. 卡方分布(Chi-Squared Distribution)
  5. 八年级作文-断了的弦
  6. 网易mumu模拟器去广告纯净版 v1.26.1.1
  7. ARM版本ubuntu安装PL2303驱动
  8. Imagination和浙江大学信电学院签署合作协议,校企共创大学课程新篇章
  9. (Window环境) curl: (6) Could not resolve host: application curl: (6) Could not resolve host: ‘localhos
  10. mysql增加重做日志组_mysql重做日志