| """ |
| QR code detection + decoding provider. |
| |
| Uses OpenCV's built-in cv2.QRCodeDetector — no external deps. |
| Detects and decodes QR codes, returning the decoded text + bounding box. |
| |
| Pure OpenCV — no model downloads, no external services. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import cv2 |
| import numpy as np |
|
|
| from config.settings import Settings, settings as _default_settings |
| from cores.vision import to_gray |
| from pipeline.feature_extraction import PipelineOutput |
| from providers.base import BaseProvider, ProviderCapability |
|
|
|
|
| class QRCodeProvider(BaseProvider): |
| name = "qr_code" |
| capability = ProviderCapability.OBJECT_DETECTION |
|
|
| def __init__(self, settings: Settings | None = None) -> None: |
| super().__init__(settings=settings or _default_settings) |
| self._detector = cv2.QRCodeDetector() |
|
|
| def is_available(self) -> bool: |
| return hasattr(cv2, "QRCodeDetector") |
|
|
| def _run(self, pipeline_output: PipelineOutput) -> tuple[dict, dict]: |
| img: np.ndarray = pipeline_output.image |
|
|
| |
| |
| try: |
| data, points, _ = self._detector.detectAndDecode(img) |
| except Exception as e: |
| return {"error": str(e)}, {"objects": [], "model": "opencv_qr"} |
|
|
| objects: list[dict] = [] |
| if points is not None and len(points) > 0: |
| |
| pts = points.reshape(-1, 2) |
| x1, y1 = int(pts[:, 0].min()), int(pts[:, 1].min()) |
| x2, y2 = int(pts[:, 0].max()), int(pts[:, 1].max()) |
| objects.append({ |
| "label": "qr_code", |
| "confidence": 1.0, |
| "box": {"x": x1, "y": y1, "w": x2 - x1, "h": y2 - y1}, |
| "decoded_text": data if data else "", |
| "is_qr_code": True, |
| }) |
|
|
| raw = { |
| "num_objects": len(objects), |
| "model": "opencv_qr", |
| "decoded": bool(data), |
| } |
| normalized = { |
| "objects": objects, |
| "model": "opencv_qr", |
| } |
| return raw, normalized |
|
|