add opencv-plot node for dora dataflows #2

Merged
cristhian merged 4 commits from add-opencv-plot-node into main 2026-09-11 10:34:36 +00:00
Owner

Summary

Fills the empty repository with opencv-plot: a dora display sink that overlays bounding boxes and text on an image input and shows it in an OpenCV window. Based on opencv-plot from dora-hub, brought up to the dora 1.0 node contract and packaged with uv.

Feature parity with the upstream node: all eight image encodings, both bbox formats, the text overlay, PLOT_WIDTH/PLOT_HEIGHT, and headless operation under CI=true.

Related Issues

Closes #1

Changes

  • opencv_plot/ — the node, split into the dora event loop (main.py), decoding (decode.py), overlay state and window (plot.py) and bbox parsing (bbox.py)
  • dora-node.yml — dora 1.0 node manifest, so a consumer's dataflow fails validation on a wiring mistake rather than at runtime
  • dataflow.yml — runnable example: a webcam and a one-shot caption feeding the viewer, under strict_types: true
  • pyproject.toml + uv.lock — uv packaging, locked
  • tests/ — 14 tests needing no camera, dataflow or display
  • .gitea/workflows/validate.yml — CI: dora validate on the dataflow, plus lint and tests
  • README.md / AGENTS.md — usage and the reasoning behind the design decisions below

Why Python and not Rust

The node was first written in Rust against the opencv crate. That crate generates its bindings from the OpenCV headers found on the build machine, so it needs libopencv-dev and libclang-dev installed per machine and per architecture, and it does not cross-compile — which fails the requirement that these nodes run on the Jetson (aarch64) and on x86_64 without a custom install on each. Every dependency here ships prebuilt wheels for linux-x86_64, linux-aarch64 and macos-arm64, so uv sync is the whole install. AVIF comes from pillow-avif-plugin rather than OpenCV, which cannot decode it before 4.10.

Three decisions worth a look

  • bbox is untyped in the manifest, deliberately: senders disagree on whether its numeric fields are 32- or 64-bit, and the node reads either.
  • dataflow.yml wires a caption into text from pyarrow-sender. Not decoration — dora validate --strict injects a contract type for every typed manifest input and then fails on any the dataflow does not wire, so a typed optional input forces the example to wire it. Noted in AGENTS.md so it does not get tidied away later.
  • bbox is left unwired in the example, because every bbox source is a detector and wiring one would put a model download in front of a webcam preview. README.md shows the dora-yolo wiring instead.

Validation

Run on the Jetson (aarch64, Python 3.12.3, dora-cli 1.0.1):

  • uv sync --locked --group dev — 14 packages, all prebuilt aarch64 wheels, no compiler and no apt package
  • uv run pytest — 14 passed
  • uv run ruff check . — clean
  • dora validate dataflow.yml — "All type annotations OK." under strict_types: true
  • All eight encodings round-tripped against real encoded data (bgr8, rgb8, png, jpeg, bmp, webp, yuv420, avif) — these are tests, not a one-off script
  • uv run opencv-plot --help — console-script entrypoint resolves
  • Not verified: a live dataflow run. This machine has no camera and no DISPLAY, so dora run dataflow.yml --uv and the window / q-key path are untested. Worth one run on a machine with a webcam and a display before tagging v0.1.0.
  • Acceptance criteria from #1 are satisfied, except the live-run caveat above
