QReader
Robust and Straight-Forward solution for reading difficult and tricky QR codes within images in Python. Powered by YOLOv8
Install / Use
/learn @Eric-Canas/QReaderREADME
QReader
<img alt="QReader" title="QReader" src="https://raw.githubusercontent.com/Eric-Canas/QReader/main/documentation/resources/logo.png" width="20%" align="left"> QReader is a Robust and Straight-Forward solution for reading difficult and tricky QR codes within images in Python. Powered by a <a href="https://github.com/Eric-Canas/qrdet" target="_blank">YOLOv8</a> model.
Behind the scenes, the library is composed by two main building blocks: A <a href="https://github.com/ultralytics/ultralytics" target="_blank">YOLOv8</a> QR Detector model trained to detect and segment QR codes (also offered as <a href="https://github.com/Eric-Canas/qrdet" target="_blank">stand-alone</a>), and the <a href="https://github.com/NaturalHistoryMuseum/pyzbar" target="_blank">Pyzbar</a> QR Decoder. Using the information extracted from this QR Detector, QReader transparently applies, on top of <a href="https://github.com/NaturalHistoryMuseum/pyzbar" target="_blank">Pyzbar</a>, different image preprocessing techniques that maximize the decoding rate on difficult images.
Installation
To install QReader, simply run:
pip install qreader
You may need to install some additional pyzbar dependencies:
On Windows:
Rarely, you can see an ugly ImportError related with lizbar-64.dll. If it happens, install the vcredist_x64.exe from the Visual C++ Redistributable Packages for Visual Studio 2013
On Linux:
sudo apt-get install libzbar0
On Mac OS X:
brew install zbar
To install the QReader package locally, run pip
python -m pip install --editable .
NOTE: If you're running QReader in a server with very limited resources, you may want to install the CPU version of PyTorch, before installing QReader. To do so, run: pip install torch --no-cache-dir (Thanks to @cjwalther for his advice).
Usage
<a href="https://colab.research.google.com/github/Eric-Canas/QReader/blob/main/example.ipynb" target="_blank"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab" data-canonical-src="https://colab.research.google.com/assets/colab-badge.svg" style="max-width: 100%;"></a>
QReader is a very simple and straight-forward library. For most use cases, you'll only need to call detect_and_decode:
from qreader import QReader
import cv2
# Create a QReader instance
qreader = QReader()
# Get the image that contains the QR code
image = cv2.cvtColor(cv2.imread("path/to/image.png"), cv2.COLOR_BGR2RGB)
# Use the detect_and_decode function to get the decoded QR data
decoded_text = qreader.detect_and_decode(image=image)
detect_and_decode will return a tuple containing the decoded string of every QR found in the image.
NOTE: Some entries can be
None, it will happen when a QR have been detected but couldn't be decoded.
API Reference
QReader(model_size = 's', min_confidence = 0.5, reencode_to = 'shift-jis', weights_folder = None)
This is the main class of the library. Please, try to instantiate it just once to avoid loading the model every time you need to detect a QR code.
model_size: str. The size of the model to use. It can be 'n' (nano), 's' (small), 'm' (medium) or 'l' (large). Larger models could be more accurate but slower. Recommended: 's' (#37). Default: 's'.min_confidence: float. The minimum confidence of the QR detection to be considered valid. Values closer to 0.0 can get more False Positives, while values closer to 1.0 can lose difficult QRs. Default (and recommended): 0.5.reencode_to: str | None. The encoding to reencode theutf-8decoded QR string. If None, it won't re-encode. If you find some characters being decoded incorrectly, try to set a Code Page that matches your specific charset. Recommendations that have been found useful:- 'shift-jis' for Germanic languages
- 'cp65001' for Asian languages (Thanks to @nguyen-viet-hung for the suggestion)
weights_folder: str|None. Folder where the detection model will be downloaded. If None, it will be downloaded to the default qrdet package internal folder, making sure that it gets correctly removed when uninstalling. You could need to change it when working in environments like AWS Lambda where only /tmp folder is writable, as issued in #21. Default:None(<qrdet_package>/.model).
QReader.detect_and_decode(image, return_detections = False)
This method will decode the QR codes in the given image and return the decoded strings (or None, if any of them was detected but not decoded).
-
image: np.ndarray. The image to be read. It is expected to be RGB or BGR (uint8). Format (HxWx3). -
return_detections: bool. IfTrue, it will return the full detection results together with the decoded QRs. If False, it will return only the decoded content of the QR codes. -
is_bgr: boolean. IfTrue, the received image is expected to be BGR instead of RGB. -
Returns: tuple[str | None] | tuple[tuple[dict[str, np.ndarray | float | tuple[float | int, float | int]]], str | None]]: A tuple with all detected QR codes decodified. If
return_detectionsisFalse, the output will look like:('Decoded QR 1', 'Decoded QR 2', None, 'Decoded QR 4', ...). Ifreturn_detectionsisTrueit will look like:(('Decoded QR 1', {'bbox_xyxy': (x1_1, y1_1, x2_1, y2_1), 'confidence': conf_1}), ('Decoded QR 2', {'bbox_xyxy': (x1_2, y1_2, x2_2, y2_2), 'confidence': conf_2, ...}), ...). Look QReader.detect() for more information about detections format.
<a name="QReader_detect"></a>
QReader.detect(image)
This method detects the QR codes in the image and returns a tuple of dictionaries with all the detection information.
-
image: np.ndarray. The image to be read. It is expected to be RGB or BGR (uint8). Format (HxWx3). -
is_bgr: boolean. IfTrue, the received image is expected to be BGR instead of RGB. <a name="QReader_detect_table"></a> -
Returns: tuple[dict[str, np.ndarray|float|tuple[float|int, float|int]]]. A tuple of dictionaries containing all the information of every detection. Contains the following keys.
| Key | Value Desc. | Value Type | Value Form |
|------------------|---------------------------------------------|----------------------------|-----------------------------|
| confidence | Detection confidence | float | conf. |
| bbox_xyxy | Bounding box | np.ndarray (4) | [x1, y1, x2, y2] |
| cxcy | Center of bounding box | tuple[float, float] | (x, y) |
| wh | Bounding box width and height | tuple[float, float] | (w, h) |
| polygon_xy | Precise polygon that segments the QR | np.ndarray (N, 2) | [[x1, y1], [x2, y2], ...] |
| quad_xy | Four corners polygon that segments the QR | np.ndarray (4, 2) | [[x1, y1], ..., [x4, y4]] |
| padded_quad_xy |quad_xy padded to fully cover polygon_xy | np.ndarray (4, 2) | [[x1, y1], ..., [x4, y4]] |
| image_shape | Shape of the input image | tuple[int, int] | (h, w) |
NOTE:
- All
np.ndarrayvalues are of typenp.float32- All keys (except
confidenceandimage_shape) have a normalized ('n') version. For example,bbox_xyxyrepresents the bbox of the QR in image coordinates [[0., im_w], [0., im_h]], whilebbox_xyxyncontains the same bounding box in normalized coordinates [0., 1.].bbox_xyxy[n]andpolygon_xy[n]are clipped toimage_shape. You can use them for indexing without further management
NOTE: Is this the only method you will need? Take a look at <a href="https://github.com/Eric-Canas/qrdet" target="_blank">QRDet</a>.
QReader.decode(image, detection_result)
This method decodes a single QR code on the given image, described by a detection result.
Internally, this method will run the <a href="https://github.com/NaturalHistoryMuseum/pyzbar" target="_blank">pyzbar</a> decoder, using the information of the detection_result, to apply different image preprocessing techniques that heavily increase the detecoding rate.
-
image: np.ndarray. NumPy Array with theimagethat contains the QR to decode. The image is expected to be inuint8format [HxWxC], RGB. -
detection_result: dict[str, np.ndarray|float|tuple[float|int, float|int]]. One of the detection dicts returned by the detect method. Note that QReader.detect() returns atupleof thesedict. This method expects just one of them. -
Returns: str | None. The decoded content of the QR code or
Noneif it couldn't be read.
