AI的探索之旅头像
97 个 OpenCV 实例(三十七):二维码识别,一张图四个码的陷阱封面图

97 个 OpenCV 实例(三十七):二维码识别,一张图四个码的陷阱

📋 本文概览:

学什么|qrcode —— OpenCV 的 GraphicalCodeDetector,一个接口搞定二维码的「检测 + 解码」,支持两种检测器、四种调用模式。

踩了什么坑|我在一张图里放了 4 个二维码,用最熟悉的 detectAndDecode 去读——返回空字符串。换成 QRCodeDetectorAruco 也只认 1 个。查了半天图片质量,最后发现:根本不是图的问题,是 API 用错了。单码 API 在多码图上「全军覆没」是设计,不是 bug。

核心数据|单码 vs 多码 API 在四码图上的检出对照(0 / 1 / 4);aruco 检测器比 contour 快 2.3×;仅检测比检测+解码快 2~3×。

适合谁|做二维码扫码、票务核验、工业追溯的工程师——尤其是第一次用 OpenCV 读二维码的人。


一、效果先行:先看它检出了什么

先摆结果。一张 500×500 的图,里面塞了 4 个二维码,用 detectAndDecodeMulti 跑:

在这里插入图片描述

四个码全部框住,四个角点都标出来了。 解码文本分别是 OPENCV-4.14、1234567890、HELLO-QR、DAY38——一字不差。

看起来很简单对吧?但我在这条路上摔了一跤,而且摔得挺典型。


二、QR 码是什么:三个定位图案决定一切

先说 QR 码的结构,因为检测算法的一切都围绕它转:

在这里插入图片描述

结构作用
三个定位图案(finder pattern)左上、右上、左下的「回」字形 7×7 方块,检测全靠它
对齐图案(alignment pattern)右下的小回字,用来纠正拍摄形变
定时图案(timing pattern)定位图案间的黑白交替线,确定模块大小
数据区 + 纠错码实际内容 + Reed-Solomon 纠错码

OpenCV 提供了两种底层检测器:

  • 轮廓法 QRCodeDetector:找三个定位图案 → 拟合四边形 → 透视校正 → 采样解码;
  • ArUco 法 QRCodeDetectorAruco:把 QR 当作 ArUco 方形标记,复用成熟的亚像素角点细化。

两者通过 GraphicalCodeDetector 这个统一接口暴露,可以互换使用。


三、坑:一张图四个码,为什么只读出来 0 个?

这是本文的核心。我手上有三张测试图,其中 qr_multi.png 里有 4 个码。最自然的写法是:

String info = qr.detectAndDecode(img, corners);   // 「检测并解码」,一步到位

结果:contour 检测器返回空字符串,corners 也是空的。

我第一反应是「图是不是有问题?」——放大了看,四个码清清楚楚,一点没坏。

换了 aruco 检测器,还是只读出来一个(DAY38)。

于是我把四种模式全跑了一遍,画了张对照表:

在这里插入图片描述

检测器detectAndDecode(单码)detectAndDecodeMulti(多码)
contour❌ 0 码✅ 4 码全中
aruco⚠️ 只认 1 个✅ 4 码全中

真相大白:

detect / detectAndDecode 的契约就是「找到一个问题码」。contour 内部假定全图只有一个定位图案组,多个码时四边形拟合失败就返回空;aruco 则返回它找到的第一个码。这是 API 语义,不是图片问题。

多码场景必须显式用 detectAndDecodeMulti。

这个坑的教训很值钱:遇到「找不到目标」,先检查 API 语义是否匹配输入的基数(1 个 vs N 个),而不是先怀疑数据质量。


四、意外收获:contour 在旋转图上会漏检

跑 qr_rotated.png(把码转了 90°)时,又发现一个反常:

检测器detectAndDecode(单码)detectAndDecodeMulti(多码)
contour✅ 正常读出❌ 漏检
aruco✅ 正常读出✅ 正常读出

同一个 contour 检测器,单码模式能读,多码模式反而漏了。 这说明这两层 API 走的是不同的代码路径——*Multi 对轮廓筛选更严格,旋转后定位图案的形态变了就过不了筛选。

