二维码和 OCR:从生成到识别的实战代码

Published on with 0 views and 0 comments

二维码和 OCR:从生成到识别的实战代码

Tags: 二维码,OCR,Python,OpenCV,pytesseract,pyzbar

前阵子在做一个小工具,需要把一段文字转成二维码,再用手机扫出来;另一边又要把纸质单据上的文字抓进系统。两件事凑一起,干脆把二维码生成/识别和 OCR 整成一条链,记录一下关键代码和踩过的坑。

一、生成二维码:qrcode 够用

Python 里 qrcode 库最省事。装一下:

pip install qrcode[pil]

基本用法几行搞定:

import qrcode

img = qrcode.make("https://symoon.icu")
img.save("qr_basic.png")

想调尺寸、容错率、颜色,就换 QRCode 对象:

from qrcode import QRCode, constants

qr = QRCode(
    version=1,               # 1-40,控制容量
    error_correction=constants.ERROR_CORRECT_H,  # 最高容错,适合中间加 logo
    box_size=10,
    border=4,
)
qr.add_data("https://symoon.icu")
qr.make(fit=True)

img = qr.make_image(fill_color="#1a1a1a", back_color="white")
img.save("qr_custom.png")

中间塞个 logo 也常见,注意 logo 别超过二维码面积的 30%,否则容错再高也扫不出来:

from PIL import Image

logo = Image.open("logo.png").convert("RGBA")
qr_img = img.convert("RGBA")

# logo 缩放到二维码的 1/4
logo_size = qr_img.width // 4
logo.thumbnail((logo_size, logo_size), Image.Resampling.LANCZOS)

pos = ((qr_img.width - logo.width) // 2, (qr_img.height - logo.height) // 2)
qr_img.paste(logo, pos, logo)
qr_img.save("qr_with_logo.png")

二、识别二维码:pyzbar + OpenCV

识别二维码主要用 pyzbar,能读 QR Code、Data Matrix、Code128 这些。需要 OpenCV 或者 Pillow 读图。

pip install pyzbar opencv-python-headless

用 OpenCV 读图识别:

import cv2
from pyzbar import pyzbar

img = cv2.imread("qr_with_logo.png")
barcodes = pyzbar.decode(img)

for bc in barcodes:
    print(bc.data.decode("utf-8"))
    # 画框
    x, y, w, h = bc.rect
    cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)

有时候摄像头画面反光、角度歪,识别率会掉。加个预处理能救回不少:

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化 + 降噪
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
barcodes = pyzbar.decode(binary)

我自己测试下来,光线正常时 pyzbar 基本一枪命中;太暗、太快、摩尔纹厉害的情况下,就得上图像预处理或者换相机对焦。

三、OCR:pytesseract 入门

OCR 我用的是 pytesseract,底层是 Tesseract。先装系统依赖:

# Ubuntu/Debian
sudo apt-get install tesseract-ocr tesseract-ocr-chi-sim

pip install pytesseract pillow

中文识别要在调用时指定语言包:

from PIL import Image
import pytesseract

img = Image.open("invoice.png")
text = pytesseract.image_to_string(img, lang="chi_sim+eng")
print(text)

Tesseract 对清晰、白底黑字的印刷体效果最好。手写体、倾斜、低分辨率会掉链子。预处理能明显提升准确率:

import cv2
import numpy as np

img = cv2.imread("invoice.png", cv2.IMREAD_GRAYSCALE)
# 放大分辨率
img = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)
# 二值化
_, img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 去噪
cv2.imwrite("processed.png", img)

四、把二维码和 OCR 串起来

一个实际场景:单据上印了二维码,里面存的是业务 ID;OCR 识别的是单据上的金额、日期。流程可以这么串:

import cv2
from pyzbar import pyzbar
import pytesseract
from PIL import Image

img = cv2.imread("doc.jpg")

# 1. 先扫二维码拿 ID
barcodes = pyzbar.decode(img)
order_id = barcodes[0].data.decode("utf-8") if barcodes else ""

# 2. 裁剪文字区域做 OCR
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
text = pytesseract.image_to_string(gray, lang="chi_sim+eng")

print("单号:", order_id)
print("识别内容:", text)

如果二维码和文字区域位置固定,直接按坐标裁剪更稳,少跑很多识别干扰。比如二维码永远在左上角 200×200:

qr_roi = img[0:200, 0:200]
barcodes = pyzbar.decode(qr_roi)

五、几个踩坑点

  1. 中文乱码:pytesseract 输出默认 UTF-8,但如果终端编码不对会显示乱码;建议打印或保存时用 encode/decode 确认一下。
  2. Tesseract 语言包路径:Windows 上经常找不到 chi_sim,需要手动指定 pytesseract.pytesseract.tesseract_cmdTESSDATA_PREFIX
  3. pyzbar 在 Windows 安装失败:Windows 需要 Visual C++ 运行库,或者直接用 conda 安装 pyzbar
  4. 二维码容量:QR 版本越高能存越多,但图也越大。URL 带参数时尽量短,否则高分辨率手机才扫得清。

六、写在最后

二维码和 OCR 都不是什么新技术,但组合起来能省很多手工录入的麻烦。如果是手机端扫描,现在微信小程序的 scanCode 和 OCR 接口也已经很成熟,后端只需要一个接收和校验的接口就行。日常小需求,上面这些代码片段基本够用了。