QR-код (код быстрого ответа) – это тип матричного штрих-кода, который содержит информацию об элементе, к которому он прикреплен, например, данные о местоположении, идентификатор или трекер, идентифицирующий веб-сайт или приложение и т.д. Это машиночитаемая оптическая этикетка, выполненная в виде 2D-изображения и имеющая рисунок определенного шаблона.
В данной статье мы рассмотрим создание сканера QR кодов на основе платы Raspberry Pi и библиотек OpenCV и ZBar. ZBar является наилучшей библиотекой для обнаружения и декодирования различных типов штриховых кодов и QR кодов. Библиотека OpenCV в нашем проекте будет использоваться для захвата нового кадра из видео потока и его обработки, а библиотека ZBar будет декодировать этот штриховой код и конвертировать его в вид, удобный для дальнейшей обработки (извлекать из него необходимую информацию). Дополнительно на нашем сайте мы рассматривали подключение к Raspberry Pi USB сканера штриховых кодов, с помощью которого можно также считывать и QR коды.
Также на нашем сайте вы можете проект распознавания лиц с помощью платы Raspberry Pi и библиотеки OpenCV.
Необходимые компоненты
- Плата Raspberry Pi 3 или выше (купить на AliExpress).
- Камера для Raspberry Pi (купить на AliExpress).
Установка OpenCV на Raspberry Pi
Для установки OpenCV на Raspberry Pi первым делом обновите программное обеспечение платы.
1 |
sudo apt-get update |
Далее установим ряд компонентов, необходимых для последующей установки OpenCV на плату Raspberry Pi.
1 2 3 4 5 6 |
sudo apt-get install libhdf5-dev -y sudo apt-get install libhdf5-serial-dev –y sudo apt-get install libatlas-base-dev –y sudo apt-get install libjasper-dev -y sudo apt-get install libqtgui4 –y sudo apt-get install libqt4-test –y |
После этого установим OpenCV на Raspberry Pi с помощью команды:
1 |
pip3 install opencv-contrib-python==4.1.0.25 |
Также, если с помощью описанной последовательности действий вам не удалось по каким либо причинам установить OpenCV на Raspberry Pi, то можете попробовать установить ее с помощью CMake.
Установка других необходимых пакетов
Установка ZBar
ZBar является лучшей в настоящее время библиотекой для обнаружения и декодирования различных типов штрих-кодов и QR кодов. Для ее установки используйте следующую команду:
1 |
pip3 install pyzbar |
Установка imutils
Библиотека imutils используется для выполнения типовых операций обработки изображений, таких как сдвиг, поворот, изменение размера, скелетирование. Установить ее можно с помощью следующей команды:
1 |
pip3 install imutils |
Установка argparse
Библиотека argparse ответственна за парсинг аргументов командной строки. Для ее установки используйте команду:
1 |
pip3 install argparse |
Схема проекта
Для реализации нашего проекта сканера QR кодов нам потребуются только плата Raspberry Pi и камера для нее, поэтому для сборки конструкции проекта вам потребуется только подключить камеру к соответствующему слоту платы Raspberry Pi с помощью ленточного кабеля.
Использование камеры вместе с платой Raspberry Pi вы можете посмотреть в таких проектах как:
- камера видеонаблюдения на Raspberry Pi с захватом движения;
- система безопасности на Raspberry Pi с оповещениями по Email;
- система мониторинга посетителей на основе Raspberry Pi и камеры.
Объяснение программы для Raspberry Pi
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
Первым делом в программе необходимо подключить (импортировать) все используемые библиотеки.
1 2 3 4 5 6 7 |
from imutils.video import VideoStream from pyzbar import pyzbar import argparse import datetime import imutils import time import cv2 |
Затем создадим парсер аргументов, с помощью которого будем парсить аргументы. Аргументы в командной строке (Command-line argument) содержат информацию о пути к CSV файлу. Файл CSV (Comma Separated Values – значения, разделенные запятыми) содержит информацию о времени и содержимом каждого штрих-кода, полученного из видео потока.
1 2 3 4 |
ap = argparse.ArgumentParser() ap.add_argument("-o", "--output", type=str, default="barcodes.csv", help="path to output CSV file containing barcodes") args = vars(ap.parse_args()) |
После этого инициализируем видео стрим и расскомментируем следующую закомментированную строчку если вы используете USB веб-камеру.
1 2 3 |
#vs = VideoStream(src=0).start() vs = VideoStream(usePiCamera=True).start() time.sleep(2.0) |
Далее, внутри цикла (loop) мы будем производить захват кадра из видео стрима и уменьшать его размер до 400 пикселов. После захвата кадра мы будем вызывать pyzbar.decode чтобы обнаруживать и декодировать QR код.
1 2 3 |
frame = vs.read() frame = imutils.resize(frame, width=400) barcodes = pyzbar.decode(frame) |
Затем в цикле мы будем извлекать местоположение штрих-кода и рисовать границы штрих-кода на его изображении.
1 2 3 |
for barcode in barcodes: (x, y, w, h) = barcode.rect cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2) |
Затем мы будем конвертировать информацию, содержащуюся в штрих-коде, в строку в формате "utf-8" с помощью соответствующей функции, и извлекать тип штрих-кода с помощью функции barcode.type.
1 2 |
barcodeData = barcode.data.decode("utf-8") barcodeType = barcode.type |
После этого сохраним извлеченные из штрих-кода данные и тип штрих-кода в переменной text и нарисуем данные штрих-кода и его тип на изображении.
1 2 3 |
text = "{} ({})".format(barcodeData, barcodeType) cv2.putText(frame, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) |
Затем покажем данные штрих-кода и его тип.
1 |
cv2.imshow("Barcode Reader", frame) |
Далее, на заключительном шаге проверим, нажата ли клавиша ‘s’. Если она нажата, то прерываем основной цикл программы и начинаем процесс очистки.
1 2 3 4 5 6 7 8 |
key = cv2.waitKey(1) & 0xFF # if the `s` key is pressed, break from the loop if key == ord("s"): break print("[INFO] cleaning up...") csv.close() cv2.destroyAllWindows() vs.stop() |
Тестирование работы сканера QR кодов
Когда аппаратная часть проекта будет готова запустите программу проекта на выполнение. После этого вы увидите окно, в котором будет транслироваться видео в реальном времени, получаемое с камеры. После этого в поле зрения камеры вы можете помещать QR коды (штрих-коды). Когда плата Raspberry Pi произведет декодирование QR кода, она нарисует прямоугольник красного цвета вокруг QR кода и напишет его тип как показано на рисунке ниже.
Более подробно работу проекта вы можете посмотреть на видео, приведенном в конце статьи.
Исходный код программы на Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
from imutils.video import VideoStream from pyzbar import pyzbar import argparse import datetime import imutils import time import cv2 ap = argparse.ArgumentParser() ap.add_argument("-o", "--output", type=str, default="barcodes.csv", help="path to output CSV file containing barcodes") args = vars(ap.parse_args()) #vs = VideoStream(src=0).start() # раскомментируйте эту строку если используете USB веб-камеру vs = VideoStream(usePiCamera=True).start() # для Pi Camera time.sleep(2.0) csv = open(args["output"], "w") found = set() while True: frame = vs.read() frame = imutils.resize(frame, width=400) barcodes = pyzbar.decode(frame) for barcode in barcodes: (x, y, w, h) = barcode.rect cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2) barcodeData = barcode.data.decode("utf-8") barcodeType = barcode.type text = "{} ({})".format(barcodeData, barcodeType) print (text) cv2.putText(frame, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) # if the barcode text is currently not in our CSV file, write (если текста штрих-кода нет в CSV файле, напишите) # the timestamp + barcode to disk and update the set (временную метку + штрих-код и обновите набор) if barcodeData not in found: csv.write("{},{}\n".format(datetime.datetime.now(), barcodeData)) csv.flush() found.add(barcodeData) cv2.imshow("Barcode Reader", frame) key = cv2.waitKey(1) & 0xFF # if the `s` key is pressed, break from the loop (если клавиша `s` нажата производим выход из цикла) if key == ord("s"): break print("[INFO] cleaning up...") csv.close() cv2.destroyAllWindows() vs.stop() |