ifURI examples
← all examples

43 - camera + USB + OCR inspection loop

This example shows the URI shape for a physical scan station:

usb:// camera discovery
  -> camera:// pre-scan beep + photo capture
  -> camera:// object detection/crop/inspection
  -> ocr:// text extraction when richer OCR is needed
  -> alert/log URI when rules fail

The important operational detail is the audible pre-scan cue. A flow sets beep=true before camera://host/photo/... capture so the person near the camera knows exactly when a photo is taken. Set beep_required=true when the flow must refuse capture if the audio cue cannot be emitted.

Connector roles

which /dev/video* node it exposes.

detect/crop the main region, OCR it.

structured alerts; persists a verdict (inspection.json sidecar + optional JSONL audit_log) so alerting has a durable trail without a separate log:// connector.

reference vs a fresh frame, or two frames captured interval_ms apart — returning changed, changeRatio and the changed region, with beep_on_change/fail_on_change.

{type, data, rect} — with required + fail_if_missing to assert an expected code.

captured image or crop.

Run the whole thing

python3 run.py            # synthetic receipt → crop+deskew+OCR, barcode decode (works offline)
python3 run.py --camera   # capture from a real /dev/video* if one is attached
python3 run.py --serve 8780   # also host the mobile webcam service and print the phone URL
python3 run.py --json     # machine-readable summary

run.py drives the connectors in-process: discover the camera (usb://), capture or synthesise a frame, crop tightly to the receipt and deskew it before OCR (camera://), decode any QR/barcode, and optionally start the LAN mobile service (webcam://).

Flows

audit log (no log:// connector needed).

what appeared, recording an audit record.

expected code.

— crop tightly to the sheet, flatten it, OCR it, audit the verdict.

currency, date, NIP) for the invoice/KSeF flow.

— derive net/VAT/gross at 23% and emit a KSeF-ready invoice draft.

camera scans into the same pipeline (with a crop-target selector); review the mobile captures.

Live run

The live script is intentionally explicit because it touches real hardware:

cd /home/tom/github/if-uri/examples/43-camera-usb-ocr-inspection
NODE=lenovo \
NODE_URL=http://192.168.188.201:8765 \
IDENTITY=~/.ssh/id_ed25519 \
./run_live_probe.sh

It ensures usb, camera and ocr are live on the node, probes devices, then runs an inspection with beep=true. If no /dev/video* camera exists, discovery still works and the capture step reports a clean URI error.

Lenovo validation

Validated on lenovo / node name laptop on 2026-06-23:

with /dev/video0../dev/video3.

via ffmpeg, and writes a 1280x720 JPEG.

inspection.json.

img2nl). Install one of those on the node, or use the host-compute pattern from SCENARIOS.md when OCR should run on a stronger machine.

Scenarios

See SCENARIOS.md for concrete office/manufacturing examples.

Files

README.mdSCENARIOS.mdcamera-alert.flow.yamlcamera-barcode-scan.flow.yamlcamera-mobile-web.flow.yamlcamera-motion-scan.flow.yamlcamera-receipt-parse.flow.yamlcamera-receipt-scan.flow.yamlcamera-usb-ocr.flow.yamlledger.pyreceipt-to-invoice.flow.yamlrun.pyrun_live_probe.shtest_camera_usb_ocr_example.py

View on GitHub →