纯手工分析图像与目标识别

作者: | 更新日期:

了解大量图片相关知识。

本文首发于公众号:天空的代码世界,微信号:tiankonguse

一、背景

之前的文章分享过《拉密数字牌(以色列麻将)》的规则,第一版破解程序在数据稍微一复杂时就跑不出答案了。
于是,我又做了一版优化:《性能优化 动态规划》,使得任何数据都可以在1秒内计算出答案。

看一下拉密数字牌(以色列麻将)的实际使用场景。

现状:牌池中的牌是可以按规则形成匹配的。
初级诉求:判断手中的牌是否可以加入牌池,使得新的牌池依旧满足匹配。
终极诉求:将手中的牌全部输出。

如果要使用我开发破解程序,就需要手动把牌池输入进来,然后枚举尝试把手中的牌加入牌池进行判断。
这个输入牌池的操作是相当费劲的。

所以,我有了一个想法:能不能拍一张照片,自动识别牌池。

当然,如今已经是 2023 年,什么机器学习等自动识别技术已经很成熟了。
但是我还是想先纯手动分析图片,来识别图片中的目标。
说干就干,虽然还没做完,但是我认为有必要把当前的全过程记录下来。

二、图片是如何储存的

既然要自动识别图片的,首先需要知道图片是怎么储存的。

我问 chatGPT:图片二进制是如何储存的?
chatGPT 回答二进制数据储存方式取决于图片的格式,常见的储存方式有位图、压缩图像、索引颜色表、Alpha通道、编码方式等。

当我看到 chatGPT 的回答后,我意识到我需要的并不是图片文件怎么储存的,而是图片在压缩前是怎么储存的。

于是我又问 ChatGPT: 图片在内存中,压缩前是怎么储存的?
chatGPT 回答内存中通常以位图的方式储存。
位图涉及几个方面:像素数组、颜色深度、分辨率、像素排列、Alpha通道。
具体含义见 ChatGPT 的回答,介绍的挺清楚的,这里我就不复述了。

三、获取图片的二进制

图片一开始是在手机上,如何获取到手机上的图片呢?

最简单的想法是做一个网站,上传图片,并获取到二进制数据。

我问 ChatGPT:javascript 如何获取上传图片的二进制数据
ChatGPT 回答:上传图片后,通过 FileReader 可以加载图片的二进制。

我试了下,电脑上传了一张 png 图片和 jpg 图片,都正常。
但是我上传苹果手机拖图片时,却没有得到对应的二进制。

图片转到电脑上一看,评估的图片格式是 heic 的。

所以,需要把 heic 图片转化为 png 图片。
ChatGPT 告诉我使用 NodeJS 的 heic-convert 库。

我的代码是在浏览器运行的,所以我需要浏览器上的 javascript,而不是 nodejs 代码。
所以我告诉 ChatGPT,请给出浏览器上可以运行的代码。
最终,chatGPT 告诉我了 heic2any 库。

四、获取图片的像素

获取到图片的二进制后,我发现不同图片储存确实有差异,我并不能直接得到像素值。

我问 ChatGPT:javascript 如何获取图片的像素

ChatGPT 回答可以使用HTML5的Canvas元素和JavaScript来获取。

大概思路是,上传文件时,使用 FileReader 加载文件,加载后的数据转化为 Image 对象,之后将 Image 对象画到 canvas 画板上。
最后,在 canvas 画板上获取图片像素即可。

前面已经了解了图片中内存中的格式,由分辨率*(颜色通道+Alpha通道)*颜色深度组成。

分辨率由宽度与高度组成的二维数组,展开是W*H的一维数组。
颜色通道一般是红绿蓝三个通道,Alpha通道是透明通道,合起来是四通道。
颜色深度一般是一字节8位表示。

所以得到的二进制每四字节组成一个像素。
第一行像素区间为[0, 4W),第二行像素区间为[4W, 8W),依次递推。

五、物品初步识别

物品识别时,我们并不关心颜色,只关心物品的边界。
所以,可以将图片变成灰色,然后根据算法来寻找边界。

彩色变成灰色有哪些算法吗?
chatGPT 告诉我有平均值法、加权平均值法、最大值法、最小值法、加权最大值法。

我采用人眼对颜色敏感度比较均衡的计算方法,下图是转化为灰色之后的效果。

之后再将灰色图片转化为黑白图片。

之后,我们就可以寻找白色区域当做识别的物品,然后通过计算计算出白色区域的凸包。

黑白图片中,可以看到桌子上也有很多黑白色,导致部分物品识别不正确,所以需要去除噪音。

六、第一轮噪音去除

噪音去除的算法有均值滤波算法、中值滤波算法、维纳滤波算法、小波变换去噪算法、图像压缩去噪算法。

使用这些算法跑了下,效果都不是很好。

所以我打算自己实现一个噪音过滤算法。

当前场景的特征很明显,图片的背景是黑色的,棋牌都是白色的,噪音都处于黑色背景中。

那只需要在黑色背景中,把非连续白色删除即可。

如果一个点上下左右都是黑色,自己确实白色,那很显然自己就是噪音,需要变成黑色。
基于这个思路,如果一个点上下左右 X% 的点都是黑色,我们就认为这个点是噪音,应该修改成黑色。

X 应该多大呢?
棋牌都是矩形,所以矩形的角都是 90 度,即一个角至少 75% 的区域是黑色的。
考虑图片是照相机拍的,近大远小,直角会变成锐角,所以直接使用 75% 是不够的。
这里可以先考虑近大远小的因素,按 75% 处理。

噪音第一轮去除后,发现中间的部分由于桌子反光比较厉害,也被识别为白色了。

七、第二轮噪音去除

可以看到,棋牌的大小也是固定的。
如果一个白色区域的大小远远小于棋牌的大小,那显然就是噪音了。

所以第二个噪音算法是根据区域大小来判断。

两个结合起来运行,噪音就只剩一个了。

两种噪音算法结合,目前就识别出了棋牌的边界。

八、最后

大部分棋牌都是独立的,很容易识别。
但是棋牌挨着时,或者桌子反光恰好是白色时,目前还没法很好的识别,还在想办法处理中。

当前的效果如下:

后面我的规划是先识别单独的棋牌,并识别出数字和颜色。

打牌时,人工保证各个牌有一定距离,这样就可以简单使用这个程序了。

《完》

-EOF-

本文公众号:天空的代码世界
个人微信号:tiankonguse
公众号ID:tiankonguse-code

本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。

关注公众号,接收最新消息

tiankonguse +
穿越