Лабораторна робота 10
Лабораторна робота №10. Розпізнавання QR-кодів
Мета: навчитися здійснювати пошук і розпізнавання QR-кодів на зображеннях, виконувати попередню обробку та виділення областей інтересу для підвищення якості розпізнавання, а також валідувати декодоване навантаження за форматом MERC-I5 перед його використанням у сценарії складу.
Результати навчання та передумови
Після виконання студент уміє:
- застосовувати
cv2.QRCodeDetectorдля одного та кількох кодів на зображенні; - виділяти області інтересу й обґрунтовувати їх межі;
- відрізняти «код знайдено» від «код декодовано» і від «навантаження валідне»;
- перевіряти декодоване навантаження за схемою
merc-i5.workpiece.v1; - обробляти невідомий, пошкоджений і повторний код без переходу до наступного кроку;
- готувати результат розпізнавання до запису в базу даних розумного складу.
Передумови: базовий Python, NumPy, індексація масивів, основи OpenCV. Апаратний допуск не потрібний.
Середовище виконання
| Середовище | Статус | Примітка |
|---|---|---|
| Google Colab | основне | Виконання на CPU; GPU не потрібен |
| Локальний ПК | дозволено | Потрібні Python, OpenCV, NumPy і Matplotlib; версії потребують перевірки |
| Raspberry Pi | не використовується | Перевірка на камері комплексу запланована для наступної версії роботи |
| Фізичний комплекс MERC-I5 | не потрібен | Робота виконується з готовими зображеннями без руху обладнання |
Необхідні знання та матеріали
- базовий синтаксис Python і робота з масивами NumPy;
- індексація зображень і побудова зрізів;
- бібліотеки OpenCV і Matplotlib;
- навчальні зображення та
CV_libraryз репозиторіюfirst_labs; - формат QR MERC-I5 v1 і модель даних складу;
- приклади навантажень у
data/qr/valid.jsonl,invalid.jsonlіduplicate-conflict.jsonl; - ролі камер комплексу.
Ризики та правила безпеки
Робота виконується з готовими зображеннями. Фізичний рух робота, конвеєрів і подавачів не потрібний і не дозволений у межах цієї роботи.
Заборонені дії
- запускати рух будь-якого механізму комплексу;
- вважати непорожній декодований рядок валідним ідентифікатором виробу без перевірки схеми;
- записувати в базу даних результат, який не пройшов валідацію;
- публікувати
key_wordразом зі звітом, якщо він містить персональні дані.
Безпечний стан у логіці розпізнавання
Якщо код не прочитано, прочитано частково або навантаження не пройшло валідацію — маршрут виробу не призначається, а цикл переходить у стан помилки й очікує рішення оператора. Автоматичне продовження руху заборонене.
Хід виконання роботи
- Опрацювати методичні вказівки.
- Отримати індивідуальний варіант завдання.
- Виконати автоматичне розпізнавання QR-кодів засобами OpenCV.
- У випадку неповного розпізнавання виділити області інтересу й повторити зчитування.
- Реалізувати валідацію декодованого навантаження за схемою
merc-i5.workpiece.v1. - Перевірити поведінку на невалідних і суперечливих навантаженнях.
- Зробити висновки щодо якості розпізнавання та впливу попередньої обробки.
- Оформити звіт і надіслати викладачу для перевірки.
Методичні вказівки й теоретичні відомості
1. Теоретичні відомості
QR-код (Quick Response Code) — це двовимірний матричний штрихкод, призначений для швидкого зберігання та зчитування інформації за допомогою комп'ютерних систем і мобільних пристроїв.
На відміну від лінійних штрихкодів, QR-код дозволяє зберігати значно більший обсяг інформації та містить механізми виправлення помилок, що забезпечують можливість розпізнавання навіть при частковому пошкодженні зображення.
QR-коди можуть містити текстову інформацію, числові дані, посилання на веб-ресурси, контактні дані, JSON-структури, службову інформацію промислових систем. У сучасних системах комп'ютерного зору QR-коди широко використовуються для маркування продукції, автоматичної ідентифікації об'єктів, контролю технологічних процесів, відстеження виробів на конвеєрі, передачі невеликих обсягів структурованих даних.
2. Програмна реалізація
Для роботи з QR-кодами у бібліотеці OpenCV використовується клас:
cv2.QRCodeDetector()
Об'єкт даного класу виконує:
-
пошук QR-кодів на зображенні;
-
визначення положення QR-кодів;
-
декодування інформації, що міститься в кодах.
Створення об'єкта:
detector = cv2.QRCodeDetector()
Метод detectAndDecode()
Використовується для роботи з одним QR-кодом.
data, points, straight_qrcode = detector.detectAndDecode(image)
Повертає:
data — розпізнаний текст;
points — координати кутів QR-коду;
straight_qrcode — вирівняне чорно-біле зображення QR-коду після перспективного перетворення.
Метод detectAndDecodeMulti()
Використовується для одночасного знаходження кількох QR-кодів.
retval, decoded_info, points, straight_qrcode = detector.detectAndDecodeMulti(image)
Повертає:
retval — логічну змінну успішності пошуку;
decoded_info — список рядків, отриманих із QR-кодів;
points — координати вершин кожного знайденого QR-коду;
straight_qrcode — вирівняне чорно-біле зображення QR-коду після перспективного перетворення.
Приклад результату:
retval = True
decoded_info = [
'{"Last Name":"Іваненко"}',
'{"your_seed":123456}'
]
Якщо QR-код не вдалося розпізнати, відповідний елемент списку може містити порожній рядок.
Області інтересу (ROI)
У складних випадках алгоритм автоматичного пошуку може не знайти всі QR-коди на зображенні. Це може статися через малий розмір QR-коду, сильний поворот, шумний фон або наявність декількох QR-кодів поруч.
У таких ситуаціях доцільно виконати виділення області інтересу, найпростішим рішенням у такій ситуації є ручна обрізка індексацією пікселів:
roi = image[y1:y2, x1:x2]
ROI рекомендовано використовувати, якщо розміщення QR-коду наперед відоме або камера охоплює забагато шумної області.
Три різні результати розпізнавання
Розпізнавання QR у виробничому сценарії має три різні результати, які не можна плутати:
| Результат | Що означає | Чи достатньо для маршруту |
|---|---|---|
| Код знайдено | детектор повернув координати вершин | ні |
| Код декодовано | отримано непорожній рядок | ні |
| Навантаження валідне | рядок відповідає схемі merc-i5.workpiece.v1 | так |
Порожній рядок, довільний текст або посилання на сайт — це успішне декодування, але невалідне навантаження. Маршрут виробу призначається лише після третього кроку.
Формат навантаження MERC-I5
QR містить UTF-8 JSON:
{"schema":"merc-i5.workpiece.v1","id":"BARREL-0001","type":"barrel","batch":"LAB-2026-01"}
| Правило | Значення |
|---|---|
| Обов'язкові поля | schema, id, type |
| Необов'язкове поле | batch |
Шаблон id | ^[A-Z][A-Z0-9_-]{2,31}$ |
| Додаткові поля | відхиляються |
| Максимальний розмір | 512 байтів |
Повторне сканування того самого id з тим самим нормалізованим вмістом ідемпотентне: новий виріб не створюється, але подія журналюється. Той самий id з іншим type або batch переводить цикл у ERROR. Повний опис — 14-qr-and-smart-warehouse.md.
3. Камери комплексу
У комплексі дві камери (ADR-0012):
| Камера | Підключення | Роль |
|---|---|---|
| IMX708 | шлейф CSI | основна для CV: висока роздільна здатність і швидкість |
| USB-камера | USB | допоміжна; використовується для QR і для навички роботи з камерами інших інтерфейсів |
[!WARNING] [ПОТРЕБУЄ ДОПРАЦЮВАННЯ: ЗЧИТУВАННЯ QR КАМЕРОЮ КОМПЛЕКСУ] Не підтверджено: фактичні параметри USB-камери (роздільна здатність, фокусна відстань, мінімальна дистанція фокусування); координати контрольної позиції, у якій робот утримує бочку перед камерою; освітлення сцени; мінімальний фізичний розмір модуля QR, що надійно читається з цієї дистанції; частка успішних зчитувань на 10 бочках палети. Чому це важливо: без цих даних неможливо визначити, чи взагалі придатні друковані QR-коди на бочках для автоматичного сценарію ЛР-22, і який тайм-аут задавати на спробу зчитування. Що потрібно додати: фото сцени зчитування; параметри камери з
v4l2-ctl --list-formats-ext; координати контрольної позиції; протокол із 10 спроб для кожної бочки з часом зчитування; фото зразкового та проблемного кадру. Як завершити: провести серію зчитувань на комплексі під наглядом оператора, внести результати в таблицю вимірювань цієї роботи та в паспорт комплексу, після чого замінити цей блок.
Виконання лабораторної роботи
Крок 1. Отримання індивідуального варіанту
Завантажте бібліотеку CV_library із вказаного репозиторію. Імпортуйте решту необхідних бібліотек. Створіть текстовий ключ key_word за правилами викладача та отримайте варіант завдання за допомогою функції CV_library.get_variant(). Не публікуйте ключ разом зі звітом, якщо він містить персональні дані. Після виконання функції буде сформовано словник із зображеннями:
-
image0 — зображення №1 із 1 QR-кодом;
-
image1 — зображення №2 із 2 QR-кодами;
-
image2 — зображення №3 із 4 QR-кодами;
Відобразіть отримані зображення.
У Google Colab виконайте:
!git clone https://github.com/Chiptrees/first_labs
!pip install -r first_labs/requirements.txt
import json
import cv2
import matplotlib.pyplot as plt
import numpy as np
from first_labs import CV_library
key_word = "ВКАЖІТЬ_КЛЮЧ_ЗА_ПРАВИЛАМИ_ВИКЛАДАЧА"
var = CV_library.get_variant(3, key_word)
Рис. 1. Вхідні зображення індивідуального варіанта
Очікуваний результат: словник var містить ключі image0, image1 та image2, а три зображення відображаються без помилок.
Критерій правильності: кількість видимих QR-кодів на зображеннях дорівнює одному, двом і чотирьом відповідно.
Якщо результат не отримано: перевірте повідомлення встановлення залежностей, правильність ключа та наявність усіх трьох ключів у словнику; не переходьте до декодування пошкодженого або відсутнього зображення.
Крок 2. Читання QR-кодів із зображення №1
image = var["image0"]
qrd = cv2.QRCodeDetector()
decoded_info, qr_points, straight_qrcodes = qrd.detectAndDecode(image)
print(decoded_info)
Рис. 2. Результат розпізнавання одного QR-коду
Приклад декодованого вмісту наведено у виводі комірки Colab.
Очікуваний результат: decoded_info містить непорожній рядок, а qr_points — координати чотирьох вершин QR-коду.
Критерій правильності: код декодовано один раз, його контур відповідає видимим межам QR-коду.
Якщо результат не отримано: виконайте обрізання ROI навколо коду та повторіть detectAndDecode().
Крок 3. Читання QR-кодів із зображення №2
image = var["image1"]
qrd = cv2.QRCodeDetector()
retval, decoded_info_list, qr_points_list, straight_qrcodes_list = (
qrd.detectAndDecodeMulti(image)
)
print(retval, decoded_info_list)
Рис. 3. Результат автоматичного розпізнавання двох QR-кодів
Якщо один або обидва QR-коди не розпізналися, використайте ROI та повторіть виконання коду
image = var["image1"]
roi = image[300:, 200:400]
Рис. 4. Повторне розпізнавання QR-коду в області інтересу
Очікуваний результат: отримано два непорожні декодовані значення автоматично або після обробки ROI.
Критерій правильності: кожен із двох видимих кодів має окреме значення та коректний контур; порожні рядки не враховуються як успішне розпізнавання.
Якщо результат не отримано: змініть межі ROI так, щоб код потрапив до області повністю разом із білим полем, і повторіть декодування.
Крок 4. Розбиття зображення на області очікування та зчитування чотирьох QR-кодів
image = var["image2"]
images = [
image[150:350, 150:350],
image[0:150, 400:600],
image[200:400, 450:],
image[320:, 320:500],
]
Рис. 5. Вхідне зображення для поділу на області інтересу
Рис. 6. Результати обробки чотирьох областей інтересу
Рис. 7. Декодовані значення QR-кодів
Очікуваний результат: сформовано чотири ROI та отримано чотири декодовані значення.
Критерій правильності: кожна ROI містить один повний QR-код, усі чотири рядки непорожні, а дублікати виводу відсутні.
Якщо результат не отримано: перевірте межі кожної ROI окремо та збільште область навколо нерозпізнаного коду.
Крок 5. Валідація навантаження за схемою MERC-I5
Декодований рядок ще не є ідентифікатором виробу. Реалізуйте функцію валідації, яка приймає рядок і повертає або перевірений запис, або причину відмови.
import json
import re
SCHEMA = "merc-i5.workpiece.v1"
ID_RE = re.compile(r"^[A-Z][A-Z0-9_-]{2,31}$")
REQUIRED = {"schema", "id", "type"}
ALLOWED = REQUIRED | {"batch"}
MAX_BYTES = 512
def validate_payload(raw: str) -> dict:
"""Повертає {'ok': True, 'record': {...}} або {'ok': False, 'reason': '...'}."""
if not raw:
return {"ok": False, "reason": "EMPTY"}
if len(raw.encode("utf-8")) > MAX_BYTES:
return {"ok": False, "reason": "TOO_LARGE"}
try:
data = json.loads(raw)
except json.JSONDecodeError:
return {"ok": False, "reason": "NOT_JSON"}
if not isinstance(data, dict):
return {"ok": False, "reason": "NOT_OBJECT"}
if data.get("schema") != SCHEMA:
return {"ok": False, "reason": "UNKNOWN_SCHEMA"}
missing = REQUIRED - data.keys()
if missing:
return {"ok": False, "reason": f"MISSING:{sorted(missing)}"}
extra = data.keys() - ALLOWED
if extra:
return {"ok": False, "reason": f"EXTRA:{sorted(extra)}"}
if not ID_RE.match(str(data["id"])):
return {"ok": False, "reason": "BAD_ID"}
return {"ok": True, "record": data}
Перевірте функцію на прикладах із data/qr/: усі рядки valid.jsonl мають дати ok=True, усі рядки invalid.jsonl — ok=False із конкретною причиною.
Очікуваний результат: для кожного декодованого коду отримано або перевірений запис, або однозначну причину відмови.
Критерій правильності: жоден рядок із invalid.jsonl не проходить валідацію; жоден рядок із valid.jsonl не відхиляється; причина відмови конкретна, а не загальна.
Якщо результат не отримано: не послаблюйте перевірку, щоб «пройшли всі» — з'ясуйте, яке саме правило порушує рядок, і зафіксуйте це у звіті.
Крок 6. Обробка повторного та суперечливого коду
Змоделюйте реєстр уже побачених виробів у пам'яті й перевірте три випадки з duplicate-conflict.jsonl:
| Випадок | Очікувана поведінка |
|---|---|
той самий id, той самий вміст | ідемпотентно: новий виріб не створюється, подія журналюється |
той самий id, інший type або batch | цикл переходить у ERROR, потрібне рішення оператора |
новий id | створюється запис виробу |
Очікуваний результат: три різні гілки поведінки, кожна з окремою подією в журналі.
Критерій правильності: суперечливий код ніколи не переписує наявний запис мовчки.
Якщо результат не отримано: перевірте, що порівнюється нормалізований вміст, а не сирий рядок із можливими відмінностями пробілів чи порядку ключів.
Сценарії перевірки
- Позитивний: один чіткий QR-код декодується методом
detectAndDecode(), повертає чотири вершини, а навантаження проходить валідацію. - Негативний: для зображення без QR-коду або з неповною ROI програма повертає порожній результат і не оголошує розпізнавання успішним; довільний текст у коді відхиляється з причиною
UNKNOWN_SCHEMAабоNOT_JSON. - Граничний: кілька кодів обробляються окремо; порожні, повторні та суперечливі значення фіксуються у звіті; навантаження понад 512 байтів відхиляється.
Таблиці вимірювань і метрики
Розпізнавання
| № зображення | Видимих кодів | Розпізнано автоматично | Розпізнано після ROI | Межі ROI | Валідних навантажень |
|---|---|---|---|---|---|
| 1 | 1 | ||||
| 2 | 2 | ||||
| 3 | 4 |
Валідація
| Джерело | Рядків | ok=True | ok=False | Причини відмов |
|---|---|---|---|---|
valid.jsonl | ||||
invalid.jsonl | ||||
duplicate-conflict.jsonl |
Метрики: 100% кодів з valid.jsonl прийнято; 0 рядків з invalid.jsonl прийнято; 0 випадків мовчазного перезапису запису суперечливим кодом.
Вимоги до звіту
Виконайте загальні вимоги до звіту. Додатково наведіть обидві таблиці вимірювань, код функції валідації та опис того, як ваша реалізація поводиться з невалідним кодом у контексті сценарію розумного складу. Декодований вміст наводьте без зайвих персональних даних.
Критерії оцінювання
Застосовується базова рубрика: підготовка — 10%, реалізація — 40%, перевірка — 25%, аналіз — 15%, звіт — 10%.
Контрольні питання
- Чим
detectAndDecode()відрізняється відdetectAndDecodeMulti()? - За яких умов використання ROI покращує розпізнавання?
- Чому порожній рядок не можна вважати успішним декодуванням?
- Як обробляти повторний або пошкоджений QR-код у виробничому сценарії?
Висновки
Напишіть висновки щодо виконаної роботи.
Посилання на лабораторну роботу №10 та приклад тут: