refactor: rename the node from opencv-plot to dora-plot
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
This commit is contained in:
@@ -36,10 +36,10 @@ uv run ruff check .
|
|||||||
|
|
||||||
## Architecture
|
## 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.
|
- **`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.
|
||||||
- **`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.
|
- **`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.
|
||||||
- **`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.
|
- **`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.
|
||||||
- **`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/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.
|
- **`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.
|
- **`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.
|
- **`.gitea/workflows/validate.yml`** — CI gate running `dora validate` on the dataflow on push and pull request. Needs no camera and no display.
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ nodes:
|
|||||||
git: ssh://git@gitea.liria.cl:5001/liria/dora-plot.git
|
git: ssh://git@gitea.liria.cl:5001/liria/dora-plot.git
|
||||||
tag: v0.1.0
|
tag: v0.1.0
|
||||||
build: pip install .
|
build: pip install .
|
||||||
path: opencv-plot
|
path: dora-plot
|
||||||
inputs:
|
inputs:
|
||||||
image: camera/image
|
image: camera/image
|
||||||
bbox: detector/bbox
|
bbox: detector/bbox
|
||||||
@@ -55,7 +55,7 @@ the camera and the viewer:
|
|||||||
- bbox
|
- bbox
|
||||||
|
|
||||||
- id: plot
|
- id: plot
|
||||||
path: opencv-plot
|
path: dora-plot
|
||||||
build: pip install .
|
build: pip install .
|
||||||
inputs:
|
inputs:
|
||||||
image: camera/image
|
image: camera/image
|
||||||
@@ -91,7 +91,7 @@ of that state:
|
|||||||
- An **`image`** event decodes the frame and immediately redraws: it draws every stored
|
- 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,
|
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
|
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
|
- A **`bbox`** or **`text`** event only updates stored state; it is drawn on the next
|
||||||
`image` redraw.
|
`image` redraw.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ nodes:
|
|||||||
data: std/core/v1/String
|
data: std/core/v1/String
|
||||||
|
|
||||||
- id: plot
|
- id: plot
|
||||||
path: opencv-plot
|
path: dora-plot
|
||||||
build: pip install .
|
build: pip install .
|
||||||
inputs:
|
inputs:
|
||||||
image: camera/image
|
image: camera/image
|
||||||
|
|||||||
+3
-3
@@ -5,14 +5,14 @@
|
|||||||
# dataflow against it without running the node, so CI needs neither OpenCV nor
|
# dataflow against it without running the node, so CI needs neither OpenCV nor
|
||||||
# a display.
|
# a display.
|
||||||
apiVersion: 1
|
apiVersion: 1
|
||||||
name: opencv-plot
|
name: dora-plot
|
||||||
namespace: liria
|
namespace: liria
|
||||||
description: Overlays bounding boxes, labels, and text on an image input and displays it in an OpenCV window
|
description: Overlays bounding boxes, labels, and text on an image input and displays it in an OpenCV window
|
||||||
categories: [visualization, debug]
|
categories: [visualization, debug]
|
||||||
keywords: [opencv, plot, bbox, bounding-box, image, visualization, object-detection]
|
keywords: [opencv, plot, bbox, bounding-box, image, visualization, object-detection]
|
||||||
|
|
||||||
runtime: python
|
runtime: python
|
||||||
entrypoint: opencv-plot
|
entrypoint: dora-plot
|
||||||
# `dora build --uv` / `dora run --uv` rewrites this to `uv pip install .` and
|
# `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
|
# gives the node its own managed venv under `.dora/python-envs/`, so the
|
||||||
# dependencies it sees at runtime are the ones installed here.
|
# 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
|
git: ssh://git@gitea.liria.cl:5001/liria/dora-plot.git
|
||||||
tag: v0.1.0
|
tag: v0.1.0
|
||||||
build: pip install .
|
build: pip install .
|
||||||
path: opencv-plot
|
path: dora-plot
|
||||||
inputs:
|
inputs:
|
||||||
image: camera/image
|
image: camera/image
|
||||||
bbox: detector/bbox
|
bbox: detector/bbox
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from opencv_plot.plot import Bboxes
|
from dora_plot.plot import Bboxes
|
||||||
|
|
||||||
|
|
||||||
def parse(value, bbox_format: str) -> Bboxes:
|
def parse(value, bbox_format: str) -> Bboxes:
|
||||||
@@ -9,8 +9,8 @@ import os
|
|||||||
|
|
||||||
from dora import Node
|
from dora import Node
|
||||||
|
|
||||||
from opencv_plot import bbox, decode
|
from dora_plot import bbox, decode
|
||||||
from opencv_plot.plot import Plot
|
from dora_plot.plot import Plot
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
@@ -59,7 +59,7 @@ def _parse_args() -> argparse.Namespace:
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--name",
|
"--name",
|
||||||
default="opencv-plot",
|
default="dora-plot",
|
||||||
help="The name of the node in the dataflow, used when running as a dynamic node.",
|
help="The name of the node in the dataflow, used when running as a dynamic node.",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@@ -5,7 +5,7 @@ from dataclasses import dataclass, field
|
|||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
WINDOW = "Dora Node: opencv-plot"
|
WINDOW = "Dora Node: dora-plot"
|
||||||
GREEN = (0, 255, 0)
|
GREEN = (0, 255, 0)
|
||||||
WHITE = (255, 255, 255)
|
WHITE = (255, 255, 255)
|
||||||
FONT = cv2.FONT_HERSHEY_SIMPLEX
|
FONT = cv2.FONT_HERSHEY_SIMPLEX
|
||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "opencv-plot"
|
name = "dora-plot"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Dora node that overlays bounding boxes and text on an image and displays it in an OpenCV window"
|
description = "Dora node that overlays bounding boxes and text on an image and displays it in an OpenCV window"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -21,7 +21,7 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
opencv-plot = "opencv_plot.main:main"
|
dora-plot = "dora_plot.main:main"
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = ["pytest >= 8.1", "ruff >= 0.9"]
|
dev = ["pytest >= 8.1", "ruff >= 0.9"]
|
||||||
@@ -31,7 +31,7 @@ requires = ["hatchling"]
|
|||||||
build-backend = "hatchling.build"
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
[tool.hatch.build.targets.wheel]
|
[tool.hatch.build.targets.wheel]
|
||||||
packages = ["opencv_plot"]
|
packages = ["dora_plot"]
|
||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
extend-select = ["D", "I", "N", "RET", "UP"]
|
extend-select = ["D", "I", "N", "RET", "UP"]
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import numpy as np
|
|||||||
import pytest
|
import pytest
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from opencv_plot import bbox, decode
|
from dora_plot import bbox, decode
|
||||||
from opencv_plot.plot import Bboxes, Plot
|
from dora_plot.plot import Bboxes, Plot
|
||||||
|
|
||||||
WIDTH, HEIGHT = 12, 8
|
WIDTH, HEIGHT = 12, 8
|
||||||
|
|
||||||
@@ -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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "dora-rs"
|
name = "dora-rs"
|
||||||
version = "1.0.1"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "opencv-python"
|
name = "opencv-python"
|
||||||
version = "5.0.0.93"
|
version = "5.0.0.93"
|
||||||
|
|||||||
Reference in New Issue
Block a user