二维码识别不了怎么办:原理、五个失败原因与命令行解法

一、结论先给

二维码扫不出来,99% 是这五个原因之一:

原因 症状 解法
1. 分辨率不够 码在图上太小、放大就糊 每个模块至少 3-4 像素
2. 反光 / 过曝 屏幕拍屏有摩尔纹、金属面反光 侧角度拍、降低曝光
3. 对比度不足 灰底灰码、浅色码 提对比度、二值化
4. 码制不支持 不是 QR(Data Matrix / PDF417 / 一维码) 换支持多码制的识别器
5. 损坏超过纠错上限 破损、污渍、logo 挡太多 换 H 级容错重新生成

先判断是哪种,再动手。盲目换工具往往没用。

二、二维码为什么能"部分遮挡也认得"

2.1 三个定位方块

二维码左下、左上、右上各有一个「回」字形方块,这是定位图案。识别器先找到这三个方块确定码的位置和方向,所以二维码斜着、倒着也能扫。

┌─────────────┐
│ ██┌─────┐██ │   ← 左上定位
│   │ ███ │   │
│   └─────┘   │
│             │
│        ██┌──┤   ← 右上定位
│           │ │
│ ██┌─────┐  │   ← 左下定位
└─────────────┘

这解释了两个常见现象:

  • 挡住定位方块 → 完全识别不了(哪怕只挡一小角)
  • 挡住中间数据区 → 可能还能认(靠纠错)

2.2 纠错等级

二维码有四个纠错等级,生成时可以选:

等级 可恢复比例 典型用途
L 约 7% 干净环境、要塞很多数据
M 约 15% 默认
Q 约 25% 户外、易污损
H 约 30% 中间要放 logo、印刷质量差

中间放 logo 的二维码必须用 H 级(或至少 Q 级)。用默认的 M 级放个大 logo,遮挡超过 15% 就扫不出来了——这是最常见的"自家二维码扫不出"事故。

2.3 版本与容量

QR 码有 40 个版本,版本 n 的尺寸是 (17 + 4n) × (17 + 4n):

版本 尺寸 数字容量(L 级)
1 21 × 21 41
10 57 × 57 652
40 177 × 177 7089

内容越多,码越密,对分辨率的要求越高。一个 URL(30-60 字符)通常在版本 3-5。

三、命令行识别:zbarimg

# 装
sudo apt install zbar-tools        # Debian/Ubuntu
brew install zbar                  # macOS

# 识别
zbarimg --raw -q photo.png

输出直接是解码内容:

https://it997.com/tool/qrcode-reader

3.1 常用参数

# 显示码制
zbarimg photo.png
# 输出:QR-Code:https://...

# 只输出内容(脚本用)
zbarimg --raw -q photo.png

# 关闭所有码制,只认 QR(减少误判)
zbarimg --disable --enable=QR-Code -q photo.png

# 支持 XML 输出(含坐标)
zbarimg --xml photo.png

3.2 zbar 支持的码制

类型 是否支持
QR Code ✅
EAN-13 / EAN-8 / UPC-A ✅
Code 39 / Code 93 / Code 128 ✅
ISBN-10 / ISBN-13 ✅
Interleaved 2 of 5 ✅
Data Matrix ❌(zbar 老版本不支持,新版部分支持)
PDF417 ❌
Aztec ❌

PDF417(驾驶证、营业执照上的那种长条码)和 Data Matrix(电子元件上的小码)zbar 认不了,要用 zxing。

四、zxing:多码制的备选

# Java 命令行(需要 java)
wget https://repo1.maven.org/maven2/com/google/zxing/javase/3.5.3/javase-3.5.3.jar
wget https://repo1.maven.org/maven2/com/google/zxing/core/3.5.3/core-3.5.3.jar
java -cp "core-3.5.3.jar:javase-3.5.3.jar" com.google.zxing.client.j2se.CommandLineRunner photo.png

支持的码制更全:QR、Data Matrix、PDF417、Aztec、以及多种一维码。

Python 方案(推荐,装起来简单):

from pyzbar.pyzbar import decode
from PIL import Image
import cv2, numpy as np

def read_qr(path):
    # 方式 1:pyzbar(快,QR 和一维码)
    results = decode(Image.open(path))
    for r in results:
        print(r.type, r.data.decode('utf-8', errors='replace'))

    # 方式 2:OpenCV(支持 detectAndDecode,且自带预处理)
    img = cv2.imread(path)
    data, pts, _ = cv2.QRCodeDetector().detectAndDecode(img)
    if data:
        print('OpenCV:', data)

    # 方式 3:cv2.wechat_qrcode(微信同款模型,对模糊图效果更好)
    detector = cv2.wechat_qrcode_WeChatQRCode(
        'detect.prototxt', 'detect.caffemodel', 'sr.prototxt', 'sr.caffemodel')
    res, _ = detector.detectAndDecode(img)
    print('WeChat:', res)

wechat_qrcode 对模糊、低分辨率的图识别率明显更高(带超分模型),但需要下 4 个模型文件。批量处理模糊图时值得折腾一次。

