QReader
QReader是一个健壮且直接的Python解决方案,用于读取图像中难以识别和棘手的QR码。它由YOLOv8模型驱动。
该库的核心由两个主要组成部分构成:一个经过训练用于检测和分割QR码的YOLOv8QR检测器模型(也作为独立项目提供),以及PyzbarQR解码器。利用从这个QR检测器中提取的信息,QReader在Pyzbar的基础上透明地应用了不同的图像预处理技术,最大化了难度较大图像的解码率。
安装
要安装QReader,只需运行:
pip install qreader
你可能需要安装一些额外的pyzbar依赖:
在Windows上:
极少数情况下,你可能会遇到与lizbar-64.dll
相关的丑陋的ImportError。如果发生这种情况,请从_Visual C++ Redistributable Packages for Visual Studio 2013_安装vcredist_x64.exe
在Linux上:
sudo apt-get install libzbar0
在Mac OS X上:
brew install zbar
要在本地安装QReader包,运行pip
python -m pip install --editable .
注意:如果你在资源非常有限的服务器上运行QReader,你可能想在安装QReader之前安装PyTorch的CPU版本。要做到这一点,运行:pip install torch --no-cache-dir
(感谢@cjwalther的建议)。
使用方法
QReader是一个非常简单直接的库。对于大多数用例,你只需要调用detect_and_decode
:
from qreader import QReader
import cv2
# 创建一个QReader实例
qreader = QReader()
# 获取包含QR码的图像
image = cv2.cvtColor(cv2.imread("path/to/image.png"), cv2.COLOR_BGR2RGB)
# 使用detect_and_decode函数获取解码后的QR数据
decoded_text = qreader.detect_and_decode(image=image)
detect_and_decode
将返回一个tuple
,包含图像中每个找到的QR的解码_字符串_。
注意:某些条目可能为
None
,这种情况发生在QR被检测到但无法解码时。
API参考
QReader(model_size = 's', min_confidence = 0.5, reencode_to = 'shift-jis', weights_folder = None)
这是库的主类。请尽量只实例化一次,以避免每次需要检测QR码时都加载模型。
model_size
:str。要使用的模型大小。可以是**'n'(nano)、's'(small)、'm'(medium)或'l'(large)。较大的模型可能更准确但速度较慢。推荐:'s'**(#37)。默认:'s'。min_confidence
:float。QR检测被认为有效的最小置信度。接近0.0的值可能会得到更多_假阳性_,而接近1.0的值可能会丢失难以识别的QR。默认(并推荐):0.5。reencode_to
:str | None。用于重新编码utf-8
解码后的QR字符串的编码。如果为None,则不会重新编码。如果发现某些字符解码不正确,可以尝试设置与你特定字符集匹配的代码页。已发现有用的建议:- 'shift-jis'用于日耳曼语系
- 'cp65001'用于亚洲语言(感谢@nguyen-viet-hung的建议)
weights_folder
:str|None。检测模型将被下载的文件夹。如果为None,它将被下载到默认的qrdet包内部文件夹,确保在卸载时正确删除。在像AWS Lambda这样只有/tmp文件夹可写的环境中工作时,你可能需要更改它,如#21中所述。默认:None
(<qrdet_package>/.model)。
QReader.detect_and_decode(image, return_detections = False)
此方法将解码给定图像中的QR码,并返回解码后的_字符串_(如果检测到但未解码,则返回_None_)。
-
image
:np.ndarray。要读取的图像。预期为_RGB_或_BGR_(uint8)格式(HxWx3)。 -
return_detections
:bool。如果为True
,将返回完整的检测结果以及解码后的QR。如果为False,则只返回QR码的解码内容。 -
is_bgr
:boolean。如果为True
,则接收的图像预期为_BGR_而不是_RGB_。 -
返回:tuple[str | None] | tuple[tuple[dict[str, np.ndarray | float | tuple[float | int, float | int]]], str | None]]:一个包含所有检测到的QR码解码结果的元组。如果
return_detections
为False
,输出将如下所示:('解码QR 1', '解码QR 2', None, '解码QR 4', ...)
。如果return_detections
为True
,则如下所示:(('解码QR 1', {'bbox_xyxy': (x1_1, y1_1, x2_1, y2_1), 'confidence': conf_1}), ('解码QR 2', {'bbox_xyxy': (x1_2, y1_2, x2_2, y2_2), 'confidence': conf_2, ...}), ...)
。有关检测格式的更多信息,请参阅QReader.detect()。
QReader.detect(image)
此方法检测图像中的QR码,并返回包含所有检测信息的_字典元组_。
-
image
:np.ndarray。要读取的图像。预期为_RGB_或_BGR_(uint8)格式(HxWx3)。 -
返回:tuple[dict[str, np.ndarray|float|tuple[float|int, float|int]]]。包含每个检测的所有信息的字典元组。包含以下键。
键 | 值描述 | 值类型 | 值形式 |
---|---|---|---|
confidence | 检测置信度 | float | conf. |
bbox_xyxy | 边界框 | np.ndarray (4) | [x1, y1, x2, y2] |
cxcy | 边界框中心 | tuple[float , float ] | (x, y) |
wh | 边界框宽度和高度 | tuple[float , float ] | (w, h) |
polygon_xy | 精确分割_QR_的多边形 | np.ndarray (N, 2) | [[x1, y1], [x2, y2], ...] |
quad_xy | 分割_QR_的四角多边形 | np.ndarray (4, 2) | [[x1, y1], ..., [x4, y4]] |
padded_quad_xy | 填充以完全覆盖polygon_xy 的quad_xy | np.ndarray (4, 2) | [[x1, y1], ..., [x4, y4]] |
image_shape | 输入图像的形状 | tuple[int , int ] | (h, w) |
注意:
- 所有
np.ndarray
值的类型为np.float32
- 除
confidence
和image_shape
外,所有键都有一个归一化('n')版本。例如,bbox_xyxy
表示图像坐标系中的QR边界框[[0., im_w], [0., im_h]],而bbox_xyxyn
包含归一化坐标[0., 1.]中的相同边界框。bbox_xyxy[n]
和polygon_xy[n]
被裁剪到image_shape
。你可以直接使用它们进行索引,无需进一步处理。
注意:这是你唯一需要的方法吗?看看QRDet。
QReader.decode(image, detection_result)
此方法根据给定的检测结果解码图像上的单个二维码。
内部实现上,该方法将运行 pyzbar 解码器,利用 detection_result
的信息,应用不同的图像预处理技术,大大提高解码成功率。
-
image
:np.ndarray。包含要解码的二维码的图像的 NumPy 数组。图像应为uint8
格式 [HxWxC],RGB。 -
detection_result
:dict[str, np.ndarray|float|tuple[float|int, float|int]]。detect 方法返回的检测字典之一。注意,QReader.detect() 返回这些字典的tuple
。此方法只需要其中一个。 -
返回值:str | None。二维码的解码内容,如果无法读取则返回
None
。
使用测试
以下代码将尝试使用 QReader、pyzbar 和 OpenCV 解码这些包含二维码的图像。
from qreader import QReader
from cv2 import QRCodeDetector, imread
from pyzbar.pyzbar import decode
# 初始化三个测试的读取器(QReader、OpenCV 和 pyzbar)
qreader_reader, cv2_reader, pyzbar_reader = QReader(), QRCodeDetector(), decode
for img_path in ('test_mobile.jpeg', 'test_draw_64x64.jpeg'):
# 读取图像
img = imread(img_path)
# 尝试使用三个读取器解码二维码
qreader_out = qreader_reader.detect_and_decode(image=img)
cv2_out = cv2_reader.detectAndDecode(img=img)[0]
pyzbar_out = pyzbar_reader(image=img)
# 读取 pyzbar 输出的内容(双重解码可以避免很多错误解码的字符)
pyzbar_out = tuple(out.data.data.decode('utf-8').encode('shift-jis').decode('utf-8') for out in pyzbar_out)
# 打印结果
print(f"图像:{img_path} -> QReader:{qreader_out}。OpenCV:{cv2_out}。pyzbar:{pyzbar_out}。")
上述代码的输出如下:
图像:test_mobile.jpeg -> QReader:('https://github.com/Eric-Canas/QReader')。OpenCV:。pyzbar:()。
图像:test_draw_64x64.jpeg -> QReader:('https://github.com/Eric-Canas/QReader')。OpenCV:。pyzbar:()。
请注意,QReader 内部使用 pyzbar 作为解码器。QReader 实现的更高检测-解码率来自于不同图像预处理技术的组合,以及基于 YOLOv8 的 二维码检测器,它能够在比传统计算机视觉方法更困难的条件下检测二维码。
运行测试
可以通过 pytest 启动测试。确保安装了包的测试版本
python -m pip install --editable ".[test]"
然后,你可以运行测试
python -m pytest tests/
基准测试
旋转测试
方法 | 最大旋转角度 |
---|---|
Pyzbar | 17º |
OpenCV | 46º |
QReader | 79º |