The repository is dora-plot, but the package, the entrypoint, the manifest name and the window title were all opencv-plot. Consumers wire the node by `path:`, so the split name was visible in every dataflow. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018WLVZCFFcCH1EpTXGfS9aD
4.9 KiB
AGENTS.md
This file provides guidance to AI coding assistants when working with code in this repository.
What this is
A dora-rs Python node that displays a video stream with overlays: it keeps a current frame, bbox set and text string, and every image event redraws all three into an OpenCV window. It is a display sink — it produces no outputs.
Based on opencv-plot from dora-hub, brought up to the dora 1.0 node contract and packaged with uv.
It is Python on purpose. The node was first written in Rust against the opencv crate; that crate generates its bindings from the OpenCV headers installed on the build machine, so it needs libopencv-dev and libclang-dev on every machine and every architecture and does not cross-compile. Every dependency here ships prebuilt wheels for linux-x86_64, linux-aarch64 and macos-arm64, so uv sync is the whole install. Do not reintroduce a dependency that has to be compiled from source.
Build
uv sync # runtime deps
uv sync --group dev # plus pytest and ruff
dora build --uv / dora run --uv rewrites the manifest's build: pip install . to uv pip install . and gives the node its own managed venv under .dora/python-envs/<node-id>/, so what it imports at runtime is what was installed at build time.
Requirements: dora 1.0+ (verified with dora-cli 1.0.1), Python 3.11+ (dora-rs 1.0.1 requires it), uv, and a display — or CI=true.
Run
dora run dataflow.yml --uv
Test
uv run pytest # 14 tests, no camera/dataflow/display needed
uv run ruff check .
Architecture
dora_plot/main.py— Argument parsing and the dora event loop, nothing else.imagedecodes and redraws;bboxandtextonly store state. The env var wins over the CLI flag forPLOT_WIDTH/PLOT_HEIGHT, matching the upstream node.Node(args.name)rather thanNode()so the node also runs standalone as a dynamic node.dora_plot/decode.py—to_bgr(): every supported encoding to a writable BGR array.width/heightare only consulted for the raw encodings (bgr8,rgb8,yuv420); the rest carry their own dimensions, so a missing dimension is an error only where it actually matters. Two behaviours are deliberate, not bugs:bgr8copies (the payload may be a read-only view on shared memory, and the frame is annotated in place), andyuv420converts withCOLOR_YUV420p2RGBrather than...2BGR— the upstream node converts to RGB and then displays it as BGR, and that channel swap is part of the behaviour callers already see.dora_plot/plot.py—Plot(the overlay state and the window) andBboxes.show()draws, resizes and displays, and returns whetherqwas pressed. Resizing only happens when bothPLOT_WIDTHandPLOT_HEIGHTare set.headless(fromCI=true) skips only the window: every input is still decoded, which is what makes the node safe in a pipeline test.dora_plot/bbox.py— Parses thebboxstruct into xyxy corners.xywhis centre-based (x, y are the box centre)._flat()unwraps the single-element list thatpa.array([{...}])produces around each field, and reads a field stored flat as-is.dora-node.yml— Node manifest (apiVersion: 1).dora validateinjects these types into any dataflow that reaches the node bypath:; agit:-sourced node does not pick it up, which is why the dataflow builds from the checkout.imageandtextcarry type URNs naming the element type (std/core/v1/UInt8,.../String) because each payload is a raw buffer described by its metadata, not an Arrow struct.bboxis deliberately untyped: senders disagree on whether its numeric fields are 32- or 64-bit, and the node reads either.dataflow.yml— Example dataflow: a webcam and a one-shot caption feeding the viewer, withstrict_types: true. Note theoutput_types:on the twogit:-sourced nodes — their own manifests are not injected, so without those annotations strict validation fails on an untyped upstream.bboxis left unwired on purpose; every bbox source is a detector, and wiring one would put a model download in front of a webcam preview..gitea/workflows/validate.yml— CI gate runningdora validateon the dataflow on push and pull request. Needs no camera and no display.
A caveat on typed optional inputs
dora validate --strict injects a contract type for every typed manifest input and then warns input_types key "..." not found in inputs list for any the dataflow does not wire. So a typed optional input forces the example dataflow to wire it. That is why dataflow.yml wires text from pyarrow-sender — dropping it would fail CI. If a typed input is added to the manifest, either wire it in dataflow.yml or leave it untyped.
Rules
- Do not mention AI tools in commit messages or documentation.