QReader

repository·main·Indexed 19 days ago

https://github.com/eric-canas/qreader

A Python library for detecting and decoding difficult QR codes in images. QReader combines a YOLOv8-based detector for precise localization and segmentation with Pyzbar for high-performance decoding, applying automated preprocessing to handle challenging conditions such as high rotation. It provides methods for detection, decoding, and combined detect-and-decode workflows, supporting various model sizes (nano, small, medium, large) and custom confidence thresholds.

Tokens
4.2K
Snippets
15
Records
17
Agent score
64%

What's inside QReader

  1. Quickstart: Detect and decode QR codes

    main

    For most use cases, you only need to instantiate QReader once and call detect_and_decode. This method returns a tuple containing the decoded strings for every QR code found. Note that some entries may be None if a QR was detected but could not be successfully decoded.

    from qreader import QReader
    import cv2
    
    # Create a QReader instance (do this once to avoid reloading the model)
    qreader = QReader()
    
    # Load image and convert to RGB
    image = cv2.cvtColor(cv2.imread("path/to/image.png"), cv2.COLOR_BGR2RGB)
    
    # Get the decoded QR data
    decoded_text = qreader.detect_and_decode(image=image)
    # decoded_text is a tuple, e.g., ('Data 1', None, 'Data 2')
    from qreader import QReader
    import cv2
    
    qreader = QReader()
    image = cv2.cvtColor(cv2.imread("path/to/image.png"), cv2.COLOR_BGR2RGB)
    decoded_text = qreader.detect_and_decode(image=image)
  2. Install QReader

    main

    Install the main package using pip:

    pip install qreader

    Depending on your operating system, you may need to install additional pyzbar dependencies:

    Resource-constrained environments: If running on a server with limited resources, install the CPU version of PyTorch before installing QReader:

    pip install torch --no-cache-dir
    pip install qreader
  3. Run QReader tests

    main

    To run the project's internal tests, you must first install the package in editable mode with the [test] extra. Then, use pytest to execute the tests located in the tests/ directory.

    # Install the test version of the package
    python -m pip install --editable ".[test]"
    
    # Run the tests
    python -m pytest tests/
  4. Install QReader and system dependencies

    main

    To use QReader, you must install the Python package and the libzbar0 system library. If you are using a Jupyter notebook or Google Colab, use the following commands:

    !pip install qreader
    !sudo apt-get install libzbar0
  5. Compare QReader with OpenCV and pyzbar

    main

    You can compare the performance of QReader against traditional methods like OpenCV's QRCodeDetector and pyzbar. QReader uses a YOLOv8-based detector and image pre-processing to achieve higher detection and decoding rates, especially in difficult conditions like high rotation.

    Note that QReader internally uses pyzbar as its decoder.

    from qreader import QReader
    from cv2 import QRCodeDetector, imread
    from pyzbar.pyzbar import decode
    
    # Initialize the three tested readers (QReader, OpenCV and pyzbar)
    qreader_reader, cv2_reader, pyzbar_reader = QReader(), QRCodeDetector(), decode
    
    for img_path in ('test_mobile.jpeg', 'test_draw_64x64.jpeg'):
        # Read the image
        img = imread(img_path)
    
        # Try to decode the QR code with the three readers
        qreader_out = qreader_reader.detect_and_decode(image=img)
        cv2_out = cv2_reader.detectAndDecode(img=img)[0]
        pyzbar_out = pyzbar_reader(image=img)
        
        # Read the content of the pyzbar output (double decoding helps avoid wrongly decoded characters)
        pyzbar_out = tuple(out.data.data.decode('utf-8').encode('shift-jis').decode('utf-8') for out in pyzbar_out)
    
        # Print the results
        print(f"Image: {img_path} -> QReader: {qreader_out}. OpenCV: {cv2_out}. pyzbar: {pyzbar_out}.")
  6. Use QReader.decode() for a single detection

    main

    Decodes a specific QR code from an image using a previously obtained detection result. This method applies specialized preprocessing to maximize decoding success for that specific region.

    Parameters:

    • image (np.ndarray): The input image (uint8, HxWxC, RGB).
    • detection_result (dict): A single detection dictionary returned by QReader.detect().

    Returns:

    • str | None: The decoded content or None if decoding failed.
    detection = detections[0]
    decoded_content = qreader.decode(image=image, detection_result=detection)
  7. Use QReader.detect() to get detection metadata

    main

    Detects QR codes and returns detailed metadata for each detection without attempting to decode the content.

    Parameters:

    • image (np.ndarray): The input image (uint8, HxWx3).
    • is_bgr (bool): If True, treats the input as BGR.

    Returns: tuple[dict, ...] where each dictionary contains:

    • confidence: Detection confidence (float).
    • bbox_xyxy: Bounding box [x1, y1, x2, y2] (np.ndarray).
    • cxcy: Center of bounding box (x, y) (tuple[float, float]).
    • wh: Bounding box width and height (w, h) (tuple[float, float]).
    • polygon_xy: Precise segmentation polygon (np.ndarray).
    • quad_xy: Four corners polygon (np.ndarray).
    • padded_quad_xy: quad_xy padded to cover polygon_xy (np.ndarray).
    • image_shape: Input image shape (h, w) (tuple[int, int]).

    Note: All keys (except confidence and image_shape) have a normalized version ending in n (e.g., bbox_xyxyn) representing coordinates in the range [0, 1].

    detections = qreader.detect(image=image)
  8. Configure the QReader instance

    main

    The QReader class constructor allows you to tune the detection and decoding behavior:

    • model_size (str): The size of the YOLOv8 model. Options: 'n' (nano), 's' (small), 'm' (medium), or 'l' (large). 's' is recommended. Default: 's'.
    • min_confidence (float): Minimum confidence for a detection to be valid. Values closer to 0.0 increase false positives; values closer to 1.0 may miss difficult QRs. Default: 0.5.
    • reencode_to (str | None): The encoding to reencode the utf-8 decoded string into. Useful for specific charsets. Examples: 'shift-jis' for Germanic languages, 'cp65001' for Asian languages. Default: None.
    • weights_folder (str | None): Custom directory to download/store detection models. Useful for read-only environments like AWS Lambda (e.g., set to /tmp). Default: None.
    qreader = QReader(model_size='s', min_confidence=0.5, reencode_to='shift-jis', weights_folder='/tmp')
  9. Use QReader.detect_and_decode()

    main

    Decodes all QR codes in an image.

    Parameters:

    • image (np.ndarray): The input image in RGB or BGR format (uint8, HxWx3).
    • return_detections (bool): If True, returns a tuple of (decoded_string, detection_dict). If False, returns only the decoded_string. Default: False.
    • is_bgr (bool): If True, treats the input as BGR instead of RGB.

    Returns:

    • If return_detections=False: tuple[str | None, ...]
    • If return_detections=True: tuple[tuple[str | None, dict], ...]
    decoded_text = qreader.detect_and_decode(image=image, return_detections=True)
  10. Detect and decode QR codes in an image

    main

    You can use the detect_and_decode method on a QReader instance to find and decode all QR codes within an image. By setting return_detections=True, the method returns both the decoded strings and their spatial locations.

    Input images should be provided as numpy.ndarray objects (typically loaded via OpenCV).

    from qreader import QReader
    import cv2
    import numpy as np
    
    # Load your image as a numpy array
    img = cv2.imread('path_to_your_image.png')
    
    # Initialize the detector
    detector = QReader()
    
    # Detect and decode the QRs within the image
    decodedQRs, QRlocations = detector.detect_and_decode(image=img, return_detections=True)
    
    # Iterate through results
    for i, (decodedQR, QRlocation) in enumerate(zip(decodedQRs, QRlocations)):
        print(f"QR {i+1}: {decodedQR}")
        # QRlocation contains spatial data, e.g., center coordinates
        print(f"QR {i+1} position: x: {QRlocation['cxcyn'][0]}, y: {QRlocation['cxcyn'][1]}")
  11. Simulate a detection result from a quadrilateral

    main

    If you are using a different QR detector but want to use QReader's robust decoding logic, use get_detection_result_from_polygon to create a compatible detection dictionary.

    Parameters

    • quadrilateral_xy (np.ndarray | Sequence): A quadrilateral surrounding the QR code with shape (4, 2). Must be in absolute coordinates. Format: ((x1, y1), (x2, y2), (x3, y3), (x4, y4)).
    import numpy as np
    
    # Example quadrilateral
    quad = [(10, 10), (100, 15), (95, 110), (5, 105)]
    fake_detection = reader.get_detection_result_from_polygon(quad)
    
    # Now you can use this with the decode method
    text = reader.decode(image_rgb, fake_detection)