Detection Drawing API

Drawing utilities - annotate RGB numpy arrays with detection boxes and text.

All functions take an RGB uint8 array (H, W, 3) and return a NEW array; the input is never modified. cv2 accelerates rendering when installed, otherwise Pillow (a hard SDK dependency) is used.

render_overlay_rgba is the hardware companion: it renders the same annotation as a minimal straight-alpha RGBA canvas (plus its frame offset) for DspClient.blend_hw instead of rasterizing onto the pixels.

Example

frame = client.get_frame(“main”) # Frame rgb = draw_detections(frame.to_rgb(), result)

neoruntime_ipc_sdk.draw.draw_boxes(image, boxes, labels=None, scores=None, color=(0, 255, 0), thickness=2)[source]

Draw bounding boxes (pixel coordinates) on an RGB array copy.

Parameters:
  • image (ndarray) – RGB uint8 array (H, W, 3).

  • boxes (Iterable) – iterable of (x1, y1, x2, y2) or BoundingBox-like objects.

  • labels (Sequence[str | None] | None) – optional per-box text (combined with scores when given).

  • scores (Sequence[float] | None) – optional per-box confidence.

  • color (tuple[int, int, int]) – RGB box color.

  • thickness (int) – line thickness in pixels.

Returns: new RGB array; the input array is not modified.

neoruntime_ipc_sdk.draw.draw_text(image, text, xy, color=(255, 255, 255), font_scale=0.5, thickness=1)[source]

Draw a text string at pixel position xy on an RGB array copy.

neoruntime_ipc_sdk.draw.draw_detections(image, result_or_objects, color=None)[source]

Draw an InferenceResult (or a list of DetectedObject) on an RGB copy.

Each object gets a box plus a “label score” caption. When color is None, a per-class color from PALETTE is chosen via class_id.

Routing: NV12 2D arrays ride the accel router (DSP blend when the daemon is reachable, the CPU mirror otherwise); RGB arrays go straight to the software raster (no doomed hardware attempt, no fake degradation row); keep-fd frames raise — the zero-copy blend chain that would serve them is gated (state-dependent field wedge).

neoruntime_ipc_sdk.draw.render_overlay_rgba(frame_w, frame_h, boxes=(), labels=None, scores=None, colors=None, thickness=2, polygons=(), tracks=())[source]

Render boxes + captions + polygons + tracks as a minimal straight-alpha RGBA overlay.

Returns (rgba, x0, y0): an (h, w, 4) uint8 canvas holding every shape and its top-left position on the frame. Hand it to DspClient.blend_hw() for the hardware composite — the canvas is the union bbox of all shapes (clamped to the frame, floored at 16x16), so untouched pixels never enter the blend and the quota footprint stays small.

polygons is a sequence of (points, color) pairs drawn as closed outlines (zone shapes); tracks the same shape drawn open (trajectories). points is any (N, 2) sequence of frame pixel coordinates; color=None falls back to green like boxes.

Straight alpha by construction: each shape is first drawn white-on-black as a coverage mask (cv2 LINE_AA captions give edge coverage t; rectangles stay hard-edged like draw_boxes()), then shapes are colored in draw order and alpha is the mask union — so compositing with t*C + (1-t)*base reproduces the software output. Colors default to green; pick from PALETTE by class_id to match draw_detections().

neoruntime_ipc_sdk.draw.draw_polygons(image, shapes, thickness=2, closed=True)[source]

Draw polygon outlines (or open tracks) on an RGB array copy.

The software counterpart of render_overlay_rgba()’s polygons/tracks legs, mirroring draw_boxes()’ copy-in copy-out conventions — reach for it when the frame stays client-side and no blend is needed.

Parameters:
  • image (ndarray) – RGB uint8 array (H, W, 3).

  • shapes (Iterable) – iterable of (points, color) pairs, points being any (N, 2) sequence of pixel coordinates (same schema as render_overlay_rgba()); color=None means green.

  • thickness (int) – line thickness in pixels.

  • closed (bool) – close each outline (True for zones, False for tracks).

Returns: new RGB array; the input array is not modified.

draw_boxes

neoruntime_ipc_sdk.draw_boxes(image, boxes, labels=None, scores=None, color=(0, 255, 0), thickness=2)[source]

Draw bounding boxes (pixel coordinates) on an RGB array copy.

Parameters:
  • image (ndarray) – RGB uint8 array (H, W, 3).

  • boxes (Iterable) – iterable of (x1, y1, x2, y2) or BoundingBox-like objects.

  • labels (Sequence[str | None] | None) – optional per-box text (combined with scores when given).

  • scores (Sequence[float] | None) – optional per-box confidence.

  • color (tuple[int, int, int]) – RGB box color.

  • thickness (int) – line thickness in pixels.

Returns: new RGB array; the input array is not modified.

draw_text

neoruntime_ipc_sdk.draw_text(image, text, xy, color=(255, 255, 255), font_scale=0.5, thickness=1)[source]

Draw a text string at pixel position xy on an RGB array copy.

draw_detections

