#!/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()