4. Misc-图片隐写-二维码修复

任务目标

你拿到一张被破坏的二维码图片,图中二维码的定位角(三个回字形方块)被黑色色块覆盖,导致手机无法扫描。目标是通过图像处理修复定位角,使二维码能够被正常识别。

环境依赖

本案例使用 Python 3 + OpenCV 和 pyzbar 库。安装命令:

pip install opencv-python pyzbar

实现思路

二维码的定位角位于左上、右上、左下三个位置,每个定位角是一个 7×7 的黑色回字形模块。修复方法:将三个定位角区域整体替换为纯黑色正方形(尺寸为模块大小的 7 倍),因为定位角本身是黑色实心回字,直接涂黑即可被识别器正确识别为定位图案。

关键完整代码

import cv2
import numpy as np
from pyzbar.pyzbar import decode

def repair_qr(image_path, output_path):
    # 读取图像
    img = cv2.imread(image_path)
    if img is None:
        raise ValueError("无法读取图像,请检查路径")
    
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    h, w = gray.shape
    
    # 二值化
    _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
    
    # 寻找二维码区域的外轮廓
    contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if not contours:
        raise ValueError("未找到二维码轮廓")
    
    # 取最大轮廓作为二维码边界
    qr_contour = max(contours, key=cv2.contourArea)
    x, y, qr_w, qr_h = cv2.boundingRect(qr_contour)
    
    # 二维码通常为正方形,取边长
    size = max(qr_w, qr_h)
    
    # 计算模块大小:二维码通常为25x25或29x29等版本,这里通过轮廓宽度估算
    # 标准二维码版本1为21x21模块,版本2为25x25,此处按常见25x25处理
    # 更精确方法:检测最外层黑白交替次数,此处简化为按25模块计算
    module_size = size // 25
    if module_size == 0:
        module_size = 1
    
    # 定位角尺寸:7个模块
    finder_size = module_size * 7
    
    # 三个定位角的中心位置(相对于二维码左上角)
    # 左上角定位角中心:(3.5*module, 3.5*module)
    # 右上角定位角中心:(size - 3.5*module, 3.5*module)
    # 左下角定位角中心:(3.5*module, size - 3.5*module)
    centers = [
        (int(3.5 * module_size), int(3.5 * module_size)),
        (int(size - 3.5 * module_size), int(3.5 * module_size)),
        (int(3.5 * module_size), int(size - 3.5 * module_size))
    ]
    
    # 在原始图像上绘制黑色正方形覆盖定位角区域
    for cx, cy in centers:
        x1 = x + cx - finder_size // 2
        y1 = y + cy - finder_size // 2
        x2 = x1 + finder_size
        y2 = y1 + finder_size
        # 确保不越界
        x1 = max(0, x1); y1 = max(0, y1)
        x2 = min(w, x2); y2 = min(h, y2)
        cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 0), -1)
    
    # 保存修复后的图像
    cv2.imwrite(output_path, img)
    
    # 验证:尝试解码
    result = decode(cv2.imread(output_path))
    if result:
        return result[0].data.decode('utf-8')
    else:
        return None

if __name__ == "__main__":
    # 使用示例
    text = repair_qr("broken_qr.png", "fixed_qr.png")
    if text:
        print(f"修复成功,二维码内容: {text}")
    else:
        print("修复后仍无法识别,请检查图像质量或调整模块估算")

运行与验证

  1. 将损坏的二维码图片命名为 broken_qr.png 放在脚本同目录下
  2. 运行脚本:python repair_qr.py
  3. 观察输出:若打印 修复成功 及内容,说明修复完成
  4. 同时可用手机扫描生成的 fixed_qr.png 验证

判定标准

  • 脚本能输出二维码编码的原始文本内容
  • 生成的 fixed_qr.png 可被手机或扫码工具正常识别
  • 若修复失败,检查原图二维码是否为标准版本(模块数 21/25/29 等),当前代码按 25×25 模块估算,若版本不同需调整 module_size 计算方式

常见问题

模块大小估算不准: 若二维码版本不是 25×25,可改为检测最外层黑白环数量来确定模块数。简单替代方案:手动指定模块大小,例如 module_size = 8(像素),然后重新运行。