From 549a16cfc92ab00c862debe736f36798b0fa21d9 Mon Sep 17 00:00:00 2001 From: Lars Date: Tue, 6 Feb 2024 22:20:39 +0100 Subject: [PATCH] Use OpenCV for decoding test cases which do not use Kanji. Avoids problems with zbar and ISO 8859-1 encoding, see issue #134. --- .gitignore | 1 + tests/requirements.txt | 1 + tests/test_encode_decode.py | 47 ++++++++++++++++++++++--------- tests/test_issue_105_epc_slash.py | 29 ++++++------------- tests/test_issue_109_bytes.py | 15 +++------- 5 files changed, 49 insertions(+), 44 deletions(-) diff --git a/.gitignore b/.gitignore index b49e9224..6b3d48ae 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .coverage .[nt]ox/ .idea/ +.venv/ *.egg-info dist/ build/ diff --git a/tests/requirements.txt b/tests/requirements.txt index 08051620..0b3f8ca2 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -4,6 +4,7 @@ pytest-cov pypng~=0.0.20 pyzbar~=0.1.8 qrcode-artistic +opencv-python Pillow importlib-metadata>=3.6.0; python_version < '3.10' diff --git a/tests/test_encode_decode.py b/tests/test_encode_decode.py index 131a95f2..40cd4465 100644 --- a/tests/test_encode_decode.py +++ b/tests/test_encode_decode.py @@ -11,6 +11,8 @@ Requires pyzbar and additional libs (libzbar0). """ import io +import cv2 as cv +import numpy as np import pytest import segno try: @@ -19,25 +21,35 @@ pytestmark = pytest.mark.skip -def qr_to_bytes(qrcode, scale): - if qrcode.is_micro: - raise Exception('zbar cannot decode Micro QR codes') - buff = io.BytesIO() - for row in qrcode.matrix_iter(scale=scale): - buff.write(bytearray(0x0 if b else 0xff for b in row)) - return buff.getvalue() - - -def decode(qrcode): +def decode_kanji(qrcode): scale = 3 width, height = qrcode.symbol_size(scale=scale) - qr_bytes = qr_to_bytes(qrcode, scale) - decoded = zbardecode((qr_bytes, width, height)) + out = io.BytesIO() + for row in qrcode.matrix_iter(scale=scale): + out.write(bytearray(0x0 if b else 0xff for b in row)) + decoded = zbardecode((out.getvalue(), width, height)) assert 1 == len(decoded) assert 'QRCODE' == decoded[0].type return decoded[0].data.decode('utf-8') +def decode_cv(qrcode): + out = io.BytesIO() + qrcode.save(out, scale=3, kind='png') + out.seek(0) + img = cv.imdecode(np.frombuffer(out.getvalue(), np.uint8), flags=cv.IMREAD_COLOR) + detector = cv.QRCodeDetector() + decoded, points, qrcode_bin = detector.detectAndDecode(img) + return decoded or None + + +def decode(qrcode): + if qrcode.is_micro: + raise Exception('Cannot decode Micro QR codes') + # OpenCV does not support Kanji + return decode_cv(qrcode) if qrcode.mode != 'kanji' else decode_kanji(qrcode) + + @pytest.mark.parametrize('content, mode', [('漢字', 'kanji'), ('続きを読む', 'kanji'), @@ -56,7 +68,7 @@ def test_stackoverflow_issue(): content = 'Thomsôn Gonçalves Ámaral,325.432.123-21' qr = segno.make(content, encoding='utf-8') assert 'byte' == qr.mode - assert content == decode(qr).encode('shift-jis').decode('utf-8') + assert content == decode(qr) def test_pyqrcode_issue76(): @@ -73,5 +85,14 @@ def test_pyqrcode_issue76(): assert content == decode(qr) +@pytest.mark.parametrize('encoding', [None, 'latin1', 'ISO-8859-1', 'utf-8']) +def test_issue134(encoding): + # See + content = 'Märchen' + qr = segno.make(content, encoding=encoding, micro=False) + assert 'byte' == qr.mode + assert content == decode(qr) + + if __name__ == '__main__': pytest.main([__file__]) diff --git a/tests/test_issue_105_epc_slash.py b/tests/test_issue_105_epc_slash.py index 39751b3f..c8de32ad 100644 --- a/tests/test_issue_105_epc_slash.py +++ b/tests/test_issue_105_epc_slash.py @@ -13,30 +13,19 @@ """ import io import pytest +import cv2 as cv +import numpy as np from segno.helpers import make_epc_qr -try: - from pyzbar.pyzbar import decode as zbardecode -except (ImportError, FileNotFoundError): # The latter may occur under Windows - pytestmark = pytest.mark.skip - - -def qr_to_bytes(qrcode, scale): - if qrcode.is_micro: - raise Exception('zbar cannot decode Micro QR codes') - buff = io.BytesIO() - for row in qrcode.matrix_iter(scale=scale): - buff.write(bytearray(0x0 if b else 0xff for b in row)) - return buff.getvalue() def decode(qrcode): - scale = 3 - width, height = qrcode.symbol_size(scale=scale) - qr_bytes = qr_to_bytes(qrcode, scale) - decoded = zbardecode((qr_bytes, width, height)) - assert 1 == len(decoded) - assert 'QRCODE' == decoded[0].type - return decoded[0].data.decode('utf-8') + out = io.BytesIO() + qrcode.save(out, scale=3, kind='png') + out.seek(0) + img = cv.imdecode(np.frombuffer(out.getvalue(), np.uint8), flags=cv.IMREAD_COLOR) + detector = cv.QRCodeDetector() + decoded, points, qrcode_bin = detector.detectAndDecode(img) + return decoded or None @pytest.mark.parametrize('text', ['/', diff --git a/tests/test_issue_109_bytes.py b/tests/test_issue_109_bytes.py index 7e5ba9a1..d16d1e81 100644 --- a/tests/test_issue_109_bytes.py +++ b/tests/test_issue_109_bytes.py @@ -20,20 +20,13 @@ pytestmark = pytest.mark.skip -def qr_to_bytes(qrcode, scale): - if qrcode.is_micro: - raise Exception('zbar cannot decode Micro QR codes') - buff = io.BytesIO() - for row in qrcode.matrix_iter(scale=scale): - buff.write(bytearray(0x0 if b else 0xff for b in row)) - return buff.getvalue() - - def decode(qrcode): scale = 3 width, height = qrcode.symbol_size(scale=scale) - qr_bytes = qr_to_bytes(qrcode, scale) - decoded = zbardecode((qr_bytes, width, height)) + out = io.BytesIO() + for row in qrcode.matrix_iter(scale=scale): + out.write(bytearray(0x0 if b else 0xff for b in row)) + decoded = zbardecode((out.getvalue(), width, height)) assert 1 == len(decoded) assert 'QRCODE' == decoded[0].type return decoded[0].data.decode('utf-8').encode('cp932')