From a62f0c905473111ad01070a018e52a49afb6837b Mon Sep 17 00:00:00 2001 From: Mephimeow Date: Wed, 29 Apr 2026 09:22:25 +0000 Subject: [PATCH] pizda bochku --- backend/cmd/scanner.md | 42 ++++++++++++++++++++ backend/cmd/scanner.py | 90 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 backend/cmd/scanner.md create mode 100644 backend/cmd/scanner.py diff --git a/backend/cmd/scanner.md b/backend/cmd/scanner.md new file mode 100644 index 0000000..7d15e1f --- /dev/null +++ b/backend/cmd/scanner.md @@ -0,0 +1,42 @@ +# QR/Barcode Scanner + +Считыватель QR-кодов и штрихкодов на базе **pyzbar** + **OpenCV** для **Raspberry Pi Camera Module v3**. + +## Зависимости + +```bash +sudo apt install libzbar0 libgstreamer1.0-dev +pip install opencv-python pyzbar +``` + +## Запуск + +```bash +python scanner.py +``` + +## Поддерживаемые форматы + +- QR-код +- Code 128, Code 39, EAN-13, EAN-8 +- UPC-A, UPC-E +- Interleaved 2 of 5 +- И другие форматы, поддерживаемые `pyzbar` + +## Управление + +| Клавиша | Действие | +|---------|-----------------------------------| +| `q` | Выход | +| `r` | Сброс списка отсканированных кодов| + +## Как работает + +1. Подключение камеры через GStreamer (`libcamerasrc`) — родной путь для Camera Module v3 +2. Каждый кадр конвертируется в градации серого и передаётся в `pyzbar.decode()` +3. Найденные коды обводятся зелёной рамкой, данные выводятся в консоль и на экран +4. Дубликаты фильтруются — каждый уникальный код выводится один раз (до сброса) + +## Fallback + +Если GStreamer-пайплайн недоступен, скрипт автоматически пробует открыть камеру через `/dev/video0` (V4L2). diff --git a/backend/cmd/scanner.py b/backend/cmd/scanner.py new file mode 100644 index 0000000..a898d06 --- /dev/null +++ b/backend/cmd/scanner.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +"""QR/Barcode scanner using pyzbar + OpenCV for Raspberry Pi Camera Module v3.""" + +import cv2 +from pyzbar import pyzbar +import time + + +def main(): + # Camera Module v3 uses libcamera (not V4L2 by default) + # GStreamer pipeline for libcamera + gst_pipeline = ( + "libcamerasrc ! " + "video/x-raw, format=NV12, width=1280, height=960 ! " + "videoconvert ! " + "video/x-raw, format=BGR ! " + "appsink" + ) + + cap = cv2.VideoCapture(gst_pipeline, cv2.CAP_GSTREAMER) + + if not cap.isOpened(): + # Fallback: try V4L2 if libcamera pipeline fails + print("GStreamer pipeline failed, trying /dev/video0...") + cap = cv2.VideoCapture(0) + if not cap.isOpened(): + print("Error: Cannot open camera") + return + + cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 960) + + scanned_codes = set() + + print("Scanner running. Press 'q' to quit, 'r' to reset scanned codes.") + + try: + while True: + ret, frame = cap.read() + if not ret: + print("Failed to grab frame") + break + + gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + codes = pyzbar.decode(gray) + + for code in codes: + data = code.data.decode("utf-8") + code_type = code.type + + if data not in scanned_codes: + scanned_codes.add(data) + print(f"[{code_type}] {data} (total unique: {len(scanned_codes)})") + + # Draw bounding box + points = code.polygon + if len(points) == 4: + pts = [(p.x, p.y) for p in points] + for i in range(4): + cv2.line(frame, pts[i], pts[(i + 1) % 4], (0, 255, 0), 3) + + # Draw data label + cv2.putText( + frame, + data[:40], + (points[0].x, points[0].y - 10), + cv2.FONT_HERSHEY_SIMPLEX, + 0.5, + (0, 255, 0), + 2, + ) + + cv2.imshow("Scanner", frame) + + key = cv2.waitKey(1) & 0xFF + if key == ord("q"): + break + elif key == ord("r"): + scanned_codes.clear() + print("Scanned codes reset.") + + except KeyboardInterrupt: + pass + finally: + cap.release() + cv2.destroyAllWindows() + + +if __name__ == "__main__": + main()