neoruntime_ipc_sdk.draw_detections(image, result_or_objects, color=None)[source]

Draw an InferenceResult (or a list of DetectedObject) on an RGB copy.

Each object gets a box plus a “label score” caption. When color is None, a per-class color from PALETTE is chosen via class_id.

Routing: NV12 2D arrays ride the accel router (DSP blend when the daemon is reachable, the CPU mirror otherwise); RGB arrays go straight to the software raster (no doomed hardware attempt, no fake degradation row); keep-fd frames raise — the zero-copy blend chain that would serve them is gated (state-dependent field wedge).

draw_polygons

neoruntime_ipc_sdk.draw_polygons(image, shapes, thickness=2, closed=True)[source]

Draw polygon outlines (or open tracks) on an RGB array copy.

The software counterpart of render_overlay_rgba()’s polygons/tracks legs, mirroring draw_boxes()’ copy-in copy-out conventions — reach for it when the frame stays client-side and no blend is needed.

Parameters:
  • image (ndarray) – RGB uint8 array (H, W, 3).

  • shapes (Iterable) – iterable of (points, color) pairs, points being any (N, 2) sequence of pixel coordinates (same schema as render_overlay_rgba()); color=None means green.

  • thickness (int) – line thickness in pixels.

  • closed (bool) – close each outline (True for zones, False for tracks).

Returns: new RGB array; the input array is not modified.

render_overlay_rgba

neoruntime_ipc_sdk.render_overlay_rgba(frame_w, frame_h, boxes=(), labels=None, scores=None, colors=None, thickness=2, polygons=(), tracks=())[source]

Render boxes + captions + polygons + tracks as a minimal straight-alpha RGBA overlay.

Returns (rgba, x0, y0): an (h, w, 4) uint8 canvas holding every shape and its top-left position on the frame. Hand it to DspClient.blend_hw() for the hardware composite — the canvas is the union bbox of all shapes (clamped to the frame, floored at 16x16), so untouched pixels never enter the blend and the quota footprint stays small.

polygons is a sequence of (points, color) pairs drawn as closed outlines (zone shapes); tracks the same shape drawn open (trajectories). points is any (N, 2) sequence of frame pixel coordinates; color=None falls back to green like boxes.

Straight alpha by construction: each shape is first drawn white-on-black as a coverage mask (cv2 LINE_AA captions give edge coverage t; rectangles stay hard-edged like draw_boxes()), then shapes are colored in draw order and alpha is the mask union — so compositing with t*C + (1-t)*base reproduces the software output. Colors default to green; pick from PALETTE by class_id to match draw_detections().

Usage Examples

Draw boxes and labels

from neoruntime_ipc_sdk import draw_boxes

# boxes are pixel coordinates (x1, y1, x2, y2); labels/scores align
# with boxes one-to-one
annotated = draw_boxes(
    image,
    boxes=[(120, 80, 360, 300), (400, 200, 620, 460)],
    labels=["car", "person"],
    scores=[0.92, 0.87],
    color=(0, 255, 0),
    thickness=2,
)

Draw arbitrary text

from neoruntime_ipc_sdk import draw_text

annotated = draw_text(
    image, "FPS: 15.2", (12, 24),
    color=(255, 255, 255), font_scale=0.5, thickness=1,
)

Render inference results directly

from neoruntime_ipc_sdk import InferenceClient, draw_detections

infer = InferenceClient()
result = infer.infer("yolov5m_vehicles", frame)
rgb = frame.to_rgb()  # materialize the Frame (keep-fd frames cannot draw)

# Accepts an InferenceResult or list[DetectedObject]; draws boxes +
# labels + confidence automatically. Since SDK 0.7.4 an NV12 2D array
# itself rides the DSP blend route; RGB arrays take the software raster
annotated = draw_detections(rgb, result)

Polygons and tracks (zones / trajectory overlays, dsp-offload P2)

from neoruntime_ipc_sdk import draw_polygons, render_overlay_rgba

# shapes = [(points, color)]; points is an (N, 2) pixel coordinate
# sequence, color=None means the default green. closed=False draws
# open polylines (trajectories).
zone = [(80, 60), (560, 60), (560, 380), (80, 380)]
track = [(100, 240), (200, 200), (320, 210), (430, 260)]
annotated = draw_polygons(image, [(zone, (255, 192, 0)),
                                   (track, (0, 200, 255))], closed=True)
# tracks alone: draw_polygons(image, [(track, (0, 200, 255))], closed=False)

# Hardware leg: the same shapes feed render_overlay_rgba's
# polygons/tracks, join the boxes in the minimal canvas union, and
# composite in one blend_hw call
rgba, x0, y0 = render_overlay_rgba(
    w, h, boxes, labels, scores, colors,
    polygons=[(zone, (255, 192, 0))],
    tracks=[(track, (0, 200, 255))],
)
annotated = dsp.blend_hw(nv12, [(rgba, x0, y0)])

Note

draw_* functions return a copy and never modify the input image; inputs/outputs are RGB pixel coordinates (matching to_array output).