From a62d6b71269ad8176ebb1baba99d63346fae4a40 Mon Sep 17 00:00:00 2001 From: Jan Domanski Date: Sat, 20 Jul 2024 14:30:31 +0100 Subject: [PATCH 1/9] further fixes --- qreader.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/qreader.py b/qreader.py index 8fbba5e..c162751 100644 --- a/qreader.py +++ b/qreader.py @@ -41,8 +41,11 @@ ("shift-jis", "big5") if os.name == "nt" else ("big5", "shift-jis") ) +from numpy.typing import NDArray + CorrectionsType = typing.Literal["cropped_bbox", "corrected_perspective"] FlavorType = typing.Literal["original", "inverted", "grayscale"] +ReencodeToType = typing.Union[str, typing.Sequence[str], None] @dataclass(frozen=True) @@ -82,7 +85,7 @@ def __init__( self, model_size: str = "s", min_confidence: float = 0.5, - reencode_to: str | tuple[str, ...] | list[str] | None = DEFAULT_REENCODINGS, + reencode_to: ReencodeToType = DEFAULT_REENCODINGS, weights_folder: str | None = None, ): """ @@ -119,7 +122,7 @@ def __init__( assert isinstance( reencode_to, (tuple, list) ), f"reencode_to must be a str, tuple, list or None. Got {type(reencode_to)}" - self.reencode_to = reencode_to + self.reencode_to = tuple(reencode_to) def detect( self, image: np.ndarray, is_bgr: bool = False @@ -331,7 +334,7 @@ def _decode_qr_zbar( results=decodedQR, ) # For QRs with black background and white foreground, try to invert the image - inverted_image = image = 255 - rescaled_image + inverted_image: NDArray[np.generic] = 255 - rescaled_image decodedQR = decodeQR(inverted_image, symbols=[ZBarSymbol.QRCODE]) if len(decodedQR) > 0: return wrap( From cf081bfa603ca9d2c7cdf74ed2cf8093777be504 Mon Sep 17 00:00:00 2001 From: Jan Domanski Date: Wed, 24 Jul 2024 13:07:23 +0100 Subject: [PATCH 2/9] update qreader.py --- qreader.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/qreader.py b/qreader.py index c162751..edaab4b 100644 --- a/qreader.py +++ b/qreader.py @@ -196,12 +196,10 @@ def decode( def detect_and_decode( self, image: np.ndarray, return_detections: bool = False, is_bgr: bool = False - ) -> ( - tuple[ - dict[str, np.ndarray | float | tuple[float | int, float | int]], str | None - ] - | tuple[str | None, ...] - ): + ) -> typing.Tuple[ + typing.Tuple[str | None, ...], + tuple[dict[str, np.ndarray | float | tuple[float | int, float | int]]] | None, + ]: """ 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). @@ -227,7 +225,7 @@ def detect_and_decode( if return_detections: return decoded_qrs, detections else: - return decoded_qrs + return decoded_qrs, None def get_detection_result_from_polygon( self, @@ -334,7 +332,7 @@ def _decode_qr_zbar( results=decodedQR, ) # For QRs with black background and white foreground, try to invert the image - inverted_image: NDArray[np.generic] = 255 - rescaled_image + inverted_image = np.array(255) - rescaled_image decodedQR = decodeQR(inverted_image, symbols=[ZBarSymbol.QRCODE]) if len(decodedQR) > 0: return wrap( From 00e18fbe8f8a511b845bf3bd9abce4a57201dbe3 Mon Sep 17 00:00:00 2001 From: Jan Domanski Date: Sat, 27 Jul 2024 17:52:05 +0100 Subject: [PATCH 3/9] refactor: project layout and pytest --- README.md | 6 ++++++ setup.py | 7 ++++--- qreader.py => src/qreader/__init__.py | 0 3 files changed, 10 insertions(+), 3 deletions(-) rename qreader.py => src/qreader/__init__.py (100%) diff --git a/README.md b/README.md index 226595a..6cb3c4c 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,12 @@ On **Mac OS X**: brew install zbar ``` +To install the QReader package locally, run pip + +```bash +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**](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). ## Usage diff --git a/setup.py b/setup.py index be98f3b..6f815e4 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,10 @@ -from setuptools import find_namespace_packages, setup +from setuptools import find_packages, setup setup( name="qreader", version="3.14", - packages=find_namespace_packages(), + packages=find_packages(where='src'), + package_dir={'': 'src'}, # expose qreader.py as the unique module py_modules=["qreader"], url="https://github.com/Eric-Canas/qreader", @@ -21,7 +22,7 @@ "qrdet>=2.5", ], extras_require={ - "tests": ["mypy"], + "tests": ["mypy", "pytest"], }, classifiers=[ "Development Status :: 5 - Production/Stable", diff --git a/qreader.py b/src/qreader/__init__.py similarity index 100% rename from qreader.py rename to src/qreader/__init__.py From b203778956a96df86917cca9e2ca1e22f906285b Mon Sep 17 00:00:00 2001 From: Jan Domanski Date: Sat, 27 Jul 2024 18:08:55 +0100 Subject: [PATCH 4/9] finish the changes, make sure tests are working --- README.md | 14 +++++++++ setup.py | 2 +- tests/__init__.py | 0 tests/integration/__init__.py | 0 main.py => tests/integration/test_qreader.py | 30 ++++++++++--------- tests/preformance/__init__.py | 0 .../preformance/test_performance.py | 14 ++++++--- 7 files changed, 41 insertions(+), 19 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/integration/__init__.py rename main.py => tests/integration/test_qreader.py (74%) create mode 100644 tests/preformance/__init__.py rename performance_test.py => tests/preformance/test_performance.py (82%) diff --git a/README.md b/README.md index 6cb3c4c..5a3c2c8 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,20 @@ Image: test_draw_64x64.jpeg -> QReader: ('https://github.com/Eric-Canas/QReader' Note that **QReader** internally uses pyzbar as **decoder**. The improved **detection-decoding rate** that **QReader** achieves comes from the combination of different image pre-processing techniques and the YOLOv8 based **QR** detector that is able to detect **QR** codes in harder conditions than classical _Computer Vision_ methods. +## Running tests + +The tests can be launched via pytest. Make sure you install the test version of the package + +```bash +python -m pip install --editable ".[test]" +``` + +Then, you can run the tests with + +```bash +python -m pytest tests/ +``` + ## Benchmark ### Rotation Test diff --git a/setup.py b/setup.py index 6f815e4..423ef82 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ "qrdet>=2.5", ], extras_require={ - "tests": ["mypy", "pytest"], + "tests": ["mypy", "pytest", "qrcode"], }, classifiers=[ "Development Status :: 5 - Production/Stable", diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/main.py b/tests/integration/test_qreader.py similarity index 74% rename from main.py rename to tests/integration/test_qreader.py index 96fcd35..6379682 100644 --- a/main.py +++ b/tests/integration/test_qreader.py @@ -1,17 +1,13 @@ import os import cv2 +import qrcode from qrdet import BBOX_XYXY from qreader import QReader -SAMPLE_IMG = os.path.join( - os.path.dirname(__file__), "documentation", "resources", "test_draw_64x64.jpeg" -) - -def utf_errors_test(): - import qrcode +def test_utf_errors(): qreader = QReader(model_size="n") image_path = "my_image.png" @@ -26,10 +22,21 @@ def utf_errors_test(): print(f"result = {result[0]}") -def decode_test_set(): +def test_decode_test_set(): images = [ - os.path.join(os.path.dirname(__file__), "testset", filename) - for filename in os.listdir(os.path.join(os.path.dirname(__file__), "testset")) + os.path.join( + os.path.dirname(__file__), + "..", + "..", + "documentation", + "resources", + filename, + ) + for filename in os.listdir( + os.path.join( + os.path.dirname(__file__), "..", "..", "documentation", "resources" + ) + ) ] # Initialize QReader detector = QReader(model_size="n") @@ -49,8 +56,3 @@ def decode_test_set(): pass # decoded_qrs = detector.detect_and_decode(image=img, return_detections=False) print("-------------------") - - -if __name__ == "__main__": - utf_errors_test() - # decode_test_set() diff --git a/tests/preformance/__init__.py b/tests/preformance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/performance_test.py b/tests/preformance/test_performance.py similarity index 82% rename from performance_test.py rename to tests/preformance/test_performance.py index 693fae4..12aa3fe 100644 --- a/performance_test.py +++ b/tests/preformance/test_performance.py @@ -8,13 +8,18 @@ from qreader import QReader SAMPLE_IMG_1 = os.path.join( - os.path.dirname(__file__), "documentation", "resources", "64x64.png" + os.path.dirname(__file__), "..", "..", "documentation", "resources", "64x64.png" ) SAMPLE_IMG_2 = os.path.join( - os.path.dirname(__file__), "documentation", "resources", "512x512.jpeg" + os.path.dirname(__file__), "..", "..", "documentation", "resources", "512x512.jpeg" ) SAMPLE_IMG_3 = os.path.join( - os.path.dirname(__file__), "documentation", "resources", "1024x1024.jpeg" + os.path.dirname(__file__), + "..", + "..", + "documentation", + "resources", + "1024x1024.jpeg", ) PERFORMANCE_TEST_IAMGES = { @@ -24,7 +29,8 @@ } RUNS_TO_AVERAGE, WARMUP_ITERATIONS = 5, 5 -if __name__ == "__main__": + +def test_performance(): results = {} for shape, img_path in tqdm(PERFORMANCE_TEST_IAMGES.items()): # Read the image From b88cb48b8cb423987fad0e40c686b6f0147fa242 Mon Sep 17 00:00:00 2001 From: Jan Domanski Date: Sat, 27 Jul 2024 18:14:03 +0100 Subject: [PATCH 5/9] create test.yml --- .github/workflows/test.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..8ee9a3c --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,26 @@ +name: Test +on: + push: + branches: + - main + pull_request: + branches: + - main +jobs: + pytest: + name: Run pytest + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' # Specify the Python version you are using + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install .[tests] + - name: Run pytest + run: | + python -m pytest tests/ From 04f956d978428c2413e8374cfe92a1186f4884fd Mon Sep 17 00:00:00 2001 From: Jan Domanski Date: Sat, 27 Jul 2024 18:19:54 +0100 Subject: [PATCH 6/9] fix setup.py formatting --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 423ef82..9f3b8be 100644 --- a/setup.py +++ b/setup.py @@ -3,8 +3,8 @@ setup( name="qreader", version="3.14", - packages=find_packages(where='src'), - package_dir={'': 'src'}, + packages=find_packages(where="src"), + package_dir={"": "src"}, # expose qreader.py as the unique module py_modules=["qreader"], url="https://github.com/Eric-Canas/qreader", From 44308086824143c72c83255ebb78fcba26dc677f Mon Sep 17 00:00:00 2001 From: Jan Domanski Date: Sat, 27 Jul 2024 18:20:32 +0100 Subject: [PATCH 7/9] fix mypy problem --- .github/workflows/lint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 01095aa..340f375 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -57,4 +57,5 @@ jobs: pip install ".[tests]" - name: Run mypy run: |- + mkdir -p .mypy_cache mypy --install-types --non-interactive qreader.py From fe08bccdc6fe2c16994d840c44c441329f6714c1 Mon Sep 17 00:00:00 2001 From: Jan Domanski Date: Sat, 27 Jul 2024 18:21:32 +0100 Subject: [PATCH 8/9] install zbar library --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8ee9a3c..9148956 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,6 +21,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install .[tests] + sudo apt-get install libzbar0 -y - name: Run pytest run: | python -m pytest tests/ From 8dc99c3518da9459170653c948a5e59358c5aa1c Mon Sep 17 00:00:00 2001 From: Jan Domanski Date: Sat, 27 Jul 2024 18:26:14 +0100 Subject: [PATCH 9/9] tweak mypy check --- .github/workflows/lint.yml | 2 +- setup.cfg | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 340f375..5dbe91e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -58,4 +58,4 @@ jobs: - name: Run mypy run: |- mkdir -p .mypy_cache - mypy --install-types --non-interactive qreader.py + mypy --install-types --non-interactive src tests diff --git a/setup.cfg b/setup.cfg index b042752..60d59bc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,3 +7,5 @@ license_files=LICENSE ignore_missing_imports = True [mypy-qrdet.*] ignore_missing_imports = True +[mypy-qrcode.*] +ignore_missing_imports = True