MERCI SPACE
Навчальні матеріали

Лабораторна робота 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не потрібенРобота виконується з готовими зображеннями без руху обладнання

Необхідні знання та матеріали

Ризики та правила безпеки

Робота виконується з готовими зображеннями. Фізичний рух робота, конвеєрів і подавачів не потрібний і не дозволений у межах цієї роботи.

Заборонені дії

  • запускати рух будь-якого механізму комплексу;
  • вважати непорожній декодований рядок валідним ідентифікатором виробу без перевірки схеми;
  • записувати в базу даних результат, який не пройшов валідацію;
  • публікувати key_word разом зі звітом, якщо він містить персональні дані.

Безпечний стан у логіці розпізнавання

Якщо код не прочитано, прочитано частково або навантаження не пройшло валідацію — маршрут виробу не призначається, а цикл переходить у стан помилки й очікує рішення оператора. Автоматичне продовження руху заборонене.

Хід виконання роботи

  1. Опрацювати методичні вказівки.
  2. Отримати індивідуальний варіант завдання.
  3. Виконати автоматичне розпізнавання QR-кодів засобами OpenCV.
  4. У випадку неповного розпізнавання виділити області інтересу й повторити зчитування.
  5. Реалізувати валідацію декодованого навантаження за схемою merc-i5.workpiece.v1.
  6. Перевірити поведінку на невалідних і суперечливих навантаженнях.
  7. Зробити висновки щодо якості розпізнавання та впливу попередньої обробки.
  8. Оформити звіт і надіслати викладачу для перевірки.

Методичні вказівки й теоретичні відомості

1. Теоретичні відомості

QR-код (Quick Response Code) — це двовимірний матричний штрихкод, призначений для швидкого зберігання та зчитування інформації за допомогою комп'ютерних систем і мобільних пристроїв.

На відміну від лінійних штрихкодів, QR-код дозволяє зберігати значно більший обсяг інформації та містить механізми виправлення помилок, що забезпечують можливість розпізнавання навіть при частковому пошкодженні зображення.

QR-коди можуть містити текстову інформацію, числові дані, посилання на веб-ресурси, контактні дані, JSON-структури, службову інформацію промислових систем. У сучасних системах комп'ютерного зору QR-коди широко використовуються для маркування продукції, автоматичної ідентифікації об'єктів, контролю технологічних процесів, відстеження виробів на конвеєрі, передачі невеликих обсягів структурованих даних.

2. Програмна реалізація

Для роботи з QR-кодами у бібліотеці OpenCV використовується клас:

Python
cv2.QRCodeDetector()

Об'єкт даного класу виконує:

  • пошук QR-кодів на зображенні;

  • визначення положення QR-кодів;

  • декодування інформації, що міститься в кодах.

Створення об'єкта:

Python
detector = cv2.QRCodeDetector()

Метод detectAndDecode()

Використовується для роботи з одним QR-кодом.

Python
data, points, straight_qrcode = detector.detectAndDecode(image)

Повертає:

data — розпізнаний текст;

points — координати кутів QR-коду;

straight_qrcode — вирівняне чорно-біле зображення QR-коду після перспективного перетворення.

Метод detectAndDecodeMulti()

Використовується для одночасного знаходження кількох QR-кодів.

Python
retval, decoded_info, points, straight_qrcode = detector.detectAndDecodeMulti(image)

Повертає:

retval — логічну змінну успішності пошуку;

decoded_info — список рядків, отриманих із QR-кодів;

points — координати вершин кожного знайденого QR-коду;

straight_qrcode — вирівняне чорно-біле зображення QR-коду після перспективного перетворення.

Приклад результату:

Text
retval = True

decoded_info = [
    '{"Last Name":"Іваненко"}',
    '{"your_seed":123456}'
]

Якщо QR-код не вдалося розпізнати, відповідний елемент списку може містити порожній рядок.

Області інтересу (ROI)

У складних випадках алгоритм автоматичного пошуку може не знайти всі QR-коди на зображенні. Це може статися через малий розмір QR-коду, сильний поворот, шумний фон або наявність декількох QR-кодів поруч.

У таких ситуаціях доцільно виконати виділення області інтересу, найпростішим рішенням у такій ситуації є ручна обрізка індексацією пікселів:

Python
roi = image[y1:y2, x1:x2]

ROI рекомендовано використовувати, якщо розміщення QR-коду наперед відоме або камера охоплює забагато шумної області.

Три різні результати розпізнавання

Розпізнавання QR у виробничому сценарії має три різні результати, які не можна плутати:

РезультатЩо означаєЧи достатньо для маршруту
Код знайденодетектор повернув координати вершинні
Код декодованоотримано непорожній рядокні
Навантаження валіднерядок відповідає схемі merc-i5.workpiece.v1так

Порожній рядок, довільний текст або посилання на сайт — це успішне декодування, але невалідне навантаження. Маршрут виробу призначається лише після третього кроку.

Формат навантаження MERC-I5

