91 lines
2.5 KiB
Python
91 lines
2.5 KiB
Python
#!/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()
|