用80行代码数1000个孔——《Python也可以》之四
年前在狗东新买了一个茶滤,店家号称有921个孔,我觉得好像有点“夸张”,总想证实一下。
肉眼肯定是数不过来了,所以我就想着要不写个程序来数吧,就拍了张照片放着。年前太忙了,然后又是过年,昨天终于有空了,便写了个80行的小脚本数了一下。
预处理
因为是白瓷,所以洞孔最好全黑,于是拍照的时候我就在下面放了个黑色的无纺布袋子,拍出来的效果还可以。
用微信传到电脑上,然后简单地用macOS自带的图像app处理一下,主要是去掉边缘无关元素和调整明暗。
预处理完成以后如下图:
概述
总体来说,这是一个很简单的任务,大概的流程是把图像二值化(黑白),只留下黑色的孔洞和白色的边缘,如下图。
可以看到有一些干扰因素,比如正下方的阴影和右上方的孔洞反光,需要做一下开运算去除掉干扰掉。这时候我们可以发现我们只要统计黑色区域(连通域)的数量就是孔洞的数量了,所以整体的代码框架如下:
# 打开
im = Image.open("WechatIMG1039.png")
w, h = im.size
# 二值化
im = im.convert('1')
pixel_data = im.load()
# 做几次开闭运算提升对比度
for i in range(3):# 用腐蚀算法去除杂点corrode(pixel_data, w, h)# 用膨胀算法填上空白expand(pixel_data, w, h)
# 统计连通域
print(f'holes count: {count_connected_area(pixel_data, w, h)}')
算法
腐蚀
腐蚀算法可以去掉孤点,是去噪的一种方法。在这里简单的把周边8邻点里黑色点不到2个的点定义为孤点,然后设置为白色去掉。
def corrode(pixel_data, w, h):for x in range(w):for y in range(h):if count_black8(pixel_data, x, y, w, h) < 2:# 设置为白色pixel_data[x, y] = WHITE
膨胀
膨胀算法是腐蚀算法的反操作,在这里简单的把周边8邻点里黑色点超过4个的点定义为设置为黑色,这样可以填上孔洞和扩展黑色区域。
def expand(pixel_data, w, h):for x in range(w):for y in range(h):if count_black8(pixel_data, x, y, w, h) > 4:# 设置为黑色pixel_data[x, y] = BLACK
统计连通域
所谓连通域就是一个黑色区块,我们从左上角开始遍历,发现一个黑色就扩展它的8邻点,然后再一步一步扩展开来,最后统计它的点阵数量。
def count_connected_area(pixel_data, w, h):connected = []for x in range(w):for y in range(h):if pixel_data[x, y] != BLACK:continuepixel_data[x, y] = WHITEpixels = [(x, y)]area = probe(pixel_data, x, y, w, h)while area:pixels.extend(area)tmp = []for px, py in area:tmp.extend(probe(pixel_data, px, py, w, h))area = tmpconnected.append(len(pixels))return reduce(lambda s, x: s + 1 if 30 < x < 500 else s, connected, 0)
我在这里使用的是Seed-Filling方法,比较慢,但胜在简单明了,反正我只是写个小脚本玩的,不在乎性能,就无所谓了。还有一种Two-Pass方法,比较快,但复杂些;也可以直接用OpenCV的算法,比自己手写可能要快个一二十倍。
全部代码
小脚本很简单,全部代码80行。
from functools import reduce
from PIL import ImageBLACK = 0
WHITE = 1def extend8(x, y, w, h):return ((x1, y1) for x1, y1 in ((x-1, y-1), (x, y-1), (x+1, y-1),(x-1, y), (x+1, y),(x-1, y+1), (x, y+1), (x+1, y+1),) if x1 >= 0 and y1 >= 0 and x1 < w and y1 < h)def count_black8(pixel_data, x, y, w, h):return reduce(lambda s, x: s + 1 if pixel_data[x] == BLACK else s, extend8(x, y, w, h), 0)def corrode(pixel_data, w, h):for x in range(w):for y in range(h):if count_black8(pixel_data, x, y, w, h) < 2:# 设置为白色pixel_data[x, y] = WHITEdef expand(pixel_data, w, h):for x in range(w):for y in range(h):if count_black8(pixel_data, x, y, w, h) > 4:# 设置为黑色pixel_data[x, y] = BLACKdef probe(pixel_data, x, y, w, h):area = []for x1, y1 in extend8(x, y, w, h):if pixel_data[x1, y1] == BLACK:pixel_data[x1, y1] = WHITEarea.append((x1, y1))return areadef count_connected_area(pixel_data, w, h):connected = []for x in range(w):for y in range(h):if pixel_data[x, y] != BLACK:continuepixel_data[x, y] = WHITEpixels = [(x, y)]area = probe(pixel_data, x, y, w, h)while area:pixels.extend(area)tmp = []for px, py in area:tmp.extend(probe(pixel_data, px, py, w, h))area = tmpconnected.append(len(pixels))return reduce(lambda s, x: s + 1 if 30 < x < 500 else s, connected, 0)# 打开
im = Image.open("WechatIMG1039.png")
w, h = im.size
# 二值化
im = im.convert('1')
pixel_data = im.load()
# 做几次开运算提升对比度
for i in range(3):# 用腐蚀算法去除杂点corrode(pixel_data, w, h)# 用膨胀算法填上空白expand(pixel_data, w, h)
# 统计连通域
print(f'holes count: {count_connected_area(pixel_data, w, h)}')
# 目视查看是不是图像全都白了。
im.show()
最后计算出来的结果是922个孔,居然比店家说的还多1个,看来是个诚信店家,可以买它!
其它
这种方法主要的理论基础是数学形态学,是一门很通用的学科,不仅可以用来数孔洞,还可以用来数血小板、细菌等等,应用广泛。
另,发现我十几年前写的《用Python做图像处理》还是最简明好用的PIL教程,我用它重新学习了PIL。
用80行代码数1000个孔——《Python也可以》之四相关推荐
- 80行代码实现简易版摩斯码编译器
@80行代码实现简易摩斯码翻译器 dict1={ #字母转换成摩斯码"A":"01","B":"1000"," ...
- python tkinter计算器实例_Python+tkinter使用80行代码实现一个计算器实例
Python+tkinter使用80行代码实现一个计算器实例 本文主要探索的是使用Python+tkinter编程实现一个简单的计算器代码示例,具体如下. 闲话不说,直奔主题.建议大家跟着敲一遍代码, ...
- resnet50代码_13、SOTA论文实践-学习ResNet(80行代码搞定残差backbone网络)
0.论文 Camera Distance-aware Top-down Approach for 3D Multi-person Pose Estimation from a Single RGB I ...
- python爬虫都能干什么用_5 行代码就能写一个 Python 爬虫
欢迎关注我的公众号:第2大脑,或者博客:高级农民工,阅读体验更好. 摘要:5 行代码就能写一个 Python 爬虫. 如果你是比较早关注我的话,会发现我此前的大部分文章都是在写 Python 爬虫,前 ...
- 5分钟,10行代码!带你用Python做个电脑文件清道夫!
"菜鸟学Python",第"508"篇原创 大家好,我是菜鸟哥!新的一周来啦,Python学起来! 大家在日常的工作和学习中,会面临到许多文件的处理,包括各种o ...
- (已加马赛克)10 行代码判定色*情*图片——Python 也可以系列之二
10 行代码判定色*情*图片--Python 也可以系列之二 作者:赖勇浩(http://blog.csdn.net/lanphaday) 致编辑:我已经给图片打上马赛克了,别再删除了啊,我这是纯技术 ...
- (已加马赛克)10 行代码判定色 情 图片——Python 也可以系列之二
10 行代码判定色*情*图片--Python 也可以系列之二 作者:赖勇浩(http://blog.csdn.net/lanphaday) 致编辑:我已经给图片打上马赛克了,别再删除了啊,我这是纯技术 ...
- Python从入门到高手的80行代码
文章目录 基础入门 菜鸟提升 基础晋级 高手之路 内置包库 奇技淫巧 最新版: Python从入门到入土的90行代码 基础入门 1 python 即在命令行输入python,进入Python的开发环境 ...
- 几行代码完成动态图表绘制 | Python实战
作者 | 小F 来源 | 法纳斯特 头图 | CSDN下载自视觉中国 关于动态条形图,小F以前推荐过「Bar Chart Race」这个库.三行代码就能实现动态条形图的绘制. 有些同学在使用的时候,会 ...
最新文章
- python保存图片到指定路径_python将处理好的图像保存到指定目录下的方法
- [Java基础] 深入jar包:从jar包中读取资源文件
- Windbg SOS and CLR版本不一致的解决方案
- java网络图片与二进制字符串相互转换
- mysql用大白话解释_大白话解释给小白如何看别人的源码(一)数据库部分
- awr报告 解读_AWR报告分析解读
- PX4 vision_to_mavros定位
- 关于Google神牛Jeff Dean的笑话,非程序员勿入
- D-Bus 性能分析
- matlab2018历史命令在哪,2018美赛准备之路——Matlab基础——命令行功能函数
- 如何让语音芯片与功放芯片之间更好的配合,使得产品音效更好
- js上传文件到OSS
- 干货丨爱奇艺CDN IPv6系统配置
- Win10专业版永久激活
- WebSocket 原理 1
- web应用登录,报错空指针java.lang.NullPointerException问题--可能的解决方式
- 记录:手机邮箱格式验证
- 电脑如何同时远程控制多台手机
- Mac 开发 打开系统偏好设置
- MinGW下载和安装教程
热门文章
- 解决XP下鼠标单击变双击的问题
- 有趣的定律1. 帕金森定律
- Python版见缝插针小游戏源代码,球球旋转大作战源程序
- GitHub(九):掌握 Issues
- 剑指 Offer II 002. 二进制加法
- 会声会影X10 64位整合光盘V10.1.0.14简体中文版 下载
- 年月日格式判断-正则表达式 YYYY/MM/DD、YYYY/MM/DD| YY/MM/DD、 ^(^(\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2}$)|(^\d{4}…
- 基于Cri-dockerd使用Kubeadm部署Kubernetes1.25集群
- 微信小程序彩票号码生成器
- Spring体系下单例策略模式,java策略模式最佳实践