如何将拍摄的QR Code图片转换为视觉一致的规整标准QR Code?
问题原因
你当前代码的逻辑是先解析二维码存储的原始数据,再调用qrcode库默认参数重新生成二维码。qrcode默认会自动匹配适配的QR版本、纠错等级、掩码模式等参数,这些参数和你拍摄的原始二维码参数不一致,因此会出现数据完全相同但视觉模块排布不同的问题。
解决方案
有两种可行方案,第二种通用适配性更强:
方案1:指定匹配原始二维码的参数重新生成
你需要先获取原始二维码的版本、纠错等级、掩码模式等参数,生成时完全对应指定,同时设置fit=False禁止自动调整参数。你提供的示例原始二维码对应参数为版本3、纠错等级M、掩码模式4,修改后代码如下:
from PIL import Image from pyzbar import pyzbar import qrcode img1 = Image.open('in.png') out1 = pyzbar.decode(img1)[0] # 完全指定原始二维码参数,禁止自动适配 qr = qrcode.QRCode( version=3, error_correction=qrcode.constants.ERROR_CORRECT_M, box_size=10, border=4, mask_pattern=4 ) qr.add_data(out1.data) qr.make(fit=False) # 关键参数:关闭自动适配,强制使用指定参数 output = qr.make_image(fill_color="black", back_color="white") output.save("out.png", "PNG")
方案2:采样原始二维码模块重绘(通用方案)
不需要提前知道二维码参数,通过透视变换转正拍摄的斜二维码,采样每个模块的颜色后重绘为标准直角模块,100%还原原始视觉排布,需要额外安装opencv-python依赖:
import cv2 import numpy as np from PIL import Image from pyzbar import pyzbar # 读取输入图像 img = cv2.imread('in.png') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) decoded_qr = pyzbar.decode(gray)[0] # 获取二维码四个角的坐标 src_points = np.array([(p.x, p.y) for p in decoded_qr.polygon], dtype=np.float32) # 可自定义输出大小:比如每个模块占10像素,29模块对应290像素,可按需调整 target_pixel = 290 dst_points = np.array([ [0, 0], [target_pixel, 0], [target_pixel, target_pixel], [0, target_pixel] ], dtype=np.float32) # 透视变换把斜的二维码转正 trans_matrix = cv2.getPerspectiveTransform(src_points, dst_points) warped_qr = cv2.warpPerspective(gray, trans_matrix, (target_pixel, target_pixel)) # 二值化转纯黑纯白 _, binary_qr = cv2.threshold(warped_qr, 127, 255, cv2.THRESH_BINARY) # 按模块采样重绘,去除噪点 module_size = 10 module_count = target_pixel // module_size output_img = np.full((target_pixel, target_pixel), 255, dtype=np.uint8) for row in range(module_count): for col in range(module_count): # 取每个模块中心位置的像素值作为模块颜色 center_y = row * module_size + module_size // 2 center_x = col * module_size + module_size // 2 if binary_qr[center_y, center_x] < 127: # 把整个模块涂成黑色 cv2.rectangle(output_img, (col * module_size, row * module_size), ((col+1)*module_size - 1, (row+1)*module_size -1), 0, -1) # 保存结果 Image.fromarray(output_img).save('out.png')
注意点
- 方案1只适用于你当前提供的这个二维码,如果要处理其他二维码,需要重新匹配对应参数
- 方案2的
target_pixel和module_size可以按需调整,只要保持target_pixel = module_size * 原始QR模块数即可,你也可以通过识别原始QR版本自动计算模块数
内容的提问来源于stack exchange,提问作者EllipticalInitial
相关产品推荐
相关产品推荐