# Summary Fills the empty repository with `opencv-plot`: a dora display sink that overlays bounding boxes and text on an image input and shows it in an OpenCV window. Based on [`opencv-plot`](https://github.com/dora-rs/dora-hub/tree/main/node-hub/opencv-plot) from dora-hub, brought up to the dora 1.0 node contract and packaged with `uv`. Feature parity with the upstream node: all eight image encodings, both bbox formats, the text overlay, `PLOT_WIDTH`/`PLOT_HEIGHT`, and headless operation under `CI=true`. ## Related Issues Closes #1 ## Changes - `opencv_plot/` — the node, split into the dora event loop (`main.py`), decoding (`decode.py`), overlay state and window (`plot.py`) and bbox parsing (`bbox.py`) - `dora-node.yml` — dora 1.0 node manifest, so a consumer's dataflow fails validation on a wiring mistake rather than at runtime - `dataflow.yml` — runnable example: a webcam and a one-shot caption feeding the viewer, under `strict_types: true` - `pyproject.toml` + `uv.lock` — `uv` packaging, locked - `tests/` — 14 tests needing no camera, dataflow or display - `.gitea/workflows/validate.yml` — CI: `dora validate` on the dataflow, plus lint and tests - `README.md` / `AGENTS.md` — usage and the reasoning behind the design decisions below ## Why Python and not Rust The node was first written in Rust against the `opencv` crate. That crate generates its bindings from the OpenCV headers found on the build machine, so it needs `libopencv-dev` and `libclang-dev` installed **per machine and per architecture**, and it does not cross-compile — which fails the requirement that these nodes run on the Jetson (aarch64) and on x86_64 without a custom install on each. Every dependency here ships prebuilt wheels for linux-x86_64, linux-aarch64 and macos-arm64, so `uv sync` is the whole install. AVIF comes from `pillow-avif-plugin` rather than OpenCV, which cannot decode it before 4.10. ## Three decisions worth a look - **`bbox` is untyped in the manifest**, deliberately: senders disagree on whether its numeric fields are 32- or 64-bit, and the node reads either. - **`dataflow.yml` wires a caption into `text` from `pyarrow-sender`.** Not decoration — `dora validate --strict` injects a contract type for every *typed* manifest input and then fails on any the dataflow does not wire, so a typed optional input forces the example to wire it. Noted in `AGENTS.md` so it does not get tidied away later. - **`bbox` is left unwired in the example**, because every bbox source is a detector and wiring one would put a model download in front of a webcam preview. `README.md` shows the `dora-yolo` wiring instead. ## Validation Run on the Jetson (aarch64, Python 3.12.3, `dora-cli 1.0.1`): - [x] `uv sync --locked --group dev` — 14 packages, all prebuilt aarch64 wheels, no compiler and no apt package - [x] `uv run pytest` — 14 passed - [x] `uv run ruff check .` — clean - [x] `dora validate dataflow.yml` — "All type annotations OK." under `strict_types: true` - [x] All eight encodings round-tripped against real encoded data (`bgr8`, `rgb8`, `png`, `jpeg`, `bmp`, `webp`, `yuv420`, `avif`) — these are tests, not a one-off script - [x] `uv run opencv-plot --help` — console-script entrypoint resolves - [ ] **Not verified: a live dataflow run.** This machine has no camera and no `DISPLAY`, so `dora run dataflow.yml --uv` and the window / `q`-key path are untested. Worth one run on a machine with a webcam and a display before tagging `v0.1.0`. - [ ] Acceptance criteria from #1 are satisfied, except the live-run caveat above
cristhian added the feature label 2026-09-10 21:56:26 +00:00
cristhian added 1 commit 2026-09-10 21:56:27 +00:00
add opencv-plot node for dora dataflows
validate / validate (push) Successful in 58m52s
validate / test (push) Failing after 58m59s
validate / validate (pull_request) Successful in 58m50s
validate / test (pull_request) Failing after 58m53s
43cb1ff01c
A display sink that overlays bounding boxes and text on an image input and
shows it in an OpenCV window. Based on opencv-plot from dora-hub, brought up
to the dora 1.0 node contract and packaged with uv.

Python rather than Rust: the opencv Rust crate generates its bindings from the
OpenCV headers on the build machine, so it needs libopencv-dev and libclang-dev
per machine and per architecture and does not cross-compile. Every dependency
here ships prebuilt wheels for linux-x86_64, linux-aarch64 and macos-arm64.

Covers every encoding the upstream node did — bgr8, rgb8, jpeg/jpg/jpe/bmp/
webp/png, yuv420 and avif — plus both bbox formats, the text overlay,
PLOT_WIDTH/PLOT_HEIGHT and headless operation under CI=true.
Author
Owner

/bot review

/bot review
bot reviewed 2026-09-10 23:03:14 +00:00
bot left a comment
Owner

Summary

Adds a dora 1.0 OpenCV display sink (opencv_plot) with typed manifest, example dataflow, uv packaging, 14 tests and a validate/test CI gate. The implementation is consistent with its own documented contract and the decode/bbox paths hold up against real pyarrow payloads; one packaging gap is worth fixing before merge.

Findings

pyproject.toml:14 -- pyarrow is a hard requirement of this node's core path (tests/test_opencv_plot.py:107 imports it directly, and bbox._flat only works on pyarrow ListScalar/Array objects), yet it appears in neither dependencies nor the dev group; it is present only because dora-rs 1.0.1 happens to pull it in transitively. If a dora-rs bump ever changes that, the test suite and the bbox input path fail with a confusing ModuleNotFoundError, and uv sync --locked cannot catch it since the lockfile hides the omission. Declare pyarrow explicitly (runtime deps, or at minimum the dev group for the test import).

