检测框绘制 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.render_overlay_fragments(frame_w, frame_h, boxes=(), labels=None, scores=None, colors=None, thickness=2, polygons=(), tracks=())[source]

Render boxes as tight stroke fragments instead of one bbox canvas.

Same inputs and straight-alpha contract as render_overlay_rgba(), but returns a list of (rgba, x, y) fragments to spread over one DspClient.blend_hw() call (the daemon composites the whole overlay list in a single job, so extra fragments cost only their area). Each box becomes up to four thin canvases — caption+top edge, bottom edge, left edge, right edge — so a box pays for its 2 * thickness stroke perimeter plus its caption strip, not its area: a full-frame 1280x720 box drops from a 0.9 MP canvas (render, RGBA->ARGB repack, wire bytes and blend are all area-bound; ~3.7 MB per frame over the socket) to ~0.06 MP of 16 px strips. Polygons and tracks stay one canvas per shape (that shape’s own bbox, not the union of everything).

Pixels are identical to render_overlay_rgba() for non-overlapping shapes: every fragment is rasterized by the same backend at the same frame coordinates on a smaller canvas, and the fragments tile the shape extents without gaps or overlap. Two caveats: overlapping shapes composite marginally differently (an AA edge landing on another shape’s stroke blends against it instead of taking the union mask), and in the per-shape fallback below a caption wider than its shape clips at that shape’s extent (the fragment path itself widens a caption’s strip to its measured text extent instead). Call once per detection for exact single-canvas parity.

Fragment count is bounded for the caller: blend_hw caps one job at 64 overlays and each box costs up to four fragments, so past ~15 boxes this falls back to one canvas per shape, and past 64 shapes to the one shared union canvas — the return always fits one blend job.

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().

使用示例

绘制检测框与标签

from neoruntime_ipc_sdk import draw_boxes

# boxes 为像素坐标 (x1, y1, x2, y2);labels/scores 与 boxes 一一对应
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,
)

绘制任意文本

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,
)

直接渲染推理结果

from neoruntime_ipc_sdk import InferenceClient, draw_detections

infer = InferenceClient()
result = infer.infer("yolov5m_vehicles", frame)
rgb = frame.to_rgb()  # Frame 先物化(keep-fd 帧不能直接画)

# 接受 InferenceResult 或 list[DetectedObject],自动画框 + 标签 + 置信度
# SDK 0.7.4 起:NV12 2D 数组本身即走 DSP blend 路由,RGB 数组走软件光栅
annotated = draw_detections(rgb, result)

多边形与轨迹(区域/轨迹可视化,dsp-offload P2)

from neoruntime_ipc_sdk import draw_polygons, render_overlay_rgba

# shapes = [(points, color)];points 是 (N, 2) 像素坐标序列,
# color=None 用默认绿色。closed=False 画开放折线(轨迹)。
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)
# 轨迹单独画:draw_polygons(image, [(track, (0, 200, 255))], closed=False)

# 硬件腿:同样的 shapes 进 render_overlay_rgba 的 polygons/tracks,
# 与 boxes 一起并入最小画布并集,一次 blend_hw 合成
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_* 函数返回**副本**,不修改传入图像;输入/输出均为 RGB 像素坐标(与 to_array 输出一致)。