年前在狗东新买了一个茶滤,店家号称有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也可以》之四相关推荐

  1. 80行代码实现简易版摩斯码编译器

    @80行代码实现简易摩斯码翻译器 dict1={ #字母转换成摩斯码"A":"01","B":"1000"," ...

  2. python tkinter计算器实例_Python+tkinter使用80行代码实现一个计算器实例

    Python+tkinter使用80行代码实现一个计算器实例 本文主要探索的是使用Python+tkinter编程实现一个简单的计算器代码示例,具体如下. 闲话不说,直奔主题.建议大家跟着敲一遍代码, ...

  3. resnet50代码_13、SOTA论文实践-学习ResNet(80行代码搞定残差backbone网络)

    0.论文 Camera Distance-aware Top-down Approach for 3D Multi-person Pose Estimation from a Single RGB I ...

  4. python爬虫都能干什么用_5 行代码就能写一个 Python 爬虫

    欢迎关注我的公众号:第2大脑,或者博客:高级农民工,阅读体验更好. 摘要:5 行代码就能写一个 Python 爬虫. 如果你是比较早关注我的话,会发现我此前的大部分文章都是在写 Python 爬虫,前 ...

  5. 5分钟,10行代码!带你用Python做个电脑文件清道夫!

    "菜鸟学Python",第"508"篇原创 大家好,我是菜鸟哥!新的一周来啦,Python学起来! 大家在日常的工作和学习中,会面临到许多文件的处理,包括各种o ...

  6. (已加马赛克)10 行代码判定色*情*图片——Python 也可以系列之二

    10 行代码判定色*情*图片--Python 也可以系列之二 作者:赖勇浩(http://blog.csdn.net/lanphaday) 致编辑:我已经给图片打上马赛克了,别再删除了啊,我这是纯技术 ...

  7. (已加马赛克)10 行代码判定色 情 图片——Python 也可以系列之二

    10 行代码判定色*情*图片--Python 也可以系列之二 作者:赖勇浩(http://blog.csdn.net/lanphaday) 致编辑:我已经给图片打上马赛克了,别再删除了啊,我这是纯技术 ...

  8. Python从入门到高手的80行代码

    文章目录 基础入门 菜鸟提升 基础晋级 高手之路 内置包库 奇技淫巧 最新版: Python从入门到入土的90行代码 基础入门 1 python 即在命令行输入python,进入Python的开发环境 ...

  9. 几行代码完成动态图表绘制 | Python实战

    作者 | 小F 来源 | 法纳斯特 头图 | CSDN下载自视觉中国 关于动态条形图,小F以前推荐过「Bar Chart Race」这个库.三行代码就能实现动态条形图的绘制. 有些同学在使用的时候,会 ...

最新文章

  1. python保存图片到指定路径_python将处理好的图像保存到指定目录下的方法
  2. [Java基础] 深入jar包:从jar包中读取资源文件
  3. Windbg SOS and CLR版本不一致的解决方案
  4. java网络图片与二进制字符串相互转换
  5. mysql用大白话解释_大白话解释给小白如何看别人的源码(一)数据库部分
  6. awr报告 解读_AWR报告分析解读
  7. PX4 vision_to_mavros定位
  8. 关于Google神牛Jeff Dean的笑话,非程序员勿入
  9. D-Bus 性能分析
  10. matlab2018历史命令在哪,2018美赛准备之路——Matlab基础——命令行功能函数
  11. 如何让语音芯片与功放芯片之间更好的配合,使得产品音效更好
  12. js上传文件到OSS
  13. 干货丨爱奇艺CDN IPv6系统配置
  14. Win10专业版永久激活
  15. WebSocket 原理 1
  16. web应用登录,报错空指针java.lang.NullPointerException问题--可能的解决方式
  17. 记录:手机邮箱格式验证
  18. 电脑如何同时远程控制多台手机
  19. Mac 开发 打开系统偏好设置
  20. MinGW下载和安装教程

热门文章

  1. 解决XP下鼠标单击变双击的问题
  2. 有趣的定律1. 帕金森定律
  3. Python版见缝插针小游戏源代码,球球旋转大作战源程序
  4. GitHub(九):掌握 Issues
  5. 剑指 Offer II 002. 二进制加法
  6. 会声会影X10 64位整合光盘V10.1.0.14简体中文版 下载
  7. 年月日格式判断-正则表达式 YYYY/MM/DD、YYYY/MM/DD| YY/MM/DD、 ^(^(\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2}$)|(^\d{4}…
  8. 基于Cri-dockerd使用Kubeadm部署Kubernetes1.25集群
  9. 微信小程序彩票号码生成器
  10. Spring体系下单例策略模式,java策略模式最佳实践