add a depth map panel

The node showed a colour image only, so debugging a depth pipeline meant
either trusting the numbers or writing a throwaway viewer. It now keeps one
panel per frame stream -- image and depth -- and tiles them side by side,
scaled to a common height. Wiring one is a plain viewer; wiring both shows
them together. bbox and text stay overlays rather than panels.

The contract follows what dora-zed already publishes: float32 [H, W] in
millimetres with 0 for missing. It is sent there as a raw byte slice, so the
payload arrives as UInt8 despite the Float32 contract and is reinterpreted
rather than converted.

Auto-ranging the colormap over the valid pixels' full extent, rather than over
a percentile of them, is deliberate and was not the first attempt: trimming
the tails collapsed on the scene the node exists for. A small object in front
of a large wall is a few percent of the pixels, so both tails landed on the
wall and the object clipped away. Rendering a frame and looking at it found
that; every test passed either way.

Verified with dora-cli 1.0.1: validate passes with both contracts injected,
the dataflow runs both nodes to a clean exit, and a composed frame was
rendered and inspected.
This commit is contained in:
2026-09-15 05:06:50 -03:00
parent 407395ab88
commit 0efeacd538
10 changed files with 417 additions and 50 deletions
+24 -4
View File
@@ -1,8 +1,11 @@
"""Synthetic source node: this repository's stand-in for a camera and a detector.
Emits an animated frame, the box around the square moving in it, and a caption --
the three inputs dora-image-view draws -- so the example dataflow needs no camera, no
detector and no network.
Emits an animated frame, a depth map of the same scene, the box around the square
moving in it, and a caption -- every input dora-image-view draws -- so the example
dataflow needs no camera, no detector and no network.
The depth map describes the same scene as the image: a flat back wall with the square
standing in front of it.
"""
import os
@@ -21,9 +24,12 @@ BOX = 96
SPEED = 6
CAPTION = "dora-image-view synthetic source"
WALL_MM = 2000.0
SQUARE_MM = 800.0
def main() -> None:
"""Send one image, bbox and text per tick."""
"""Send one image, depth map, bbox and text per tick."""
node = Node("source")
sent = 0
@@ -46,6 +52,12 @@ def main() -> None:
}]),
{"format": "xyxy"},
)
depth = render_depth(corners)
node.send_output(
"depth",
pa.array(depth.ravel()),
{"shape": [HEIGHT, WIDTH], "dtype": "float32", "layout": "HW", "units": "mm"},
)
node.send_output("text", pa.array([CAPTION]))
sent += 1
@@ -69,5 +81,13 @@ def render(index: int) -> tuple[np.ndarray, np.ndarray]:
return frame, corners
def render_depth(corners: np.ndarray) -> np.ndarray:
"""Depth of the scene in `render`: a flat wall with the square in front of it."""
depth = np.full((HEIGHT, WIDTH), WALL_MM, dtype=np.float32)
left, top, right, bottom = (int(value) for value in corners)
depth[top:bottom, left:right] = SQUARE_MM
return depth
if __name__ == "__main__":
main()