CodeRigor

GUIDED LESSON / 4 OF 6

Write local events and release the camera

Connect capture, decoding and the tested policy with explicit file ownership.

What moves through this step
  1. 01Frame read
  2. 02QR decode
  3. 03Journal policy
  4. 04Append + flush

Put the boundary code in one place

The adapter opens one image or one camera. It appends accepted events to a local JSON-lines file, flushes after each event and releases the camera on exit. It does not save frames, upload images, launch a browser or execute the contents of a marker.

Python
"""Use a V4L2/OpenCV camera or a local image; never follow decoded URLs."""
import argparse
from datetime import datetime, timezone
import json
from pathlib import Path
import time
from journal import Journal


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--device", type=int, default=0)
    parser.add_argument("--image", type=Path)
    parser.add_argument("--output", type=Path, default=Path("events.jsonl"))
    args = parser.parse_args()
    import cv2  # Install python3-opencv using your OS package manager.
    detector, journal = cv2.QRCodeDetector(), Journal()
    camera = None
    try:
        if not args.image:
            camera = cv2.VideoCapture(args.device)
            if not camera.isOpened():
                raise RuntimeError("Camera unavailable; verify its vendor example and device index first")
        with args.output.open("a", encoding="utf-8") as log:
            while True:
                if args.image:
                    frame = cv2.imread(str(args.image))
                    ok = frame is not None
                else:
                    ok, frame = camera.read()
                if not ok:
                    raise RuntimeError("No image/frame; no event was recorded")
                payload, _, _ = detector.detectAndDecode(frame)
                event = journal.accept(payload, time.monotonic())
                if event:
                    event["at_utc"] = datetime.now(timezone.utc).isoformat()
                    log.write(json.dumps(event, ensure_ascii=True) + "\n")
                    log.flush()
                    print(event["label"])
                if args.image:
                    break
                time.sleep(0.05)
    except KeyboardInterrupt:
        pass
    finally:
        if camera:
            camera.release()


if __name__ == "__main__":
    main()

Append mode preserves earlier events. Flush makes Python's buffered data available to the operating system; it is not a crash-durability guarantee. If you need durable publication later, continue into the free Unix file and filesystem lessons.

Observe the output

Run the program, present CRIGOR:BOX_A, hold it still, then wait longer than five seconds and present it again. Inspect the file with a text editor. Each line should be a complete JSON object. Stop with Ctrl+C and confirm another camera program can open the device afterward.

The elapsed value comes from a monotonic clock and is not seconds since the start of this particular program. Use differences within a run. at_utc is the human-readable timestamp.

Try it

Point --output at a directory you cannot write to. The program should report the filesystem error rather than quietly claiming a saved event. Use a temporary trial file when experimenting and keep personal locations out of marker labels.