make the node standalone and rename it to dora-image-view #3

Merged
cristhian merged 5 commits from standalone-dora-plot into main 2026-09-15 09:33:12 +00:00
12 changed files with 57 additions and 57 deletions
Showing only changes of commit e2debe0581 - Show all commits
+4 -4
View File
@@ -36,10 +36,10 @@ uv run ruff check .
## Architecture
- **`opencv_plot/main.py`** — Argument parsing and the dora event loop, nothing else. `image` decodes and redraws; `bbox` and `text` only store state. The env var wins over the CLI flag for `PLOT_WIDTH`/`PLOT_HEIGHT`, matching the upstream node. `Node(args.name)` rather than `Node()` so the node also runs standalone as a dynamic node.
- **`opencv_plot/decode.py`** — `to_bgr()`: every supported encoding to a writable BGR array. `width`/`height` are 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: `bgr8` **copies** (the payload may be a read-only view on shared memory, and the frame is annotated in place), and `yuv420` converts with `COLOR_YUV420p2RGB` rather 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.
- **`opencv_plot/plot.py`** — `Plot` (the overlay state and the window) and `Bboxes`. `show()` draws, resizes and displays, and returns whether `q` was pressed. Resizing only happens when *both* `PLOT_WIDTH` and `PLOT_HEIGHT` are set. `headless` (from `CI=true`) skips only the window: every input is still decoded, which is what makes the node safe in a pipeline test.
- **`opencv_plot/bbox.py`** — Parses the `bbox` struct into xyxy corners. `xywh` is centre-based (x, y are the box centre). `_flat()` unwraps the single-element list that `pa.array([{...}])` produces around each field, and reads a field stored flat as-is.
- **`dora_plot/main.py`** — Argument parsing and the dora event loop, nothing else. `image` decodes and redraws; `bbox` and `text` only store state. The env var wins over the CLI flag for `PLOT_WIDTH`/`PLOT_HEIGHT`, matching the upstream node. `Node(args.name)` rather than `Node()` 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`/`height` are 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: `bgr8` **copies** (the payload may be a read-only view on shared memory, and the frame is annotated in place), and `yuv420` converts with `COLOR_YUV420p2RGB` rather 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) and `Bboxes`. `show()` draws, resizes and displays, and returns whether `q` was pressed. Resizing only happens when *both* `PLOT_WIDTH` and `PLOT_HEIGHT` are set. `headless` (from `CI=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 the `bbox` struct into xyxy corners. `xywh` is centre-based (x, y are the box centre). `_flat()` unwraps the single-element list that `pa.array([{...}])` produces around each field, and reads a field stored flat as-is.
- **`dora-node.yml`** — Node manifest (`apiVersion: 1`). `dora validate` injects these types into any dataflow that reaches the node by `path:`; a `git:`-sourced node does **not** pick it up, which is why the dataflow builds from the checkout. `image` and `text` carry 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. `bbox` is 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, with `strict_types: true`. Note the `output_types:` on the two `git:`-sourced nodes — their own manifests are not injected, so without those annotations strict validation fails on an untyped upstream. `bbox` is 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 running `dora validate` on the dataflow on push and pull request. Needs no camera and no display.
+3 -3
View File
@@ -17,7 +17,7 @@ nodes:
git: ssh://git@gitea.liria.cl:5001/liria/dora-plot.git
tag: v0.1.0
build: pip install .
path: opencv-plot
path: dora-plot
inputs:
image: camera/image
bbox: detector/bbox
@@ -55,7 +55,7 @@ the camera and the viewer:
- bbox
- id: plot
path: opencv-plot
path: dora-plot
build: pip install .
inputs:
image: camera/image
@@ -91,7 +91,7 @@ of that state:
- An **`image`** event decodes the frame and immediately redraws: it draws every stored
bbox (green rectangle plus `label, conf` text), draws the stored text at the top left,
resizes to `PLOT_WIDTH`x`PLOT_HEIGHT` if both are set, and shows it in a window titled
`Dora Node: opencv-plot`. Press `q` in the window to quit.
`Dora Node: dora-plot`. Press `q` in the window to quit.
- A **`bbox`** or **`text`** event only updates stored state; it is drawn on the next
`image` redraw.
+1 -1
View File
@@ -34,7 +34,7 @@ nodes:
data: std/core/v1/String
- id: plot
path: opencv-plot
path: dora-plot
build: pip install .
inputs:
image: camera/image
+3 -3
View File
@@ -5,14 +5,14 @@
# dataflow against it without running the node, so CI needs neither OpenCV nor
# a display.
apiVersion: 1
name: opencv-plot
name: dora-plot
namespace: liria
description: Overlays bounding boxes, labels, and text on an image input and displays it in an OpenCV window
categories: [visualization, debug]
keywords: [opencv, plot, bbox, bounding-box, image, visualization, object-detection]
runtime: python
entrypoint: opencv-plot
entrypoint: dora-plot
# `dora build --uv` / `dora run --uv` rewrites this to `uv pip install .` and
# gives the node its own managed venv under `.dora/python-envs/`, so the
# dependencies it sees at runtime are the ones installed here.
@@ -57,7 +57,7 @@ example: |
git: ssh://git@gitea.liria.cl:5001/liria/dora-plot.git
tag: v0.1.0
build: pip install .
path: opencv-plot
path: dora-plot
inputs:
image: camera/image
bbox: detector/bbox
+1 -1
View File
@@ -2,7 +2,7 @@
import numpy as np
from opencv_plot.plot import Bboxes
from dora_plot.plot import Bboxes
def parse(value, bbox_format: str) -> Bboxes:
+3 -3
View File
@@ -9,8 +9,8 @@ import os
from dora import Node
from opencv_plot import bbox, decode
from opencv_plot.plot import Plot
from dora_plot import bbox, decode
from dora_plot.plot import Plot
def main() -> None:
@@ -59,7 +59,7 @@ def _parse_args() -> argparse.Namespace:
)
parser.add_argument(
"--name",
default="opencv-plot",
default="dora-plot",
help="The name of the node in the dataflow, used when running as a dynamic node.",
)
parser.add_argument(
+1 -1
View File
@@ -5,7 +5,7 @@ from dataclasses import dataclass, field
import cv2
import numpy as np
WINDOW = "Dora Node: opencv-plot"
WINDOW = "Dora Node: dora-plot"
GREEN = (0, 255, 0)
WHITE = (255, 255, 255)
FONT = cv2.FONT_HERSHEY_SIMPLEX
+3 -3
View File
@@ -1,5 +1,5 @@
[project]
name = "opencv-plot"
name = "dora-plot"
version = "0.1.0"
description = "Dora node that overlays bounding boxes and text on an image and displays it in an OpenCV window"
readme = "README.md"
@@ -21,7 +21,7 @@ dependencies = [
]
[project.scripts]
opencv-plot = "opencv_plot.main:main"
dora-plot = "dora_plot.main:main"
[dependency-groups]
dev = ["pytest >= 8.1", "ruff >= 0.9"]
@@ -31,7 +31,7 @@ requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["opencv_plot"]
packages = ["dora_plot"]
[tool.ruff.lint]
extend-select = ["D", "I", "N", "RET", "UP"]
@@ -7,8 +7,8 @@ import numpy as np
import pytest
from PIL import Image
from opencv_plot import bbox, decode
from opencv_plot.plot import Bboxes, Plot
from dora_plot import bbox, decode
from dora_plot.plot import Bboxes, Plot
WIDTH, HEIGHT = 12, 8
Generated
+36 -36
View File
@@ -15,6 +15,42 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "dora-plot"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "dora-rs" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "opencv-python" },
{ name = "pillow" },
{ name = "pillow-avif-plugin" },
{ name = "pyarrow" },
]
[package.dev-dependencies]
dev = [
{ name = "pytest" },
{ name = "ruff" },
]
[package.metadata]
requires-dist = [
{ name = "dora-rs", specifier = ">=1.0.1" },
{ name = "numpy", specifier = ">=2.0" },
{ name = "opencv-python", specifier = ">=4.9" },
{ name = "pillow", specifier = ">=10.4" },
{ name = "pillow-avif-plugin", specifier = ">=1.4" },
{ name = "pyarrow", specifier = ">=17.0" },
]
[package.metadata.requires-dev]
dev = [
{ name = "pytest", specifier = ">=8.1" },
{ name = "ruff", specifier = ">=0.9" },
]
[[package]]
name = "dora-rs"
version = "1.0.1"
@@ -206,42 +242,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/fb/0b/b12a2df5d1b774bd9007a6fdff9381145b6223d37f11afc9c37ab0efd9a1/numpy-2.5.3-cp315-cp315t-win_arm64.whl", hash = "sha256:befa1ae5bd6030b3f512b43ff3fa5290bbed6b84411a44244b14adf835f5b89d", size = 10850807, upload-time = "2026-09-06T16:27:43.868Z" },
]
[[package]]
name = "opencv-plot"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "dora-rs" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "opencv-python" },
{ name = "pillow" },
{ name = "pillow-avif-plugin" },
{ name = "pyarrow" },
]
[package.dev-dependencies]
dev = [
{ name = "pytest" },
{ name = "ruff" },
]
[package.metadata]
requires-dist = [
{ name = "dora-rs", specifier = ">=1.0.1" },
{ name = "numpy", specifier = ">=2.0" },
{ name = "opencv-python", specifier = ">=4.9" },
{ name = "pillow", specifier = ">=10.4" },
{ name = "pillow-avif-plugin", specifier = ">=1.4" },
{ name = "pyarrow", specifier = ">=17.0" },
]
[package.metadata.requires-dev]
dev = [
{ name = "pytest", specifier = ">=8.1" },
{ name = "ruff", specifier = ">=0.9" },
]
[[package]]
name = "opencv-python"
version = "5.0.0.93"