五、预处理:把"认不出的图"救回来

识别不了时,先处理图再识别,成功率能翻倍。

5.1 放大(分辨率不足)

# 用最近邻放大,别用平滑插值(会把边缘糊掉)
magick photo.png -filter point -resize 400% big.png
zbarimg --raw -q big.png

-filter point(最近邻)是关键:默认的平滑插值会让黑白边界变灰,反而更难识别。

5.2 提对比度

# 自动拉伸对比度
magick photo.png -normalize out.png

# 更强:直方图均衡
magick photo.png -equalize out.png

# 灰度 + 二值化(最有效)
magick photo.png -colorspace Gray -threshold 50% bin.png
zbarimg --raw -q bin.png

5.3 去模糊

# 轻度锐化
magick photo.png -sharpen 0x1.5 out.png

# 反卷积去运动模糊(复杂,但效果明显)
magick photo.png -motion-blur 0x0-8  # 这是加模糊,去模糊用 -deconvolve
magick photo.png -deconvolve kernel.txt out.png

实操经验:二值化 + 最近邻放大这两步能解决大部分问题,去模糊是最后的手段。

5.4 反光 / 摩尔纹(拍屏常见)

# 轻度模糊消除摩尔纹,再锐化
magick photo.png -blur 0x0.8 -sharpen 0x1.5 out.png

或者物理上解决:侧 15-30 度角拍屏幕,正面拍必然反光。

5.5 只截码的部分

码在照片里太小,先裁出来再放大:

magick photo.jpg -crop 400x400+150+300 +repage crop.png
magick crop.png -filter point -resize 400% -colorspace Gray -threshold 50% final.png
zbarimg --raw -q final.png

👉 裁剪可以先用 图片裁剪 框出二维码区域,再识别,比整图识别准得多。

六、安全:扫码前必做的一步

二维码是"不透明的链接",扫之前你看不到它要你访问什么。

真实风险(每年都有案例):

风险 表现
钓鱼页 扫码跳转到仿冒的登录页/支付页
自动下载 触发 apk/ipa/木马下载
恶意配置 扫码连上恶意 WiFi、加恶意联系人
支付码替换 商家收款码被贴纸覆盖("码上骗")
恶意填充 扫码自动填表提交个人信息

正确做法:

  1. 用能显示原文的识别器,先看清楚内容再决定要不要打开
  2. 内容是 URL 时,检查域名(pay.example.com 和 pay-example.com 天差地别)
  3. 涉及支付、登录、下载的,手工输入网址,别直接跳转
  4. 商家静态收款码,付款前确认金额和商户名
  5. 共享单车、充电桩上的码,被贴纸覆盖的一律不用

👉 二维码识别工具 是先把内容解析出来给你看、再由你决定要不要访问的模式,图片在浏览器本地解码不上传——对不确定的码,这比直接用微信扫要安全。

七、如果你是生成方:让别人一次扫中

7.1 尺寸

场景 最小尺寸
屏幕显示 码区 ≥ 200 × 200 px
打印(A4 传单) ≥ 2 × 2 cm
户外海报 每米距离 ≥ 1 cm(10 米外扫需 ≥ 10 cm)
名片 ≥ 1.5 × 1.5 cm

7.2 静默区

二维码四周必须留至少 4 个模块宽的空白(静默区)。没有静默区,识别器找不到码的边界。

# 生成时带静默区(qrencode 默认 4 模块)
qrencode -o qr.png -s 8 -m 4 "https://it997.com"
#                          ↑ 静默区

最常见的生成错误:设计稿把二维码紧贴边框或压在背景图上。

7.3 颜色

  • 深色的码 + 浅色的底(黑白色最好)
  • 不要用反色(白码黑底):大量识别器不认
  • 彩色码要保证亮度对比,红底绿码这种亮度接近的组合识别率低
  • 渐变背景会让阈值判断失败,尽量用纯色底

7.4 容错等级

用途 等级
纯文本/URL,干净环境 M(默认)
中间要放 logo H
户外、易磨损 Q 或 H
内容特别长,塞不下 L(但要保证环境干净)
# qrencode 指定容错等级
qrencode -o qr.png -s 8 -l H "https://it997.com"
#                          ↑ L/M/Q/H

👉 生成用 二维码生成工具,可以直接选容错等级;生成完务必自己扫一遍再印刷。

八、几条纪律

  1. 先判断失败原因,别急着换工具。
  2. 预处理顺序:裁剪 → 最近邻放大 → 灰度 → 二值化 → 识别。
  3. 放大用 -filter point,平滑插值会毁掉边缘。
  4. 中间放 logo 必须用 H 级容错。
  5. 生成后一定要留 4 模块静默区,别贴边。
  6. 别做反色码,很多识别器认不了。
  7. 扫码先看原文,涉及钱和登录的手工输入网址。

九、延伸阅读

👉 相关在线工具:二维码识别 · 二维码生成 · 图片裁剪 · 图片压缩,免登录、浏览器内处理。

还有 65 个免费在线工具

纯前端实现,不用注册,数据不上传服务器。

浏览全部工具