python解析二维码的库有很多,我这里使用的是pyzbar
pip install pyzbar -i https://mirrors.aliyun.com/pypi/simple/
from pyzbar import pyzbar
# 1、读取二维码图片
qrcode = Image.open('/root/Downloads/二维码.png')
# 2、解析二维码中的数据
data = pyzbar.decode(qrcode)
# 3、在数据中解析出二维码的data信息
text = data[0].data.decode('utf-8')
print(text)
执行上面代码输出 hello wa !!!
先来一个简单的微信用户二维码
from pyzbar import pyzbar
# 1、读取二维码图片
qrcode = Image.open('/root/Downloads/wechat.png')
# 2、解析二维码中的数据
data = pyzbar.decode(qrcode)
# 3、在数据中解析出二维码的data信息
text = data[0].data.decode('utf-8')
print(text)
执行上面代码输出 https://u.wechat.com/MDGwSLSFcgijEQ9FrHDS3rI,也解析成功了
后面对于同样的二维码我只是换了一下样式,如下
上面的二维码,相同的代码就识别不了,但这个二维码的定位点什么的都很清楚,不应该扫码失败啊
后面仔细分析了一下,这两张图的最大区别可能就是在将两张图二值化后,第一张是白底黑点,第二张是黑底白点,所以我就怀疑这个pyzbar的库是不是只能解析白底黑点的二维码(即表达信息的点相对于背景必须是深色),然后就将第二张二维码的二值图反转,使其变为白底黑点
PS:简单的解释了什么是二值图,这里是将图像先转为灰度图,灰度图可以简单的理解为去除了原图的色彩信息,仅保留原图的亮度信息,而灰度图的亮度由0~255种亮度组成,0为纯黑色,亮度依次递增,255为纯白色。二值图是在灰度图的基础上做运算,比如如果灰度图中某个像素点的亮度低于150就将该像素点的亮度改为0,大于等于150则将其改为255,这样原来的灰度图里的像素点非黑即白,具体效果如下
qrcode = Image.open('/root/Downloads/wechat.png')
qrcode.show()
# 将图像转为灰度度
gray_qrcode = qrcode.convert('L')
gray_qrcode.show()
# 将灰度图转为二值图
binary_qrcode = gray_qrcode.point(lambda x: 0 if x < 150 else 255, '1')
binary_qrcode.show()
从左到右,依次是原图、灰度图、二值图
下面是二维码二值化后,用pyzbar解析的示列代码
from pyzbar import pyzbar
# 1、读取二维码图片
qrcode = Image.open('/root/Downloads/wechat.png').convert('L')
binary_image1 = qrcode.point(lambda x: 255 if x < 150 else 0, '1')
binary_image1.show()
data = pyzbar.decode(binary_image1)
# 3、在数据中解析出二维码的data信息
text = data[0].data.decode('utf-8')
print(text)
执行上面代码输出 https://u.wechat.com/MAnv3oeW_fMREg4OzIhkcTI,也解析成功了
这样问题就又出现了,有的二维码它本来就是白底黑点,经过上面的二值化处理就相当于反转了一下变成了白底黑点,经过前面的分析这样的二维码pyzbar是解析不了的。其实解决的方法简单粗暴,只需生成一张正常的二值化图像,然后在生成一张反转后二值化图像,最后分别对两张图的二维码进行解析,下面是改进的代码
import pyzbar
from PIL import Image
from pyzbar import pyzbar
def scan_qr_code(image_path, threshold=150):
image = Image.open(image_path) # 将二进制转为PIL格式图片
# 将图像转换为灰度图
gray_image = image.convert('L')
# 将灰度图转换为二值图
binary_image1 = gray_image.point(lambda x: 0 if x < threshold else 255, '1')
binary_image2 = gray_image.point(lambda x: 255 if x < threshold else 0, '1')
# binary_image1.show()
for i in binary_image1, binary_image2:
barcodes = pyzbar.decode(i)
if barcodes:
for barcode in barcodes:
barcode_data = barcode.data.decode("utf-8")
return barcode_data
else:
return '扫码失败'
print(scan_qr_code('/root/Downloads/二维码.png'))
执行上面代码输出 hello wa !!!
所以现在的问题就是如何有效的通过二值化将二维码的信息点与背景分割出来,其实大多数二维码在被生成的时候都考虑到了这点,即使那些花里胡哨的二维码在被扫描时也可通过150的阈值将信息点与背景分离开,以防万一如果上面的代码解析不了,可以取消注销上面代码binary_image1.show(),看看二值化后的图像信息点是否清晰,然后调整阈值
最后QQ二维码通过上面的代码也是可以解析的,就不赘述了。还有就是AI生成的二维码大多数都解析不了,比如说下面这张,不过微信的扫一扫还是可以的,
写这些,仅记录自己学习使用python解析扫描二维码的过程。如果有什么错误的地方,还请大家批评指正。最后,希望小伙伴们都能有所收获。