- Computer Vision в помощь декодирования QR-кода на Python
- qreader 2.13
- Навигация
- Ссылки проекта
- Статистика
- Метаданные
- Сопровождающие
- Классификаторы
- Описание проекта
- QReader
- Installation
- Usage
- API Reference
- QReader(reencode_to = ‘shift-jis’)
- QReader.detect_and_decode(image, return_bboxes = False)
- QReader.detect(image)
- QReader.decode(image, bbox = None)
- Usage Tests
- Acknowledgements
Computer Vision в помощь декодирования QR-кода на Python
В моей работе встретилась такая задача, как проверка кассовых чеков на корректность и легальность. Чеки хранились в отсканированном виде и требовалось найти решение получения платежных данных для последующей проверки в налоговых органах. На помощь пришла технология Computer Vision (компьютерное зрение). С помощью этой технологии получилось считать с изображений QR-коды чеков, расшифровать и проверить их.
Классическая задача компьютерного зрения — это обработка изображений и видеоданных, и библиотека pyzbar эту задачу успешно выполняет. Поиск QR-кода реализует на достаточно высоком уровне, поэтому мой выбор пал на нее.
Но для начала краткая истории появления QR-кода. QR-код (Quick response code – код быстрого реагирования) был разработан Масахиро Хара в японской компании Denso-Wave в 1994г. Несмотря на то что код был разработан для упрощения работы сотрудников на производстве за счет простоты и удобства он получил распространение во многих сферах деятельности человека.
В первой части статьи я расскажу, как сгенерировать свой QR-код. Во второй части будет рассказано как считать и декодировать QR-код.
Первым делом установим все необходимые библиотеки.
pyzbar — декодирование QR-кодов;
opencv-python — работа с изображениями;
glob — считывание всех файлов в указанной папке.
Как сгенерировать QR-код
Создать собственный QR-код легче, чем может показаться.
import qrcode qr = qrcode.QRCode() qr.add_data(‘https://yandex.ru’) img = qr.make_image() img.save(‘ya.png’)
Если требуется создать код под свои нужды, можно вписать параметры создания QR-кода.
В таком простом исполнении параметры создания QR-кода будут выставлены автоматически, но можно создать и по своим параметрам.
qr = qrcode.QRCode( version = 3, box_size = 10, border = 2, error_correction = qrcode.constants.ERROR_CORRECT_Q ) qr.add_data(‘https://yandex.ru’) img = qr.make_image(back_color=’yellow’, fill_color=’black’) img.save(‘ya2.png’)
Тот же самый адрес сайта, но QR-код уже отличается.
Параметр version может быть от 1 до 40, от 21х21 до 177х177 пикселей, не учитывая поля.
Параметр box_size отвечает за количество пикселей в каждом квадрате QR-кода.
Параметр border создает границу вокруг QR-кода.
Параметр Error_correction служит для восстановления кода, если код повредился и плохо читаем.
Каждый уровень указывает на процент данных для восстановления.
В методе make_image() добавил параметр back_color, отвечающий за цвет фона и fill_color- за цвет QR-кода.
Добавим логотип в наш QR-код. Раз у нас ссылка ведет на сайт yandex.ru, то и логотип будем использовать от Яндекса.
from PIL import Image logo = Image.open(‘yandex_logo.png’) qr = qrcode.QRCode( version = 3, box_size = 10, border = 2, error_correction = qrcode.constants.ERROR_CORRECT_H ) pos = ( (img.size[0] — logo.size[0]) // 2, (img.size[1] — logo.size[1]) // 2, ) qr.add_data(‘https://yandex.ru’) img = qr.make_image().convert(‘RGB’) # конвертируем в RGB чтобы логотип был цветным img.paste(logo, pos) img.save(‘yandex_qr_logo.png’)
Вставляя логотип, мы повреждаем часть QR-кода, но из-за того, что мы добавили 30% на восстановление при ошибках, код считывается без проблем.
Перейдем ко второй части и попробуем декодировать зашифрованные данные кода.
Как декодировать QR-код
Импортируем установленные библиотеки.
Также я буду использовать библиотеку glob. Glob создает список файлов по указанному пути и мы поочередно их считываем.
Чтобы показать, как работает библиотека, считаем 1 чек и посмотрим, какой результат она выдаст.
filename = ‘receipts/1.jpg’ img = cv2.imread(filename) # Считываем файл с изображением qrcodes = pyzbar.decode(img) # Создается список найденных кодов print(qrcodes)
pyzbar.decode() принимает изображение с QR-кодом и декодирует его, выдавая параметры самого QR-кода и обнаруженные данные.
[Decoded(data=b’t=20170221T095900&s=30.00&fn=99990789659&i=501&fp=4189225230&n=1′, type=’QRCODE’, rect=Rect(left=111, top=462, width=159, height=159), polygon= [Point(x=111, y=462), Point(x=112, y=619), Point(x=270, y=621), Point(x=268, y=462)])]Для тестирования я взял несколько чеков, для тестового решения можно взять любые QR-коды, которые вы сможете найти.
Для упрощения работы все операции с изображением я вывел в отдельную функцию.
def getQr(filename): all_data = [] img = cv2.imread(filename) # Считываем файл с изображением qrcodes = pyzbar.decode(img) # Создается список найденных кодов for qrcode in qrcodes: qrcodeData = qrcode.data.decode(‘utf-8’) if qrcode.type == ‘QRCODE’ and ‘fn=’ in qrcodeData: # проверяем тип кода и проверяем вхождение строки ‘fn=’ all_data.append(qrcodeData.split(‘&’)) return all_data
В основном коде дополнительно импортировал библиотеку pandas для создания датафрейма.
# данные чеков которые я буду собирать в отдельную таблицу # t — timestamp, время, когда осуществлена покупка # s — сумма чека # fn —номер фискального накопителя # fd — номер фискального документа # fp — информация для проверки ФД # n — номер чека import pandas as pd df_good = pd.DataFrame() # Удачно считанные данные df_bad = pd.DataFrame() # список не распознанных кодов for filename in glob(‘receipts/*.jpg’): # Считываем тип файлов только jpg data = getQr(filename) if data != []: for cell in data: # если на изображении больше 1 кода df_good = df_good.append(pd.DataFrame([cell])) else: df_bad = df_bad.append(pd.DataFrame([filename])) if len(df_good) != 0: df_good.columns = [‘t’, ‘s’, ‘fn’, ‘fd’, ‘fp’, ‘number’] df_good.reset_index(drop=True, inplace=True) if len(df_bad) != 0: df_bad.columns = [‘filename’] df_bad.reset_index(drop=True, inplace=True) data — сами данные которые мне нужны; type — тип кода, меня интересовал тип ‘QRCODE’; rect — координаты QR-кода на изображении; polygon – координаты точек(углов) QR-кода.
Посмотрим удачно считанные коды.
qreader 2.13
Robust and Straight-Forward solution for reading difficult and tricky QR codes within images in Python. Supported by a YOLOv7 QR Detection model.
Навигация
Ссылки проекта
Статистика
Метаданные
Лицензия: MIT License (MIT)
Сопровождающие
Классификаторы
- Development Status
- 5 — Production/Stable
- Developers
- Science/Research
- OSI Approved :: MIT License
- OS Independent
- Python :: 3
- Scientific/Engineering :: Image Recognition
- Software Development :: Libraries :: Python Modules
- Utilities
Описание проекта
QReader
QReader is a Robust and Straight-Forward solution for reading difficult and tricky QR codes within images in Python. Powered by a YOLOv7 model.
Behind the scenes, the library is composed by two main building blocks: A QR Detector based on a YOLOv7 model trained on a large dataset of QR codes (also offered as stand-alone), and the Pyzbar QR Decoder. On top of Pyzbar, QReader transparently applyes different image preprocessing techniques that maximize the decoding rate on difficult images.
Installation
To install QReader, simply run:
If you’re not using Windows, you may need to install some additional pyzbar dependencies:
sudo apt-get install libzbar0
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
QReader is a very simple and straight-forward library. For most use cases, you’ll only need to call detect_and_decode :
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(reencode_to = ‘shift-jis’)
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.
- reencode_to : str or 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 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)
QReader.detect_and_decode(image, return_bboxes = False)
This method will decode the QR codes in the given image and return the decoded strings (or None, if any of them could be detected but not decoded).
- image : np.ndarray. NumPy Array containing the image to decode. The image is expected to be in uint8 format [HxWxC], RGB.
- return_bboxes : boolean. If True , it will also return the bboxes of each detected QR. Default: False
- Returns: tuple[str | None] | tuple[tuple[tuple[int, int, int, int], str | None]]: A tuple with all detected QR codes decodified. If return_bboxes is False , the output will look like: (‘Decoded QR 1’, ‘Decoded QR 2’, None, ‘Decoded QR 4’, . ) . If return_bboxes is True it will look like: (((x1_1, y1_1, x2_1, y2_1), ‘Decoded QR 1’), ((x1_2, y1_2, x2_2, y2_2), ‘Decoded QR 2’), . ) .
QReader.detect(image)
This method detects the QR codes in the image and returns the bounding boxes surrounding them in the format (x1, y1, x2, y2).
- image : np.ndarray. NumPy Array containing the image to decode. The image must is expected to be in uint8 format [HxWxC], RGB.
- Returns: tuple[tuple[int, int, int, int]]. The bounding boxes of the QR code in the format ((x1_1, y1_1, x2_1, y2_1), (x1_1, y1_1, x2_1, x2_2)) .
NOTE: This the only function you will need? Take a look at QRDet.
QReader.decode(image, bbox = None)
This method decodes a single QR code on the given image, if a bbox is given (recommended) it will only look within that delimited region.
Internally, this method will run the pyzbar decoder, using different image preprocessing techniques (sharpening, binarization, blurring. ) every time it fails to increase the detection rate.
- image : np.ndarray. NumPy Array containing the image to decode. The image must is expected to be in uint8 format [HxWxC], RGB.
- bbox : tuple[int, int, int, int] | None. The bounding box of the QR code in the format (x1, y1, x2, y2) [that’s the output of detect ]. If None , it will look for the QR code in the whole image (not recommended). Default: None .
- Returns: str. The decoded text of the QR code. If no QR code can be decoded, it will return None .
Usage Tests
Two sample images. At left, an image taken with a mobile phone. At right, a 64×64 QR pasted over a drawing.
The following code will try to decode these images containing QRs with QReader, pyzbar and OpenCV.
QReader: The output of the previous code is:
Image: test_mobile.jpeg -> QReader: ('https://github.com/Eric-Canas/QReader'). OpenCV: . pyzbar: (). Image: test_draw_64x64.jpeg -> QReader: ('https://github.com/Eric-Canas/QReader'). OpenCV: . pyzbar: ().
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 YOLOv7 based QR detector that is able to detect QR codes in harder conditions than classical Computer Vision methods.
Acknowledgements
This library is based on the following projects:
- YoloV7 model for Object Detection.
- PyzbarQR Decoder.
- OpenCV methods for image filtering.