NVIDIA DeepStream
NVIDIA DeepStreamはnvinferエレメントを通して推論を実行し、そこにはONNXグラフ、対応する設定ファイル、バウンディングボックスパーサーが必要です。ONNXエクスポートでdeepstream=Trueを指定すると、前の2つが出力され、3つ目に接続されます。
- フラグ
export(format="onnx", deepstream=True)- 出力
- ONNXグラフ、config_infer_primary_<stem>.txt、<stem>_labels.txt
- 対応範囲
- 9タスクにまたがる43通りのファミリーとタスクの組み合わせ
- パーサー
- Marcos LucianoによるMITライセンスのDeepStream-YoloプロジェクトのNvDsInferParseYoloです。デバイスごとに1回ビルドします。 github.com/marcoslucianops/DeepStream-Yolo
- 提供状況
- v1.5.0で提供されます。2026-08-08にプルリクエスト728でdevへマージされました。 プルリクエスト728, issue 648
- 実機検証
- RTX 5070 Ti上のDeepStream 8.0.0、検出のみ、2026-08-08
提供状況
DeepStreamエクスポートはv1.5.0で提供されます。2026-08-08にプルリクエスト728で
devへマージされたため、最新のインストールにはすでに含まれており、ブランチを
固定する必要はありません。
pip install "libreyolo[onnx]"2026-08-08より前にdeepstream-exportブランチをクローンしている場合は、取り直して
ください。このブランチはリベースして強制プッシュされており、古い履歴にはこれらの
エクスポートをCUDAマシンで動かすための修正が入っていません。
エクスポートが出力するもの
model.export(format="onnx", deepstream=True)は3つのファイルを並べて出力します。
libreyolo9s.ptの場合は次のとおりです。
libreyolo9s.onnx。検出グラフで、出力テンソルは(batch, num_detections, 6)の 形状が1つ、各行は[x1, y1, x2, y2, score, class_id]で、ネットワーク入力の ピクセル座標です。config_infer_primary_libreyolo9s.txt。ファミリーの前処理定数、クラス数、 しきい値、パーサーの接続設定を持つnvinferの設定ファイルです。libreyolo9s_labels.txt。1行に1つのクラス名です。
ラベルファイルは、チェックポイントがクラス名を持っている場合に出力されます。深度
推定モデルはクラス名を持たないため、ファイルもlabelfile-pathキーも出力されません。
LibreYOLOは.soを出力しません。DeepStreamが読み込む.soは
marcoslucianops/DeepStream-Yoloのバウンディングボックスパーサーで、デバイスごとに
1回コンパイルすればよく、どのLibreYOLO検出器に向けても同じバイナリです。モデルに
あたるのがONNXです。nvinferが自前で後処理を行うため、分類とセマンティック
セグメンテーションはパーサーをまったく必要としません。
モデルをエクスポートする
from libreyolo import LibreYOLO9, LibreDFINE # libreyolo9s.onnx、config_infer_primary_libreyolo9s.txt、# libreyolo9s_labels.txt を作業ディレクトリに出力LibreYOLO9("libreyolo9s.pt", size="s").export(format="onnx", deepstream=True) # 検出モデルはそれぞれ別のディレクトリに置く。どの検出用設定も# 同じエンジンキャッシュファイル名を指すため。「よくある落とし穴」を参照LibreDFINE("LibreDFINEs.pt", size="s").export(format="onnx", deepstream=True)model.export( format="onnx", # deepstream=True は他のどの形式でも拒否される deepstream=True, conf=0.25, # pre-cluster-threshold の初期値(該当タスクでは # classifier-threshold、segmentation-threshold も) iou=0.45, # nms-iou-threshold の初期値、cluster-mode=4 では省略 batch=1, # batch-size とエンジンキャッシュのファイル名の初期値 half=False, # True で設定に network-mode=2 を記録(fp16 ビルド) int8=False, # True で設定に network-mode=1 を記録 dynamic=True, # ONNX グラフの動的バッチ軸 imgsz=640, # infer-dims=3;H;W の初期値) # deepstream=True と nms=True は排他。DeepStream はクラスタリング段階で# 抑制を行うため、グラフには何も埋め込まれないcurl -L -o LibreDFINEs.pt \ https://huggingface.co/LibreYOLO/LibreDFINEs/resolve/main/LibreDFINEs.ptLibreDFINE._load_weightsは、ファイルがまだディスク上にない場合にダウンロードを
試みずにFileNotFoundErrorを送出するため、先にLibreDFINEs.ptを自分で取得して
ください。この不足は
issue #727として追跡されて
います。YOLO9の重みは初回使用時にダウンロードされます。
このフラグはPythonのみです。このブランチのlibreyolo exportにdeepstream
オプションはなく、CLIは未知のキーをそのまま渡すのではなく、固定のリストから
エクスポート引数を組み立てます。
バウンディングボックスパーサーをビルドする
検出にはパーサーライブラリが必要で、インスタンスセグメンテーションには別のものが 必要、残りのタスクには不要です。DeepStream 8.0イメージには、ドキュメントに載って いるビルドコマンドを壊す点が2つあり、どちらもLibreYOLOではなく環境側の問題です。
イメージには/usr/localの下にcuda、cuda-12、cuda-12.5、cuda-12.8、
cuda-12.9が入っています。ツールキットが完全なのはcuda-12.5だけです。また、
libcublas.so.12とlibcublas.so.12.8.4.1は入っていますが、-lcublasが解決先と
するバージョンなしのlibcublas.soはありません。以下のスクリプトはその両方を回避
します。
set -egit clone --depth 1 https://github.com/marcoslucianops/DeepStream-Yolo.git # このイメージの /usr/local/cuda-12 はスタブで、ビルドは# "fatal error: crt/host_defines.h: No such file or directory" で失敗する。# ヘッダーを実際に含むツールキットを解決する。8.0 イメージでは cuda-12.5CUDA_DIR=$(readlink -f /usr/local/cuda)[ -f "$CUDA_DIR/include/crt/host_defines.h" ] || \ CUDA_DIR=$(ls -d /usr/local/cuda-*.* | sort -Vr | \ while read d; do [ -f "$d/include/crt/host_defines.h" ] && echo "$d" && break; done) # イメージには libcublas.so.12 と libcublas.so.12.8.4.1 はあるが、-lcublas が# 必要とするバージョンなしの libcublas.so がないため、リンク段階が# "/usr/bin/ld: cannot find -lcublas" で失敗する。リンカーが求める名前を用意するmkdir -p /tmp/cudalibsfor lib in cublas cublasLt cudart; do real=$(find /usr/local -name "lib${lib}.so.1*" | grep -v stubs | sort -V | tail -1) ln -sf "$real" "/tmp/cudalibs/lib${lib}.so"doneexport LIBRARY_PATH="/tmp/cudalibs:$LIBRARY_PATH" make -C DeepStream-Yolo/nvdsinfer_custom_impl_Yolo CUDA_VER="${CUDA_DIR##*/cuda-}"git clone --depth 1 https://github.com/marcoslucianops/DeepStream-Yolo-Seg.gitmake -C DeepStream-Yolo-Seg/nvdsinfer_custom_impl_Yolo_seg \ CUDA_VER="${CUDA_DIR##*/cuda-}"そのうえで、生成された設定のcustom-lib-pathを、ビルドした
libnvdsinfer_custom_impl_Yolo.soに向けてください。生成される値は相対パスの
nvdsinfer_custom_impl_Yolo/libnvdsinfer_custom_impl_Yolo.soで、deepstream-appを
DeepStream-Yoloのチェックアウト先から実行する場合は解決できますが、それ以外では
編集が必要です。
パイプラインを実行する
ほかに時間をかける前に、コンテナーからGPUが見えることを確認してください。これは 検証時に最初に行ったチェックで、WSL2上のBlackwellカードで実施しました。
docker run --rm --gpus all nvcr.io/nvidia/tritonserver:26.04-py3 \ nvidia-smi --query-gpu=name,driver_version,compute_cap --format=csvOutput
name, driver_version, compute_cap NVIDIA GeForce RTX 5070 Ti, 591.86, 12.0
検証では、ファイルソース1つ、表示シンクなし、オンスクリーンディスプレイ有効、
gie-kitti-output-dirを設定して全フレームの検出結果をKITTI形式のテキストとして
ディスクに書き出す構成でdeepstream-appを動かしました。その設定を反映した設定
ファイルは次のとおりです。
[application]enable-perf-measurement=1perf-measurement-interval-sec=5gie-kitti-output-dir=kitti [tiled-display]enable=0 [source0]enable=1type=3uri=file:///opt/nvidia/deepstream/deepstream/samples/streams/sample_1080p_h264.mp4num-sources=1gpu-id=0 [streammux]gpu-id=0batch-size=1batched-push-timeout=40000width=1920height=1080live-source=0 [primary-gie]enable=1gpu-id=0gie-unique-id=1config-file=config_infer_primary_libreyolo9s.txt [osd]enable=1border-width=2text-size=15 [sink0]enable=1type=1sync=0 [tests]file-loop=0deepstream-app -c deepstream_app_config.txtOutput
App run successful
docker run --rm --gpus all -v "$PWD:/work" -w /work \ nvcr.io/nvidia/deepstream:8.0-samples-multiarch \ bash -c "bash build_parser.sh && deepstream-app -c deepstream_app_config.txt"nvinferは初回実行時にONNXからTensorRTエンジンをビルドしてモデルの隣にキャッシュ
するため、初回はエンジンのビルド分の時間がかかり、2回目以降はキャッシュを読み込み
ます。
生成される設定ファイル
以下の2つの設定はいずれも検証時にエクスポーターが生成したもので、あとから編集して いません。
| キー | YOLO9-s | D-FINE-s |
|---|---|---|
net-scale-factor | 0.003921568627 | 0.003921568627 |
model-color-format | 0 | 0 |
infer-dims | 3;640;640 | 3;640;640 |
maintain-aspect-ratio | 1 | 0 |
symmetric-padding | 0 | 0 |
network-type | 0 | 0 |
num-detected-classes | 80 | 80 |
cluster-mode | 2 | 4 |
parse-bbox-func-name | NvDsInferParseYolo | NvDsInferParseYolo |
pre-cluster-threshold | 0.25 | 0.25 |
nms-iou-threshold | 0.45 | |
topk | 300 | 300 |
2つの設定が異なるのは3か所、maintain-aspect-ratio、cluster-mode、そして
nms-iou-thresholdがそもそも存在するかどうかです。D-FINEの設定はこのキーを完全に
省いており、これはcluster-mode=4が求める形です。
クラスタリングは本来別々の検出結果を統合してしまうため、物体ごとに最大1つしか
予測を出さないヘッドにはcluster-mode=4が設定され、DeepStreamはそれらに対して
クラスタリングを行いません。対象はrfdetr、dfine、deim、deimv2、ec、
rtdetr、rtdetrv2、rtdetrv4、yolo9_e2eです。グリッドヘッドとアンカーヘッドに
はcluster-mode=2とnms-iou-thresholdが設定されます。
検出用の設定にはさらにengine-create-func-name=NvDsInferYoloCudaEngineGetが入って
おり、エンジンのビルドをパーサーライブラリに委ねます。これによってエンジン
キャッシュのファイル名が固定され、よくある落とし穴で説明する衝突の原因になって
います。
対応するタスクとファミリー
エクスポートできるファミリーとタスクの組み合わせは43通りです。
libreyolo/export/deepstream.pyのdeepstream_supported_tasks()と
deepstream_supported_families(task)は、実行時に同じ一覧を返します。
| タスク | network-type | パーサーライブラリ | ファミリー |
|---|---|---|---|
| 検出 | 0 | DeepStream-Yolo | yolo9, yolo9_p2, yolo9_e2e, yolo1, yolo2, yolo3, yolo4, yolo7, yolox, yolonas, rtmdet, picodet, rfdetr, dfine, deim, deimv2, ec, rtdetr, rtdetrv2, rtdetrv4 |
| 分類 | 1 | 不要 | mobilenetv4, convnext, efficientnetv2, resnet, dinov2 |
| セマンティックセグメンテーション | 2 | 不要 | pidnet, eomt, dinov2, lingbotvision |
| インスタンスセグメンテーション | 3 | DeepStream-Yolo-Seg | rfdetr, dfine, ec |
| 姿勢推定 | 100 | 不要 | yolo9, yolonas, rfdetr, ec |
| 深度推定 | 100 | 不要 | depth_anything, zipdepth |
| 復元 | 100 | 不要 | nafnet, realesrgan, swinir |
| マッティング | 100 | 不要 | birefnet |
| 視線推定 | 100 | 不要 | l2cs |
network-type=100は、そのタスク向けの後処理器をDeepStreamが持たないことを意味
します。これらの設定はoutput-tensor-meta=1を指定し、グラフ本来の出力はそのまま
素通りして、アプリケーション側がテンソルのメタデータからデコードします。複数出力の
グラフでも問題ありません:すべての出力レイヤーが、通常のONNXエクスポートと同じ
出力名と動的軸のままメタデータに届きます。
インスタンスセグメンテーションの行は、検出行のあとにそのインスタンスのマスクが
続く形で、マスクはsegパーサーがハードコードしている解像度(netH / 4, netW / 4)に
平坦化され、segmentation-threshold用の確率として並びます。
分類と視線推定はセカンダリ推論として動作します。検出器の後段に分類器を置くには、
生成された設定でprocess-mode=2とoperate-on-gie-idを指定してください。視線推定
はヘッドのみの契約で、入力は顔のクロップ1枚ずつになるため、前段に顔検出器が必要
です。
3つのファミリーは意図的に外してあります。segformerは共通のセマンティック
エクスポート契約につながっておらず、どの形式でもONNXへエクスポートできません。
RTMDet-InsとYOLO9は、インスタンスセグメンテーションのエクスポートがLibreYOLO側で
ブロックされています。depth_anything3にはエクスポートの実装がありません。
表の2行にはチェックポイントの不足が隠れています。EoMTのセマンティック
チェックポイントはlだけが公開されており、DINOv2の分類は公開チェックポイントが
まったくないため、この組み合わせには自分でファインチューニングした重みが必要です。
前処理の違い
nvinferはチャンネルごとにnet-scale-factor * (x - offsets)をスカラーのスケール
で計算するため、チャンネルごとの標準偏差を表現できません。それが必要なファミリー
(rfdetr、ec、DINOバックボーンのdeimv2サイズ、rtmdet、picodet、および
すべての分類ファミリー)は、正規化をエクスポートしたグラフに焼き込んであり、生成
された設定はそれに合う生の入力空間をグラフへ渡します。
LibreYOLO自身のPythonパイプラインとnvinferが依然として食い違うのは、ジオメトリ
の部分です。
- レターボックス方式のファミリー(
yolo9、yolox、yolonas、rtmdet、yolo2、yolo3、yolo4、yolo7)は、本来グレーでパディングします。nvinferは黒で パディングします。 yolonasの検出は、本来640のキャンバス内で長辺を636にリサイズします。nvinferのmaintain-aspect-ratioは640をそのまま使います。- 分類は本来、短辺をリサイズしてから中央でクロップします。
nvinferはフレーム または物体のROIをネットワーク入力へ引き伸ばすため、被写体が詰まったクロップでは 結果が変わります。 - EoMTは本来、セマンティックセグメンテーションでスライディングウィンドウのタイル 処理を行います。エクスポートしたグラフは引き伸ばした単一のキャンバスで、高速です が精度は下がります。
pidnetは入力解像度の1/8、lingbotvisionは1/16でクラスマップを出力します。 DeepStreamは表示のためにクラスマップをアップサンプリングします。
ONNXの一致検証は前処理済みのテンソルを入力するため、グラフの出力は確認できますが、 設定内の誤ったカラー順序やパディング方針は捕まえられません。厳密な一致が要る ワークロードをデプロイする前に、自分のデータで検証してください。
よくある落とし穴
同じディレクトリの2つの検出モデルが互いのエンジンを読み込む
どの検出用設定にも同じ行が入っています。
model-engine-file=model_b1_gpu0_fp32.engineパーサーのエンジンビルダーはこのベース名を要求し、モデルによって変わりません。 2つ目の検出モデルを同じディレクトリにエクスポートすると、2回目の実行では1つ目の モデルのキャッシュ済みエンジンが読み込まれます。クラッシュはせず、ボックスが誤る だけです。検出モデルにはそれぞれ専用のディレクトリを用意してください。検証時も、 D-FINEを別のディレクトリに隔離してはじめてテストできました。
1つのボックスは1つのクラスしか持てない
nvinferの行フォーマットは[x1, y1, x2, y2, score, class_id]で、1ボックスにつき
1クラスのため、エクスポートではクラススコアをargmaxに畳み込みます。predictが
2つのクラスで報告するボックスは、片方のクラスだけが残ります。実測例として、
LibreYOLOは同じボックスにvase 0.773とbottle 0.383を報告しますが、DeepStreamの
グラフはvaseを残します。これはパーサーの行フォーマットから生じるもので、その契約
を外れない限り変えられないため、リグレッションではなく想定どおりの動作です。
検証済み
deepstream-appは、NVIDIA同梱のsample_1080p_h264.mp4(1443フレーム)に対して
フレームごとのKITTI出力を有効にした状態で、どちらの検出ヘッド型でもEOSまで走り、
App run successfulを出しました。
| YOLO9-s | D-FINE-s | |
|---|---|---|
| ヘッド型 | grid | one-to-one |
cluster-mode | 2 | 4 |
maintain-aspect-ratio | 1 | 0 |
| 検出のあったフレーム数 | 1443 | 1443 |
| 総検出数 | 18031 | 71105 |
1443フレーム全体のクラスヒストグラムは、どちらのモデルでも1位が車、2位が人で、
街の風景として妥当です。検出数の4倍の開きはcluster-modeの違いが働いた結果です:
cluster-mode=4のD-FINEはクラスタリングを行わないため、しきい値を超えたクエリは
ほぼ重複しているものも含めてすべて残ります。
独立に学習された2つのモデルが、主要な物体を同じ位置に置いています。
YOLO9 bus [706.72, 0.82, 1916.34, 1062.97] conf 0.965
D-FINE bus [702.73, 2.93, 1916.24, 1069.32] conf 0.965この実行で5点が確認できました:sm_120でTensorRTがエクスポートしたONNXから
エンジンをビルドすること、nvinferが生成された設定のすべてのキーを受け付けること、
NvDsInferParseYoloがテンソルのレイアウトを正しく読むこと、ボックスがソース解像度
である1920x1080の座標に収まること、そしてラベルが生成されたラベルファイルで解決
されることです。
実行環境は次のとおりです。
| 構成要素 | 値 |
|---|---|
| ホストOS | Windows 11 Pro 26200 |
| GPU | NVIDIA GeForce RTX 5070 Ti、16 GB |
| ドライバー | 591.86 |
| Compute Capability | 12.0(Blackwell、sm_120) |
| コンテナーランタイム | Docker Desktop 29.4.3、WSL2バックエンド |
| DeepStreamイメージ | nvcr.io/nvidia/deepstream:8.0-samples-multiarch |
| DeepStreamバージョン | 8.0.0 |
| コンテナーのCUDA | 12.8.1 |
| パーサー | marcoslucianops/DeepStream-YoloのHEAD |
パイプラインの実行と併せて、tests/unit/test_deepstream_export.pyがグラフ
アダプターと生成される設定キーをカバーしており、その35件のテストはこのコミットで
通過します。
未検証
上記の範囲を実際より広く読まれないよう、明記しておきます。
- Jetsonとaarch64。エクスポートの契約はアーキテクチャに依存しませんが、パイプライン はx86のディスクリートGPUでしか実行していません。
- 43通りのうち41通り。DeepStreamを通したのは
yolo9の検出とdfineの検出だけです。 分類、セマンティックセグメンテーション、インスタンスセグメンテーション、および 生テンソルのタスクは、パイプラインの実行ではなくユニットテストとONNXの一致 チェックでカバーしています。 - FP16とINT8。動かしたのは
network-mode=0だけです。 - マルチストリームとバッチ処理。ソースは1つ、
batch-size=1です。 - 正解データ(ground truth)のデータセットに対する精度。検出結果は意味的な妥当性と モデル間の一致を確認しただけで、DeepStreamを通してmAPとしてスコア化しては いません。