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:
@@ -0,0 +1,135 @@
|
||||
"""Tests that need neither a dataflow nor a display."""
|
||||
|
||||
import io
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import pytest
|
||||
from PIL import Image
|
||||
|
||||
from dora_plot import bbox, decode
|
||||
from dora_plot.plot import Bboxes, Plot
|
||||
|
||||
WIDTH, HEIGHT = 12, 8
|
||||
|
||||
|
||||
class FakeStorage:
|
||||
"""Stands in for the Arrow array dora hands to the node."""
|
||||
|
||||
def __init__(self, data: np.ndarray) -> None:
|
||||
self._data = data
|
||||
|
||||
def to_numpy(self) -> np.ndarray:
|
||||
return self._data
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def frame() -> np.ndarray:
|
||||
rng = np.random.default_rng(seed=0)
|
||||
return rng.integers(0, 255, (HEIGHT, WIDTH, 3), dtype=np.uint8)
|
||||
|
||||
|
||||
def test_bgr8_round_trips_unchanged(frame):
|
||||
decoded = decode.to_bgr(FakeStorage(frame.ravel()), "bgr8", WIDTH, HEIGHT)
|
||||
|
||||
np.testing.assert_array_equal(decoded, frame)
|
||||
|
||||
|
||||
def test_rgb8_round_trips_unchanged(frame):
|
||||
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||
|
||||
decoded = decode.to_bgr(FakeStorage(rgb.ravel()), "rgb8", WIDTH, HEIGHT)
|
||||
|
||||
np.testing.assert_array_equal(decoded, frame)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("suffix", "encoding"), [
|
||||
(".png", "png"),
|
||||
(".jpg", "jpeg"),
|
||||
(".bmp", "bmp"),
|
||||
(".webp", "webp"),
|
||||
])
|
||||
def test_self_describing_encodings_need_no_dimensions(frame, suffix, encoding):
|
||||
ok, buffer = cv2.imencode(suffix, frame)
|
||||
assert ok
|
||||
|
||||
decoded = decode.to_bgr(FakeStorage(buffer.ravel()), encoding, None, None)
|
||||
|
||||
assert decoded.shape == (HEIGHT, WIDTH, 3)
|
||||
|
||||
|
||||
def test_yuv420_is_decoded(frame):
|
||||
planes = cv2.cvtColor(frame, cv2.COLOR_BGR2YUV_I420)
|
||||
|
||||
decoded = decode.to_bgr(FakeStorage(planes.ravel()), "yuv420", WIDTH, HEIGHT)
|
||||
|
||||
assert decoded.shape == (HEIGHT, WIDTH, 3)
|
||||
|
||||
|
||||
def test_avif_is_decoded(frame):
|
||||
buffer = io.BytesIO()
|
||||
Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)).save(buffer, format="AVIF")
|
||||
payload = np.frombuffer(buffer.getvalue(), dtype=np.uint8)
|
||||
|
||||
decoded = decode.to_bgr(FakeStorage(payload), "avif", None, None)
|
||||
|
||||
assert decoded.shape == (HEIGHT, WIDTH, 3)
|
||||
|
||||
|
||||
def test_decoded_frame_is_writable():
|
||||
storage = FakeStorage(np.arange(2 * 3 * 3, dtype=np.uint8))
|
||||
|
||||
decoded = decode.to_bgr(storage, "bgr8", width=3, height=2)
|
||||
|
||||
# The frame is annotated in place, so it must not be a read-only view.
|
||||
decoded[0, 0] = 0
|
||||
|
||||
|
||||
def test_raw_encodings_need_dimensions():
|
||||
storage = FakeStorage(np.zeros(18, dtype=np.uint8))
|
||||
with pytest.raises(RuntimeError, match="width"):
|
||||
decode.to_bgr(storage, "bgr8", width=None, height=None)
|
||||
|
||||
|
||||
def test_short_payload_is_rejected():
|
||||
storage = FakeStorage(np.zeros(4, dtype=np.uint8))
|
||||
with pytest.raises(RuntimeError, match="expected at least"):
|
||||
decode.to_bgr(storage, "bgr8", width=3, height=2)
|
||||
|
||||
|
||||
def test_unsupported_encoding_is_rejected():
|
||||
storage = FakeStorage(np.zeros(4, dtype=np.uint8))
|
||||
with pytest.raises(RuntimeError, match="unsupported image encoding"):
|
||||
decode.to_bgr(storage, "tiff", width=None, height=None)
|
||||
|
||||
|
||||
def test_xywh_is_converted_to_corners():
|
||||
import pyarrow as pa
|
||||
|
||||
value = pa.array(
|
||||
[
|
||||
{
|
||||
"bbox": np.array([10.0, 20.0, 4.0, 6.0]),
|
||||
"conf": np.array([0.5]),
|
||||
"labels": np.array(["cat"]),
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
boxes = bbox.parse(value, "xywh")
|
||||
|
||||
np.testing.assert_allclose(boxes.corners, [[8.0, 17.0, 12.0, 23.0]])
|
||||
assert list(boxes.labels) == ["cat"]
|
||||
|
||||
|
||||
def test_headless_show_draws_without_a_window():
|
||||
plot = Plot(headless=True, text="hello")
|
||||
plot.bboxes = Bboxes(
|
||||
corners=np.array([[1.0, 1.0, 5.0, 5.0]]),
|
||||
conf=np.array([0.9]),
|
||||
labels=np.array(["cat"]),
|
||||
)
|
||||
frame = np.zeros((32, 32, 3), dtype=np.uint8)
|
||||
|
||||
assert plot.show(frame) is False
|
||||
assert frame.any(), "the overlays should have marked the frame"
|
||||
Reference in New Issue
Block a user