{"id":18511991,"url":"https://github.com/eric-canas/qreader","last_synced_at":"2025-05-15T09:06:00.363Z","repository":{"id":64183022,"uuid":"573820339","full_name":"Eric-Canas/QReader","owner":"Eric-Canas","description":"Robust and Straight-Forward solution for reading difficult and tricky QR codes within images in Python. Powered by YOLOv8","archived":false,"fork":false,"pushed_at":"2025-02-22T19:47:50.000Z","size":40610,"stargazers_count":268,"open_issues_count":8,"forks_count":28,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-05-12T02:53:43.892Z","etag":null,"topics":["computer-vision","easy-to-use","image-processing","object-detection","pip","python","pytorch","pyzbar","qr","qrcode","qrcode-reader","qrcode-scanner","yolov8"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Eric-Canas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"Eric-Canas"}},"created_at":"2022-12-03T14:37:04.000Z","updated_at":"2025-05-07T06:51:43.000Z","dependencies_parsed_at":"2024-06-21T14:12:19.104Z","dependency_job_id":"6fa2b56d-2c5a-4a9e-8886-8e029f6d51e2","html_url":"https://github.com/Eric-Canas/QReader","commit_stats":{"total_commits":49,"total_committers":2,"mean_commits":24.5,"dds":"0.020408163265306145","last_synced_commit":"3972fb75f72ce5800b3bdecb025ff42b2cb7a7ac"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Eric-Canas%2FQReader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Eric-Canas%2FQReader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Eric-Canas%2FQReader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Eric-Canas%2FQReader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Eric-Canas","download_url":"https://codeload.github.com/Eric-Canas/QReader/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254310513,"owners_count":22049468,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["computer-vision","easy-to-use","image-processing","object-detection","pip","python","pytorch","pyzbar","qr","qrcode","qrcode-reader","qrcode-scanner","yolov8"],"created_at":"2024-11-06T15:31:24.866Z","updated_at":"2025-05-15T09:06:00.342Z","avatar_url":"https://github.com/Eric-Canas.png","language":"Python","funding_links":["https://github.com/sponsors/Eric-Canas"],"categories":[],"sub_categories":[],"readme":"# QReader\n\n\u003cimg alt=\"QReader\" title=\"QReader\" src=\"https://raw.githubusercontent.com/Eric-Canas/QReader/main/documentation/resources/logo.png\" width=\"20%\" align=\"left\"\u003e **QReader** is a **Robust** and **Straight-Forward** solution for reading **difficult** and **tricky** **QR** codes within images in **Python**. Powered by a \u003ca href=\"https://github.com/Eric-Canas/qrdet\" target=\"_blank\"\u003eYOLOv8\u003c/a\u003e model.\n\nBehind the scenes, the library is composed by two main building blocks: A \u003ca href=\"https://github.com/ultralytics/ultralytics\" target=\"_blank\"\u003eYOLOv8\u003c/a\u003e **QR Detector** model trained to **detect** and **segment** QR codes (also offered as \u003ca href=\"https://github.com/Eric-Canas/qrdet\" target=\"_blank\"\u003estand-alone\u003c/a\u003e), and the \u003ca href=\"https://github.com/NaturalHistoryMuseum/pyzbar\" target=\"_blank\"\u003ePyzbar\u003c/a\u003e **QR Decoder**. Using the information extracted from this **QR Detector**, **QReader** transparently applies, on top of \u003ca href=\"https://github.com/NaturalHistoryMuseum/pyzbar\" target=\"_blank\"\u003ePyzbar\u003c/a\u003e, different image preprocessing techniques that maximize the **decoding** rate on difficult images.\n\n\n## Installation\n\nTo install **QReader**, simply run:\n\n```bash\npip install qreader\n```\n\nYou may need to install some additional **pyzbar** dependencies:\n\nOn **Windows**:  \n\nRarely, you can see an ugly ImportError related with `lizbar-64.dll`. If it happens, install the [vcredist_x64.exe](https://www.microsoft.com/en-gb/download/details.aspx?id=40784) from the _Visual C++ Redistributable Packages for Visual Studio 2013_\n\nOn **Linux**:  \n```bash\nsudo apt-get install libzbar0\n```\n\nOn **Mac OS X**: \n```bash\nbrew install zbar\n```\n\nTo install the QReader package locally, run pip\n\n```bash\npython -m pip install --editable .\n```\n\n**NOTE:** If you're running **QReader** in a server with very limited resources, you may want to install the **CPU** version of [**PyTorch**](https://pytorch.org/get-started/locally/), before installing **QReader**. To do so, run: ``pip install torch --no-cache-dir`` (Thanks to [**@cjwalther**](https://github.com/Eric-Canas/QReader/issues/5) for his advice).\n\n## Usage\n\u003ca href=\"https://colab.research.google.com/github/Eric-Canas/QReader/blob/main/example.ipynb\" target=\"_blank\"\u003e\u003cimg 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%;\"\u003e\u003c/a\u003e\n\n**QReader** is a very simple and straight-forward library. For most use cases, you'll only need to call ``detect_and_decode``:\n\n```python\nfrom qreader import QReader\nimport cv2\n\n\n# Create a QReader instance\nqreader = QReader()\n\n# Get the image that contains the QR code\nimage = cv2.cvtColor(cv2.imread(\"path/to/image.png\"), cv2.COLOR_BGR2RGB)\n\n# Use the detect_and_decode function to get the decoded QR data\ndecoded_text = qreader.detect_and_decode(image=image)\n```\n\n``detect_and_decode`` will return a `tuple` containing the decoded _string_ of every **QR** found in the image. \n\n\u003e **NOTE**: Some entries can be `None`, it will happen when a **QR** have been detected but **couldn't be decoded**.\n\n\n## API Reference\n\n### QReader(model_size = 's', min_confidence = 0.5, reencode_to = 'shift-jis', weights_folder = None)\n\nThis 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.\n- ``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](https://github.com/Eric-Canas/QReader/issues/37)). Default: 's'.\n- ``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.\n- ``reencode_to``: **str** | **None**. The encoding to reencode the `utf-8` decoded QR string. If None, it won't re-encode. If you find some characters being decoded incorrectly, try to set a [Code Page](https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers) that matches your specific charset. Recommendations that have been found useful:\n  - 'shift-jis' for Germanic languages\n  - 'cp65001' for Asian languages (Thanks to @nguyen-viet-hung for the suggestion)\n- ``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](https://aws.amazon.com/es/pm/lambda/) where only [/tmp folder](https://docs.aws.amazon.com/lambda/latest/api/API_EphemeralStorage.html) is writable, as issued in [#21](https://github.com/Eric-Canas/QReader/issues/21). Default: `None` (_\u003cqrdet_package\u003e/.model_).\n\n### QReader.detect_and_decode(image, return_detections = False)\n\nThis 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).\n\n- ``image``: **np.ndarray**. The image to be read. It is expected to be _RGB_ or _BGR_ (_uint8_). Format (_HxWx3_).\n- ``return_detections``: **bool**. If `True`, 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.\n- ``is_bgr``: **boolean**. If `True`, the received image is expected to be _BGR_ instead of _RGB_.\n\n  \n- **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_detections`` is `False`, the output will look like: `('Decoded QR 1', 'Decoded QR 2', None, 'Decoded QR 4', ...)`. If ``return_detections`` is `True` it 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()](#QReader_detect_table) for more information about detections format.\n\n\u003ca name=\"QReader_detect\"\u003e\u003c/a\u003e\n\n### QReader.detect(image)\n\nThis method detects the **QR** codes in the image and returns a _tuple of dictionaries_ with all the detection information.\n\n- ``image``: **np.ndarray**. The image to be read. It is expected to be _RGB_ or _BGR_ (_uint8_). Format (_HxWx3_).\n- ``is_bgr``: **boolean**. If `True`, the received image is expected to be _BGR_ instead of _RGB_.\n\u003ca name=\"QReader_detect_table\"\u003e\u003c/a\u003e\n\n- **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.\n\n| Key              | Value Desc.                                 | Value Type                 | Value Form                  |\n|------------------|---------------------------------------------|----------------------------|-----------------------------|\n| `confidence`     | Detection confidence                        | `float`                    | `conf.`                     |\n| `bbox_xyxy`      | Bounding box                                | np.ndarray (**4**)         | `[x1, y1, x2, y2]`          |\n| `cxcy`           | Center of bounding box                      | tuple[`float`, `float`]    | `(x, y)`                    |\n| `wh`             | Bounding box width and height               | tuple[`float`, `float`]    | `(w, h)`                    |\n| `polygon_xy`     | Precise polygon that segments the _QR_      | np.ndarray (**N**, **2**)  | `[[x1, y1], [x2, y2], ...]` |\n| `quad_xy`        | Four corners polygon that segments the _QR_ | np.ndarray (**4**, **2**)  | `[[x1, y1], ..., [x4, y4]]` |\n| `padded_quad_xy` |`quad_xy` padded to fully cover `polygon_xy` | np.ndarray (**4**, **2**)  | `[[x1, y1], ..., [x4, y4]]` |\n| `image_shape`    | Shape of the input image                    | tuple[`int`, `int`]    | `(h, w)`                    |  \n\n\u003e **NOTE:**\n\u003e - All `np.ndarray` values are of type `np.float32` \n\u003e - All keys (except `confidence` and `image_shape`) have a normalized ('n') version. For example,`bbox_xyxy` represents the bbox of the QR in image coordinates [[0., im_w], [0., im_h]], while `bbox_xyxyn` contains the same bounding box in normalized coordinates [0., 1.].\n\u003e - `bbox_xyxy[n]` and `polygon_xy[n]` are clipped to `image_shape`. You can use them for indexing without further management\n\n\n**NOTE**: Is this the only method you will need? Take a look at \u003ca href=\"https://github.com/Eric-Canas/qrdet\" target=\"_blank\"\u003eQRDet\u003c/a\u003e.\n\n### QReader.decode(image, detection_result)\n\nThis method decodes a single **QR** code on the given image, described by a detection result. \n\nInternally, this method will run the \u003ca href=\"https://github.com/NaturalHistoryMuseum/pyzbar\" target=\"_blank\"\u003epyzbar\u003c/a\u003e decoder, using the information of the `detection_result`, to apply different image preprocessing techniques that heavily increase the detecoding rate.\n\n- ``image``: **np.ndarray**. NumPy Array with the ``image`` that contains the _QR_ to decode. The image is expected to be in ``uint8`` format [_HxWxC_], RGB.\n- ``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()](#QReader_detect) returns a `tuple` of these `dict`. This method expects just one of them.\n\n\n- Returns: **str | None**. The decoded content of the _QR_ code or `None` if it couldn't be read.\n\n## Usage Tests\n\u003cdiv\u003e\u003cimg alt=\"test_on_mobile\" title=\"test_on_mobile\" src=\"https://raw.githubusercontent.com/Eric-Canas/QReader/main/documentation/resources/test_mobile.jpeg\" width=\"60%\"\u003e\u003cimg alt=\"\" title=\"QReader\" src=\"https://raw.githubusercontent.com/Eric-Canas/QReader/main/documentation/resources/test_draw_64x64.jpeg\" width=\"32%\" align=\"right\"\u003e\u003c/div\u003e\n\u003cdiv\u003eTwo sample images. At left, an image taken with a mobile phone. At right, a 64x64 \u003cb\u003eQR\u003c/b\u003e pasted over a drawing.\u003c/div\u003e    \n\u003cbr\u003e\n\nThe following code will try to decode these images containing \u003cb\u003eQR\u003c/b\u003es with **QReader**, \u003ca href=\"https://github.com/NaturalHistoryMuseum/pyzbar\" target=\"_blank\"\u003epyzbar\u003c/a\u003e and \u003ca href=\"https://opencv.org/\" target=\"_blank\"\u003eOpenCV\u003c/a\u003e.\n```python\nfrom qreader import QReader\nfrom cv2 import QRCodeDetector, imread\nfrom pyzbar.pyzbar import decode\n\n# Initialize the three tested readers (QRReader, OpenCV and pyzbar)\nqreader_reader, cv2_reader, pyzbar_reader = QReader(), QRCodeDetector(), decode\n\nfor img_path in ('test_mobile.jpeg', 'test_draw_64x64.jpeg'):\n    # Read the image\n    img = imread(img_path)\n\n    # Try to decode the QR code with the three readers\n    qreader_out = qreader_reader.detect_and_decode(image=img)\n    cv2_out = cv2_reader.detectAndDecode(img=img)[0]\n    pyzbar_out = pyzbar_reader(image=img)\n    # Read the content of the pyzbar output (double decoding will save you from a lot of wrongly decoded characters)\n    pyzbar_out = tuple(out.data.data.decode('utf-8').encode('shift-jis').decode('utf-8') for out in pyzbar_out)\n\n    # Print the results\n    print(f\"Image: {img_path} -\u003e QReader: {qreader_out}. OpenCV: {cv2_out}. pyzbar: {pyzbar_out}.\")\n```\n\nThe output of the previous code is:\n\n```txt\nImage: test_mobile.jpeg -\u003e QReader: ('https://github.com/Eric-Canas/QReader'). OpenCV: . pyzbar: ().\nImage: test_draw_64x64.jpeg -\u003e QReader: ('https://github.com/Eric-Canas/QReader'). OpenCV: . pyzbar: ().\n```\n\nNote that **QReader** internally uses \u003ca href=\"https://github.com/NaturalHistoryMuseum/pyzbar\" target=\"_blank\"\u003epyzbar\u003c/a\u003e as **decoder**. The improved **detection-decoding rate** that **QReader** achieves comes from the combination of different image pre-processing techniques and the \u003ca href=\"https://github.com/ultralytics/ultralytics\" target=\"_blank\"\u003eYOLOv8\u003c/a\u003e based \u003ca href=\"https://github.com/Eric-Canas/qrdet\" target=\"_blank\"\u003e**QR** detector\u003c/a\u003e that is able to detect **QR** codes in harder conditions than classical _Computer Vision_ methods.\n\n## Running tests\n\nThe tests can be launched via pytest. Make sure you install the test version of the package\n\n```bash\npython -m pip install --editable \".[test]\"\n```\n\nThen, you can run the tests with\n\n```bash\npython -m pytest tests/\n```\n\n## Benchmark\n\n### Rotation Test\n\u003cdiv\u003e\n\u003cimg alt=\"Rotation Test\" title=\"Rotation Test\" src=\"https://raw.githubusercontent.com/Eric-Canas/QReader/main/documentation/benchmark/rotation_benchmark.gif\" width=\"40%\" align=\"left\"\u003e\n\n\u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp; \u0026nbsp;  \n\u003cdiv align=\"center\"\u003e\n  \n| Method  | Max Rotation Degrees  |\n|---------|-----------------------|\n| Pyzbar  | 17º                   |\n| OpenCV  | 46º                   |\n| QReader | 79º                   |\n  \n\u003c/div\u003e\n\u003c/div\u003e\n\n\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\n\n## Star History\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://api.star-history.com/svg?repos=Eric-Canas/QReader\u0026type=Date\" alt=\"Star History Chart\"\u003e\n\u003c/p\u003e\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feric-canas%2Fqreader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feric-canas%2Fqreader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feric-canas%2Fqreader/lists"}