整管线 API(平台调度管线)
Whole-pipeline facade: one call wiring capture -> inference -> overlay.
Positioning (read this before reaching for InferencePipeline in
pipeline.py): this module is the platform-scheduled pipeline. The
camera daemon feeds frames to the model (InferenceClient.subscribe),
so no pixels ever cross into the app process; results come back over the
stream, get pushed to the hardware overlay via the event bus
(OverlayClient), and the annotated video ships inside the encoded
stream — the app only ever sees lightweight InferenceResult structs.
InferencePipeline, by contrast, is the client-side convenience: it
pulls frames into the app, runs pre->infer->post on the app thread, and
is the right tool when the app needs the pixels (custom preprocessing,
multi-model chains on the same frame, drawing with its own rasterizer).
Choose by where the pixels should live: keep them on the device and take
results — StreamPipeline; need them in your process —
InferencePipeline.
One instance is single-shot: start() once, stop() once; build a
new instance for another run. Typical use:
zones = [{"points": [[0, 0], [1, 0], [1, 1]], "label": "yard"}]
pipe = StreamPipeline(
"main", "yolov5n", fps=10,
min_score=0.5, labels=["person"], polygons=zones,
on_result=lambda r: r if r.count_by_label("person") else None,
)
pipe.start()
for seq, result in pipe.results(): # optional: results in the app too
...
pipe.stop() # clears boxes + zones on the stream
- class neoruntime_ipc_sdk.stream_pipeline.StreamPipelineStatus(running, results_seen, results_annotated, annotate_errors, result_queue_drops, subscribe_dropped, last_error, last_latency_ms, avg_latency_ms, last_skew_us, avg_skew_us)[source]
Bases:
objectPoint-in-time health snapshot from
StreamPipeline.status().results_seencounts every result the worker accepted from the subscription, before theon_resulthook runs — results the hook dropped or replaced are included.last_latency_ms/avg_latency_mslikewise time every accepted result, including ones later dropped by the hook.- running: bool
- results_seen: int
- results_annotated: int
- annotate_errors: int
- result_queue_drops: int
- subscribe_dropped: int
- last_error: str
- last_latency_ms: float
- avg_latency_ms: float
- last_skew_us: int
- avg_skew_us: float
- __init__(running, results_seen, results_annotated, annotate_errors, result_queue_drops, subscribe_dropped, last_error, last_latency_ms, avg_latency_ms, last_skew_us, avg_skew_us)
- class neoruntime_ipc_sdk.stream_pipeline.StreamPipeline(stream, model, *, fps=10, session_id='', raw_output_only=False, max_consecutive_failures=10, queue_size=100, draw=True, overlay_config=None, ttl_ms=None, min_score=0.0, labels=None, polygons=None, on_result=None, result_queue_size=100, inference=None, overlay=None)[source]
Bases:
objectOne-call pipeline: subscribe, draw on the hardware overlay, observe.
- Parameters:
stream (str) – camera stream id the model runs on (e.g.
"main").model (str) – registered model id.
fps (int) – result-rate cap for the subscription.
session_id (str) – optional inference session id (observability/logging).
raw_output_only (bool) – skip post-processing, hand back raw tensors.
max_consecutive_failures (int | None) – consecutive failed frames tolerated before the worker gives up (0/None = unlimited); see
InferenceClient.subscribe().queue_size (int | None) – bound for the subscribe-side result queue.
draw (bool) – push results to the hardware overlay.
Falsealso suppresses staticpolygons— the encoded stream stays clean; results still flow toresults().overlay_config (OverlayConfig | None) – optional
OverlayConfigapplied at start (labels, colors, strict frame-lock, …).ttl_ms (int | None) – positive int, per-event validity override forwarded to every annotate call; omitted = the daemon derives it from the stream fps.
min_score (float) – draw only detections with
score >= min_score.labels (list[str] | None) – draw only these labels (
None= all). Filtering affects drawing only —results()yields the full result — and a fully-filtered result still publishes an empty detection list, clearing stale boxes.polygons (list[dict] | None) – static zone polygons pushed once at start and cleared at stop (normalized [0,1] points; see
OverlayClient.annotate()).on_result (Callable[[InferenceResult], InferenceResult | None] | None) – hook called per result on the worker thread. Return a (possibly replaced) result to flow onward, or
Noneto drop it (not drawn, not queued) — put tracking/filtering logic here. A raised exception drops that result, is recorded instatus().last_errorand is non-fatal.result_queue_size (int) – bound for the app-facing queue behind
results(); a slow app drops its oldest queued results (counted instatus().result_queue_drops).inference (InferenceClient | None) – inject an
InferenceClient(tests / client reuse). Injected clients are NOT closed bystop().overlay (OverlayClient | None) – inject an
OverlayClient. Not closed on stop.
- __init__(stream, model, *, fps=10, session_id='', raw_output_only=False, max_consecutive_failures=10, queue_size=100, draw=True, overlay_config=None, ttl_ms=None, min_score=0.0, labels=None, polygons=None, on_result=None, result_queue_size=100, inference=None, overlay=None)[source]
- start()[source]
Subscribe, apply overlay config / static zones, start drawing.
- stop()[source]
Stop the worker, clear what was drawn, close owned clients.
Idempotent, and a no-op before
start(). The worker is joined first so the box-clearing annotate cannot race a final result. Safe to call fromon_result(i.e. from the worker thread itself): the self-join is skipped and the teardown below runs on the caller’s thread.
- results()[source]
Yield
(frame_sequence, InferenceResult)post-callback.Ends when the worker stops (
stop()or a fatal stream error). The queue is bounded byresult_queue_sizewith drop-oldest overflow — a slow consumer loses its oldest results, counted instatus().result_queue_drops. The pre-start check is eager: it raises at call time, not on the firstnext().
- status()[source]
Snapshot of pipeline health and pass-through subscribe stats.
StreamPipeline
- class neoruntime_ipc_sdk.StreamPipeline(stream, model, *, fps=10, session_id='', raw_output_only=False, max_consecutive_failures=10, queue_size=100, draw=True, overlay_config=None, ttl_ms=None, min_score=0.0, labels=None, polygons=None, on_result=None, result_queue_size=100, inference=None, overlay=None)[source]
Bases:
objectOne-call pipeline: subscribe, draw on the hardware overlay, observe.
- Parameters:
stream (str) – camera stream id the model runs on (e.g.
"main").model (str) – registered model id.
fps (int) – result-rate cap for the subscription.
session_id (str) – optional inference session id (observability/logging).
raw_output_only (bool) – skip post-processing, hand back raw tensors.
max_consecutive_failures (int | None) – consecutive failed frames tolerated before the worker gives up (0/None = unlimited); see
InferenceClient.subscribe().queue_size (int | None) – bound for the subscribe-side result queue.
draw (bool) – push results to the hardware overlay.
Falsealso suppresses staticpolygons— the encoded stream stays clean; results still flow toresults().overlay_config (OverlayConfig | None) – optional
OverlayConfigapplied at start (labels, colors, strict frame-lock, …).ttl_ms (int | None) – positive int, per-event validity override forwarded to every annotate call; omitted = the daemon derives it from the stream fps.
min_score (float) – draw only detections with
score >= min_score.labels (list[str] | None) – draw only these labels (
None= all). Filtering affects drawing only —results()yields the full result — and a fully-filtered result still publishes an empty detection list, clearing stale boxes.polygons (list[dict] | None) – static zone polygons pushed once at start and cleared at stop (normalized [0,1] points; see
OverlayClient.annotate()).on_result (Callable[[InferenceResult], InferenceResult | None] | None) – hook called per result on the worker thread. Return a (possibly replaced) result to flow onward, or
Noneto drop it (not drawn, not queued) — put tracking/filtering logic here. A raised exception drops that result, is recorded instatus().last_errorand is non-fatal.result_queue_size (int) – bound for the app-facing queue behind
results(); a slow app drops its oldest queued results (counted instatus().result_queue_drops).inference (InferenceClient | None) – inject an
InferenceClient(tests / client reuse). Injected clients are NOT closed bystop().overlay (OverlayClient | None) – inject an
OverlayClient. Not closed on stop.
- __init__(stream, model, *, fps=10, session_id='', raw_output_only=False, max_consecutive_failures=10, queue_size=100, draw=True, overlay_config=None, ttl_ms=None, min_score=0.0, labels=None, polygons=None, on_result=None, result_queue_size=100, inference=None, overlay=None)[source]
- start()[source]
Subscribe, apply overlay config / static zones, start drawing.
- stop()[source]
Stop the worker, clear what was drawn, close owned clients.
Idempotent, and a no-op before
start(). The worker is joined first so the box-clearing annotate cannot race a final result. Safe to call fromon_result(i.e. from the worker thread itself): the self-join is skipped and the teardown below runs on the caller’s thread.
- results()[source]
Yield
(frame_sequence, InferenceResult)post-callback.Ends when the worker stops (
stop()or a fatal stream error). The queue is bounded byresult_queue_sizewith drop-oldest overflow — a slow consumer loses its oldest results, counted instatus().result_queue_drops. The pre-start check is eager: it raises at call time, not on the firstnext().
- status()[source]
Snapshot of pipeline health and pass-through subscribe stats.
StreamPipelineStatus
- class neoruntime_ipc_sdk.StreamPipelineStatus(running, results_seen, results_annotated, annotate_errors, result_queue_drops, subscribe_dropped, last_error, last_latency_ms, avg_latency_ms, last_skew_us, avg_skew_us)[source]
Point-in-time health snapshot from
StreamPipeline.status().results_seencounts every result the worker accepted from the subscription, before theon_resulthook runs — results the hook dropped or replaced are included.last_latency_ms/avg_latency_mslikewise time every accepted result, including ones later dropped by the hook.- __init__(running, results_seen, results_annotated, annotate_errors, result_queue_drops, subscribe_dropped, last_error, last_latency_ms, avg_latency_ms, last_skew_us, avg_skew_us)
使用示例
一次启动:订阅推理 + 硬件叠加 + 应用侧结果
from neoruntime_ipc_sdk import StreamPipeline
zones = [{"points": [[0, 0], [1, 0], [1, 1]], "label": "yard"}]
pipe = StreamPipeline(
"main", "yolov5n", fps=10,
min_score=0.5, labels=["person"], polygons=zones,
on_result=lambda r: r if r.objects else None, # 空结果不画不进队列
).start()
# 可选:应用侧同时消费结果(告警、二次处理、转发)。
# 叠加画面不经过这里 —— 框已由硬件叠加层烘进编码流。
for seq, result in pipe.results():
print(seq, [(o.label, round(o.score, 2)) for o in result.objects])
if seq >= 100:
break
st = pipe.status() # results_seen / annotated / drops / skew ...
pipe.stop() # 清除检测框与静态多边形,编码画面恢复干净
语义要点
与 InferencePipeline(pipeline.py)的定位区分:StreamPipeline 是 平台调度管线 —— 帧由 camera daemon 喂给模型(
subscribe),像素 不进应用进程,结果经OverlayClient推到硬件叠加层并烘进编码流; InferencePipeline 是客户端便捷层 —— 帧拉进应用、pre→infer→post 在 应用线程同步执行,适合需要像素的场景(自定义预处理、同帧多模型链、 自绘)。按”像素应该住哪”选择。单发生命周期:
start()一次、stop()一次;再次运行须新建 实例。stop()幂等、start 前调用为 no-op;先 join 工作线程再清 框/清静态多边形,只关闭自己创建的客户端(注入的inference=/overlay=不关闭)。过滤只影响绘制:
min_score/labels仅过滤画到叠加层的 内容,results()仍返回完整结果;全被滤掉的结果发布空检测列表, 以清除滞留旧框。on_result 钩子:在工作线程逐结果调用,返回(可替换的)结果继续 流动,返回
None丢弃;抛异常只丢该结果并记入status().last_error,非致命。两段有界队列、丢最旧:subscribe 侧
queue_size(subscribe_dropped透出)与应用侧result_queue_size(result_queue_drops计数);叠加失败计入annotate_errors且不中断管线。draw=False 完全不触碰叠加层:静态 polygons 也不推,编码流保持 干净;结果仍进
results()。会话标记(P2-13):
session_id=贯穿推理与绘制 —— 订阅请求 与结果绘制带同一标记;进程意外退出(含 SIGKILL)后,推理会话结束 事件触发 daemon 把带该标记的多边形一并清掉。启动时的静态 zone 与stop()的清屏写入不带标记,属于运维写入、无条件生效。status() 透传订阅观测面:last/avg latency_ms 与 last/avg skew_us (结果时间戳相对帧时间戳的偏移 —— 实时预览与严格对帧两种模式共用 的验收指标)。