Время прочтения: 5 мин.
В моей работе встретилась такая задача, как проверка кассовых чеков на корректность и легальность. Чеки хранились в отсканированном виде и требовалось найти решение получения платежных данных для последующей проверки в налоговых органах. На помощь пришла технология Computer Vision (компьютерное зрение). С помощью этой технологии получилось считать с изображений QR-коды чеков, расшифровать и проверить их.
Классическая задача компьютерного зрения — это обработка изображений и видеоданных, и библиотека pyzbar эту задачу успешно выполняет. Поиск QR-кода реализует на достаточно высоком уровне, поэтому мой выбор пал на нее.
Но для начала краткая истории появления QR-кода. QR-код (Quick response code – код быстрого реагирования) был разработан Масахиро Хара в японской компании Denso-Wave в 1994г. Несмотря на то что код был разработан для упрощения работы сотрудников на производстве за счет простоты и удобства он получил распространение во многих сферах деятельности человека.
В первой части статьи я расскажу, как сгенерировать свой QR-код. Во второй части будет рассказано как считать и декодировать QR-код.
Первым делом установим все необходимые библиотеки.
pip install pyzbar qrcode opencv-python glob
pyzbar - декодирование QR-кодов;
qrcode - создание 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 служит для восстановления кода, если код повредился и плохо читаем.
Каждый уровень указывает на процент данных для восстановления.
ERROR_CORRECT_L | 7% |
ERROR_CORRECT_M | 15% |
ERROR_CORRECT_Q | 25% |
ERROR_CORRECT_H | 30% |
В методе 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 создает список файлов по указанному пути и мы поочередно их считываем.
from pyzbar import pyzbar
import cv2
from glob import 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-кода.
Посмотрим удачно считанные коды.
t | s | fn | fd | fp | number |
t=20170221T095900 | s=30.00 | fn=99990789659 | i=501 | fp=4189225230 | n=1 |
t=20170512T080900 | s=1300.32 | fn=8710000100308965 | i=8218 | fp=3172198901 | n=1 |
t=20180717T1608 | s=350.00 | fn=8710000101441965 | i=3760 | fp=3967995548 | n=1 |
t=20190411T1620 | s=199.00 | fn=9289000100355637 | i=4522 | fp=4253291993 | n=1 |
t=20190320T2303 | s=5803.00 | fn=9251440300007971 | i=141637 | fp=4087570038 | n=1 |
А вот название файлов не найденных, либо не считанных кодов.
filename | |
0 | receipts\4.jpg |
Т.е. файлы, в которых pyzbar не нашел QR-кодов, можно дополнительно обработать, сделать изображение черно-белым добавляя четкости, убирать шумы и т.д.
Как вы уже увидели в создании и декодировании QR-кодов нет ничего сложного. Я показал лишь основу, которую вы можете модернизировать, добавляя дополнительные библиотеки или встраивая в другие программы. Это еще один пример того, как Computer Vision облегчает жизнь и работу людей.