QR містить UTF-8 JSON:

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 виконайте:

Python
!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)
Три зображення індивідуального варіанта з одним, двома та чотирма QR-кодами

Рис. 1. Вхідні зображення індивідуального варіанта

Очікуваний результат: словник var містить ключі image0, image1 та image2, а три зображення відображаються без помилок.

Критерій правильності: кількість видимих QR-кодів на зображеннях дорівнює одному, двом і чотирьом відповідно.

Якщо результат не отримано: перевірте повідомлення встановлення залежностей, правильність ключа та наявність усіх трьох ключів у словнику; не переходьте до декодування пошкодженого або відсутнього зображення.

Крок 2. Читання QR-кодів із зображення №1

Python
image = var["image0"]
qrd = cv2.QRCodeDetector()
decoded_info, qr_points, straight_qrcodes = qrd.detectAndDecode(image)

print(decoded_info)
Зображення з одним QR-кодом і позначеними контурами

Рис. 2. Результат розпізнавання одного QR-коду

Text
Приклад декодованого вмісту наведено у виводі комірки Colab.

Очікуваний результат: decoded_info містить непорожній рядок, а qr_points — координати чотирьох вершин QR-коду.

Критерій правильності: код декодовано один раз, його контур відповідає видимим межам QR-коду.

Якщо результат не отримано: виконайте обрізання ROI навколо коду та повторіть detectAndDecode().

Крок 3. Читання QR-кодів із зображення №2

Python
image = var["image1"]
qrd = cv2.QRCodeDetector()
retval, decoded_info_list, qr_points_list, straight_qrcodes_list = (
    qrd.detectAndDecodeMulti(image)
)

print(retval, decoded_info_list)
Зображення з двома QR-кодами і позначеними контурами

Рис. 3. Результат автоматичного розпізнавання двох QR-кодів

Якщо один або обидва QR-коди не розпізналися, використайте ROI та повторіть виконання коду

Python
image = var["image1"]
roi = image[300:, 200:400]
Область інтересу з другим QR-кодом

Рис. 4. Повторне розпізнавання QR-коду в області інтересу

Очікуваний результат: отримано два непорожні декодовані значення автоматично або після обробки ROI.

Критерій правильності: кожен із двох видимих кодів має окреме значення та коректний контур; порожні рядки не враховуються як успішне розпізнавання.

Якщо результат не отримано: змініть межі ROI так, щоб код потрапив до області повністю разом із білим полем, і повторіть декодування.

Крок 4. Розбиття зображення на області очікування та зчитування чотирьох QR-кодів

Python
image = var["image2"]

images = [
    image[150:350, 150:350],
    image[0:150, 400:600],
    image[200:400, 450:],
    image[320:, 320:500],
]
Вхідне зображення з чотирма QR-кодами

Рис. 5. Вхідне зображення для поділу на області інтересу

Чотири області інтересу з результатами розпізнавання QR-кодів

Рис. 6. Результати обробки чотирьох областей інтересу

Приклад декодованих значень чотирьох QR-кодів

Рис. 7. Декодовані значення QR-кодів

Очікуваний результат: сформовано чотири ROI та отримано чотири декодовані значення.

Критерій правильності: кожна ROI містить один повний QR-код, усі чотири рядки непорожні, а дублікати виводу відсутні.

Якщо результат не отримано: перевірте межі кожної ROI окремо та збільште область навколо нерозпізнаного коду.

Крок 5. Валідація навантаження за схемою MERC-I5

Декодований рядок ще не є ідентифікатором виробу. Реалізуйте функцію валідації, яка приймає рядок і повертає або перевірений запис, або причину відмови.

Python
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.jsonlok=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Валідних навантажень
11
22
34

Валідація

ДжерелоРядківok=Trueok=FalseПричини відмов
valid.jsonl
invalid.jsonl
duplicate-conflict.jsonl

Метрики: 100% кодів з valid.jsonl прийнято; 0 рядків з invalid.jsonl прийнято; 0 випадків мовчазного перезапису запису суперечливим кодом.

Вимоги до звіту

Виконайте загальні вимоги до звіту. Додатково наведіть обидві таблиці вимірювань, код функції валідації та опис того, як ваша реалізація поводиться з невалідним кодом у контексті сценарію розумного складу. Декодований вміст наводьте без зайвих персональних даних.

Критерії оцінювання

Застосовується базова рубрика: підготовка — 10%, реалізація — 40%, перевірка — 25%, аналіз — 15%, звіт — 10%.

Контрольні питання

  1. Чим detectAndDecode() відрізняється від detectAndDecodeMulti()?
  2. За яких умов використання ROI покращує розпізнавання?
  3. Чому порожній рядок не можна вважати успішним декодуванням?
  4. Як обробляти повторний або пошкоджений QR-код у виробничому сценарії?

Висновки

Напишіть висновки щодо виконаної роботи.

Посилання на лабораторну роботу №10 та приклад тут:

MERCI SPACE