aruco 检测器两种模式都稳。这就是「把 QR 当 ArUco 标记」的好处:ArUco 的角点检测天生对旋转鲁棒。


五、速度实测:aruco 全面快于 contour

我顺便做了速度基准(单码 420×420,Release 构建):

在这里插入图片描述

检测器detect(仅检测)detectAndDecodedetectAndDecodeMulti
contour34.2 ms75.4 ms62.3 ms
aruco17.9 ms59.1 ms27.6 ms

两个结论:

  1. aruco 全面快于 contour,多码场景差 2.3×(27.6 vs 62.3 ms);
  2. 仅检测(detect)比检测+解码(detectAndDecode)快 2~3× —— 说明解码的主要开销在 Reed-Solomon 纠错和模块采样。如果只需要知道「码在哪里」(比如引导摄像头对准),用 detect 就够了,能省大半时间。

六、方法沉淀:这次学到的三件事

  1. API 语义要对上输入基数。 「单码」和「多码」是两套 API,用错不是数据问题,是调用问题。以后遇到「找不到」,先看 API 文档的「能找几个」。
  2. 同一模块的两个实现要对照着用。 contour 快照准、aruco 快且旋转稳——二维码识别优先 aruco,只有需要特定实现时才用 contour。
  3. 测试数据可以自己造,不依赖网络。 这次的测试图全是用 OpenCV 自带的 QRCodeEncoder 现生成的,断网也能复现。

七、其他值得记的细节

① QRCodeEncoder 产出的是单通道灰度图,必须转色:

Ptr<QRCodeEncoder> enc = QRCodeEncoder::create();
Mat qr;                                          // ★ 单通道 CV_8UC1
enc->encode("https://opencv.org", qr);
Mat canvas(420, 420, CV_8UC3, Scalar::all(255)); // 画布是三通道
cvtColor(qr, qrBGR, COLOR_GRAY2BGR);             // ★ 先转成 BGR 再贴
qrBGR.copyTo(canvas(Rect(x, y, qr.cols, qr.rows)));

② 多码结果的 corners 是「扁平化」的,不是 vector< vector<Point> >:

vector<Point> corners;    // ★ 长度 = 4 × 码数,不是嵌套结构
vector<String> infos;
qr.detectAndDecodeMulti(input, infos, corners);
for (size_t i = 0; i + 4 <= corners.size(); i += 4) {   // 自己按 4 个一组切
    vector<Point> c(corners.begin() + i, corners.begin() + i + 4);
    // c 就是第 i/4 个码的四角
}

这是本示例最容易踩的坑——官网 API 里 corners 类型是 OutputArray,实际是扁平的 vector<Point>。

③ imwrite 失败不抛异常,返回 false。 我第一次生成测试图时路径写错成 data/data/,图片全丢却毫无提示。加上 if (!imwrite(...)) printf("写失败") 才定位到。


八、AI 与 LLM Wiki:这一课沉淀了什么

  • 把「单码 API 在多码图上失败是设计」写进了验收清单 —— 以后任何「找不到码」的问题,第一反应是检查 API 语义是否匹配输入基数,而不是怀疑图片质量;
  • 建立了 GraphicalCodeDetector 选型表:二维码识别优先 aruco(快 + 旋转稳),只有需要特定实现时才用 contour;
  • 测试图自生成(QRCodeEncoder):以后所有条码/二维码类实验都可离线复现,不再受网络图片源限制 —— 这条经验已写进工程 README 的踩坑节;
  • 把「同一模块多实现要对照着用」升级成方法论:这次 contour vs aruco 的对照,和 Day37 的 Default vs Daimler、Day34 的 Haar vs LBP 是同一种模式——OpenCV 很多模块都提供了多个内置实现,默认的那个往往不是最优的。

运行环境:Ubuntu 24.04 / OpenCV 4.14.0(源码编译)/ g++ 13.3.0

官方源码:samples/cpp/qrcode.cpp(原样复制,零改动),代码引自 OpenCV 官方样例,遵循 Apache-2.0 许可。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/qq_20575585/article/details/169563873

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--