Post-processing API

Post-processing helpers for raw model outputs — greedy NMS in numpy.

Streaming inference on the platform applies NMS server-side when the model is registered with NMS parameters, but single-shot infer() results, custom decode heads and app-side re-filtering still need a local implementation. Apps currently hand-roll one whenever scores and boxes arrive separately.

Pure numpy, no accelerator — the vectorised IoU matrix is already memory-bound at the sizes detector heads produce. When ai-runtime exposes its create-time NMS registration over the service layer (see docs/proposals/sdk-hardware-routing.md), prefer that and keep this for client-side filtering.

from neoruntime_ipc_sdk.postprocess import nms

keep = nms(boxes_xyxy, scores, iou_threshold=0.45, class_ids=cls)
boxes, scores = boxes[keep], scores[keep]
neoruntime_ipc_sdk.postprocess.nms(boxes, scores, iou_threshold=0.5, class_ids=None)[source]

Greedy non-maximum suppression.

Parameters:
  • boxes (ndarray) – (N, 4) xyxy boxes [x1, y1, x2, y2].

  • scores (ndarray) – (N,) confidence scores.

  • iou_threshold (float) – boxes overlapping a kept box by more than this are suppressed.

  • class_ids (ndarray | None) – optional (N,) class labels — boxes of different classes never suppress each other.

Returns:

Indices of the kept boxes, in descending-score order.

Return type:

list[int]

Functions

nms

neoruntime_ipc_sdk.postprocess.nms(boxes, scores, iou_threshold=0.5, class_ids=None)[source]

Greedy non-maximum suppression.

Parameters:
  • boxes (ndarray) – (N, 4) xyxy boxes [x1, y1, x2, y2].

  • scores (ndarray) – (N,) confidence scores.

  • iou_threshold (float) – boxes overlapping a kept box by more than this are suppressed.

  • class_ids (ndarray | None) – optional (N,) class labels — boxes of different classes never suppress each other.

Returns:

Indices of the kept boxes, in descending-score order.

Return type:

list[int]

Examples

Filter overlapping boxes after decoding

import numpy as np
from neoruntime_ipc_sdk import nms

# Candidate boxes (xywh), scores and classes come from the model's
# output-tensor decoding
keep = nms(boxes, scores, iou_threshold=0.45, class_ids=class_ids)

for i in keep:
    x, y, w, h = boxes[i]
    print(f"{labels[class_ids[i]]} {scores[i]:.2f} @ ({x:.0f},{y:.0f},{w:.0f},{h:.0f})")

Through the accel router (switches to hardware NMS when exposed)

# Software implementation today; once ai-runtime exposes NMS
# registration params the hardware leg takes over automatically —
# see docs/proposals/sdk-hardware-routing.md
from neoruntime_ipc_sdk import get_default_router

keep = get_default_router().run("nms", boxes, scores, iou_threshold=0.45)