Recommendation

Request changes -- declare the dependency the node and its tests import directly; everything else is sound.

## Summary Adds a dora 1.0 OpenCV display sink (`opencv_plot`) with typed manifest, example dataflow, uv packaging, 14 tests and a validate/test CI gate. The implementation is consistent with its own documented contract and the decode/bbox paths hold up against real pyarrow payloads; one packaging gap is worth fixing before merge. ## Findings **`pyproject.toml:14`** -- `pyarrow` is a hard requirement of this node's core path (`tests/test_opencv_plot.py:107` imports it directly, and `bbox._flat` only works on pyarrow `ListScalar`/`Array` objects), yet it appears in neither `dependencies` nor the dev group; it is present only because `dora-rs` 1.0.1 happens to pull it in transitively. If a dora-rs bump ever changes that, the test suite and the `bbox` input path fail with a confusing `ModuleNotFoundError`, and `uv sync --locked` cannot catch it since the lockfile hides the omission. Declare `pyarrow` explicitly (runtime deps, or at minimum the dev group for the test import). ## Recommendation **Request changes** -- declare the dependency the node and its tests import directly; everything else is sound.
cristhian added 3 commits 2026-09-11 09:47:08 +00:00
The bbox input path and the tests import pyarrow directly; it was
present only because dora-rs pulls it in transitively.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JPUZks9z3Pi5oxw9GQ9znt
The opencv-python wheel links against libGL at import time, so pytest
failed at collection with ImportError: libGL.so.1 on the runner image.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JPUZks9z3Pi5oxw9GQ9znt
fix duplicate CI runs by scoping the push trigger to main
validate / validate (pull_request) Successful in 58m50s
validate / test (pull_request) Successful in 59m57s
702812c35f
A pull request ran the workflow twice on the same commit, once for the
branch push and once for the PR.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JPUZks9z3Pi5oxw9GQ9znt
Author
Owner

/bot review

/bot review
bot reviewed 2026-09-11 10:12:23 +00:00
bot left a comment
Owner

Summary

Full port of upstream opencv-plot to dora 1.0: event loop split into main.py / decode.py / plot.py / bbox.py, a dora-node.yml manifest wired into a strict-typed example dataflow, 14 tests, and a CI gate. I verified the port against upstream line-by-line and against primary sources: the bbox parsing chain (value[0] → fields → .values.to_numpy()) against pyarrow 25 on the test payload and upstream's identical access pattern, the CI asset URL and tarball layout against the dora v1.0.1 release assets, the git-node path:/build: idiom against the dora 1.0 descriptor docs (clone root is the cwd for both, bare path: resolves as the console script — matching dora-hub's own usage), and the DATA: "'dora-plot'" example against the pyarrow-sender contract (scalar → 1-element array → value[0].as_py()). CI run 384 on this head is green.

Findings

None blocking. Deliberate deviations from upstream are stricter than upstream (length-checked raw decodes instead of silent reshape failure), documented as such (decode.py yuv channel-swap note, bgr8 copy for shared-memory payloads), or parity-preserving. The untested area is the dora event loop itself, which needs a runtime and is honestly flagged.

Recommendation

Approve.

## Summary Full port of upstream `opencv-plot` to dora 1.0: event loop split into `main.py` / `decode.py` / `plot.py` / `bbox.py`, a `dora-node.yml` manifest wired into a strict-typed example dataflow, 14 tests, and a CI gate. I verified the port against upstream line-by-line and against primary sources: the bbox parsing chain (`value[0]` → fields → `.values.to_numpy()`) against pyarrow 25 on the test payload and upstream's identical access pattern, the CI asset URL and tarball layout against the dora v1.0.1 release assets, the git-node `path:`/`build:` idiom against the dora 1.0 descriptor docs (clone root is the cwd for both, bare `path:` resolves as the console script — matching dora-hub's own usage), and the `DATA: "'dora-plot'"` example against the pyarrow-sender contract (scalar → 1-element array → `value[0].as_py()`). CI run 384 on this head is green. ## Findings None blocking. Deliberate deviations from upstream are stricter than upstream (length-checked raw decodes instead of silent reshape failure), documented as such (`decode.py` yuv channel-swap note, bgr8 copy for shared-memory payloads), or parity-preserving. The untested area is the dora event loop itself, which needs a runtime and is honestly flagged. ## Recommendation Approve.
cristhian merged commit d5d3026b92 into main 2026-09-11 10:34:36 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: liria/dora-image-view#2