闪耀的二维码 之一


发布于

|

分类

今天看到一个消息:有些网站(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 帧 / 秒?我忘记了。显示帧数和图片所在环境也有关,比如在浏览器里面显示可能会慢一点,甚至在不同浏览器里面显示速度也会不一样。

总结

好了,就是一个思路。实现很粗糙,开心就好。


评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注