今天看到一个消息:有些网站(TB?)不允许留二维码。如果检测到图片中的二维码,这张图片就会被河蟹。
什么鬼规定……
原理
我们知道,人眼是又视觉暂留的。有了视觉暂留,才形成了动画。不仅是人眼,显示器也有。
但是!截屏没有!
我们可以充分利用这个特性来做一些事情。比如,只显示二维码的一部分,刷新快一点,人眼和显示器因为有视觉暂留,可以看到二维码;截屏软件抓取到的图像里面不包含完整的二维码,导致河蟹失败。
实现
将上面思路细化一下:我们把一个二维码分成四个区域,每次只显示一个区域。
很简单是吧。
但是实现中,我发现 PIL/Pillow 里面只有读取 GIF 动画的方法,没有 GIF 动画生成的方法。Skimage 也是。如果想生成 GIF 动画,只能使用 OpenCV,不太划算。在 StackOverflow 上面找到了 images2gif
包,但是年久失修,import 就会出错。
最终在一个很不起眼的地方发现了 imageio
这个包,顺利解决问题。(整整花了一天时间啊!)
代码
# -*- coding: utf-8 -*-
import skimage.io
import skimage.color
import imageio
import numpy as np
def make_qr_gif(filename_in, filename_out, mask_type=0, fps=80):
image = skimage.io.imread(filename_in)
# 为了防止输入图片为灰度图
if image.ndim == 2:
image = skimage.color.gray2rgb(image)
(height, width, _) = image.shape
image = skimage.img_as_ubyte(image)
# 图片切片位置
ranges_width = [(0, width >> 1), (width >> 1, width)]
ranges_height = [(0, height >> 1), (height >> 1, height)]
sequence = list()
for x in ranges_height:
for y in ranges_width:
# 复制原图
one_frame = image.copy()
# 制作 mask
mask = np.ones((height, width), np.bool)
mask[x[0]:x[1], y[0]:y[1]] = 0
if mask_type == 1:
mask = np.invert(mask)
# 掩模
one_frame[mask] = 255
# 加入动画序列
sequence.append(one_frame)
# 使用 imageio 库保存 gif 图片
imageio.mimsave(filename_out, sequence, fps=fps)
if __name__ == '__main__':
make_qr_gif(filename_in='qr.png', filename_out='result.gif')
效果
我们使用下面这张图来进行测试。
经过处理之后的图片如下:
哦对了,你也可以试试截图和扫一扫。
扫码成功率不是特别高,mask_type=1
时拿微信扫识别率还是不错的,mask_type=0
时识别率大大下降。
另外,fps
参数用于控制每秒钟显示多少帧。好像现在液晶显示器都是 60 帧 / 秒?我忘记了。显示帧数和图片所在环境也有关,比如在浏览器里面显示可能会慢一点,甚至在不同浏览器里面显示速度也会不一样。
总结
好了,就是一个思路。实现很粗糙,开心就好。
发表回复