# Accelerate multi-streaming cameras with DeepStream and deploy custom (YOLO) models<br>使用DeepStream加速多串流攝影機並部署客製(YOLO)模型 ###### tags: `Edge AI` `DeepStream` `Edge_AI` `deployment` `Nvidia` `Jetson` ![](https://i.imgur.com/S5QFcnE.png =800x) ## NVIDIA Jetson 平台部署相關筆記 ### 基本環境設定 - [Jetson AGX Xavier 系統環境設定1_在windows10環境連接與安裝](https://hackmd.io/@YungHuiHsu/HJ2lcU4Rj) - [Jetson AGX Xavier 系統環境設定2_Docker安裝或從源程式碼編譯](https://hackmd.io/k-lnDTxVQDWo_V13WEnfOg) - [NVIDIA Container Toolkit 安裝筆記](https://hackmd.io/wADvyemZRDOeEduJXA9X7g) - [Jetson 邊緣裝置查詢系統性能指令jtop](https://hackmd.io/VXXV3T5GRIKi6ap8SkR-tg) - [Jetson Network Setup 網路設定](https://hackmd.io/WiqAB7pLSpm2863N2ISGXQ) - [OpenCV turns on cuda acceleration in Nvidia Jetson platform<br>OpenCV在Nvidia Jetson平台開啟cuda加速](https://hackmd.io/6IloyiWMQ_qbIpIE_c_1GA) ### 模型部署與加速 - [[Object Detection_YOLO] YOLOv7 論文筆記](https://hackmd.io/xhLeIsoSToW0jL61QRWDcQ) - [Deploy YOLOv7 on Nvidia Jetson](https://hackmd.io/kZftj6AgQmWJsbXsswIwEQ) - [Convert PyTorch model to TensorRT for 3-6x speedup<br>將PyTorch模型轉換為TensorRT,實現3-8倍加速](https://hackmd.io/_oaJhYNqTvyL_h01X1Fdmw?both) - [Accelerate multi-streaming cameras with DeepStream and deploy custom (YOLO) models<br>使用DeepStream加速多串流攝影機並部署客製(YOLO)模型](https://hackmd.io/@YungHuiHsu/rJKx-tv4h) - [Use Deepstream python API to extract the model output tensor and customize model post-processing (e.g., YOLO-Pose)<br>使用Deepstream python API提取模型輸出張量並定製模型后處理(如:YOLO-Pose)](https://hackmd.io/@YungHuiHsu/rk41ISKY2) - [Model Quantization Note 模型量化筆記](https://hackmd.io/riYLcrp1RuKHpVI22oEAXA) --- ### yolov7 with multiple cameras running on DeepStream {%youtube 5BVrPbOyNHE%} --- ## Github 開箱即用 - [github/deepstream-yolov7](https://github.com/YunghuiHsu/deepstream_python_apps/tree/master/apps/deepstream-rtsp-in-rtsp-out) - [github/deepstream-yolo-pose](https://github.com/YunghuiHsu/deepstream-yolo-pose) ![](https://github.com/YunghuiHsu/deepstream-yolo-pose/blob/main/imgs/Multistream_4_YOLOv8s-pose-3.PNG?raw=true =600x) --- ## 簡介 > Nvidia DeepStream是一個基於GStreamer的流分析工具包,可用於多傳感器處理、視頻、音頻和圖像理解等多種用途。 目標是提供一個完整的流處理框架,基於AI和計算機視覺技術,以實現對傳感器數據的即時處理和分析 > - 詳見官方介紹[DeepStream SDK | NVIDIA Developer](https://developer.nvidia.com/deepstream-sdk) 白話來說,DeepStream是Nvidia是作為AI部屬平台的加速工具,主要在串接模型資料傳輸處理與協定上進行加速,特別是在多媒體串流處理上更有使用的必要性 ![](https://hackmd.io/_uploads/r19WxcuE2.png =800x) ![](https://hackmd.io/_uploads/B1dXg9_V3.png =800x) ### DeepStream 應用架構解析 [DeepStream Reference Application - deepstream-app](https://docs.nvidia.com/metropolis/deepstream/dev-guide/text/DS_ref_app_deepstream.html#application-architecture) #### DeepStream組件 ![](https://hackmd.io/_uploads/r1BI7wuB2.png =800x) Nvidia DeepStream是一個人工智能框架,有助於利用Jetson和GPU設備中的Nvidia GPU的最終潛力來實現計算機視覺。它為Jetson Nano等邊緣設備和Jetson系列的其他設備提供動力,實時處理邊緣設備上的並行視頻流。 DeepStream使用Gstreamer流水線(用C語言編寫)在GPU中獲取輸入視頻,最終以更快的速度處理它,以便進一步處理。 - DeepStream的組成部分 DeepStream有一個基於插件的架構。基於圖形的管道接口允許高層組件互連。它可以在GPU和CPU上使用多線程進行異質並行處理(heterogeneous parallel processing)。 - 下面是DeepStream的主要組件和它們的高級功能 | 名稱 | 說明 | | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | 元數據<br>Meta Dat | 它是由圖形生成的,在圖形的每個階段都會生成。利用它,我們可以得到許多重要的字段,如檢測到的物體類型、ROI坐標、物體分類、來源等。 | | 解碼器<br>Decoder | 解碼器有助於對輸入影片(H.264和H.265)進行解碼。它支持多數據流(multi-stream)同時解碼。它將位元深度(Bit depth)和分辨率(Resolution)作為參數。 | | 影片聚合器<br>Video Aggregator<br>(nvstreammux) | 它有助於接受n個輸入流並將其轉換為連續的批量幀(sequential batch frames)。它使用低級別的API來訪問GPU和CPU來完成這個過程。 | | 推理 <br>Inferencing<br>(nvinfer) | 這是用來獲得所使用的模型的推斷。所有與模型相關的工作都是通過nvinfer完成的。它還支持一級和二級模式以及各種聚類方法。 | | 格式轉換和縮放<br>Format Conversion and Scaling<br>(nvvidconv) | 它將格式從YUV轉換為RGBA/BRGA,縮放分辨率並做圖像旋轉部分。 | | 對象追蹤器<br>Object Tracker<br>(nvtracker) | 它使用CUDA並基於KLT參考實現。我們也可以用其他追蹤器來替換默認的追蹤器。 | | 屏幕追蹤器<br>Screen Tiler<br>(nvstreamtiler) | 它管理輸出的視頻,即相當於open cv的imshow函數。 | | 屏幕顯示<br>On Screen Display<br>(nvdosd) | 它管理屏幕上所有的可畫性,比如畫線、邊界框、圓圈、ROI等。 | | 訊息轉換器和代理器<br>(nvmsgconv + nvmsgbroker) | 組合在一起,將分析資料發送到雲中的伺服器 | (source : [Nvidia DeepStream – A Simplistic Guide](https://www.datatobiz.com/blog/nvidia-deepstream-guide/)) - Nvidia DeepStream的執行流程 Decoder -> Muxer -> Inference -> Tracker (if any) -> Tiler -> Format Conversion -> On Screen Display -> Sink DeepStream應用程序由兩部分組成,一部分是配置文件,另一部分是其驅動文件(可以是C語言或Python語言)。 在設定檔config.txt相關的控制項的說明在[`Configuration Groups`](https://docs.nvidia.com/metropolis/deepstream/dev-guide/text/DS_ref_app_deepstream.html#deepstream-reference-application-deepstream-app)文件內 #### 使用Gstreamer 命令列檢視組件功能 這邊需要先有Gstreamer的基本概念才看得懂:slightly_smiling_face: - [GStreamer 簡介與筆記](https://hackmd.io/@YungHuiHsu/ryhRTZpt3) 安裝完成gstreamer後,執行`gst-inspect-1.0`指令檢視模組功能,以`nvinfer`模組為例 ``` gst-inspect-1.0 nvinfer ``` ##### `nvinfer` 插件的屬性定義與功能 ![](https://hackmd.io/_uploads/rJPdndRYh.png =800x) ###### `Pad Templates` and `Pads` (數據傳輸接口) - 可以見到有`src`輸出(數據生產)與`sink`輸入(數據消費)的端點 - 支援的影片格式為`video/x-raw(memory:NVMM)` 預設使用NVIDIA的GPU處理 - 可選格式為 `NV12` 與`RGBA` - 這邊可見輸入模型預設的通道為`RGBA` ![](https://hackmd.io/_uploads/SyRghORYn.png =400x)   ###### `Element Properties` (元素屬性) 可以見到有許多參數的設定說明,這也是後面config file中設定的參數項目 ![](https://hackmd.io/_uploads/rJr96_0Fn.png =800x) - 也可在code中取得或進行設定 - 取得 `.get_property()` - 設定 `.set_property()` ``` # create "nvinfer" element pgie = Gst.ElementFactory.make("nvinfer", "primary-inference") # get "batch size" pgie.get_property("batch-size") # set "config file" pgie.set_property("config-file-path", config_file_path) ``` ## Python API(binding)使用入門 - [deepstream-get-started-with-python](https://resources.nvidia.com/en-us-deepstream-get-started-with-python/ds-python-sample-app) 以下主要參考官方提供的DeepStream Python Apps[NVIDIA-AI-IOT/deepstream_python_apps](https://github.com/NVIDIA-AI-IOT/deepstream_python_apps)的範例,內有詳細執行範例 - DeepStream python biding 示意圖 ![](https://hackmd.io/_uploads/ByPrf9OEn.png) ### 環境安裝 按[NVIDIA-AI-IOT/deepstream_python_apps/blob/master/bindings/README.md](https://github.com/NVIDIA-AI-IOT/deepstream_python_apps/blob/master/bindings/README.md)文件指引 :::spoiler #### 補充 執行到 步驟1.3 deepstream_python_apps倉儲安裝 按以下指示安裝 當執行到 "1.3 Initialization of submodules"時 要將"deepstream_python_apps"的倉儲clone到DeepStream根目錄/source目錄下`<DeepStream 6.2 ROOT>/sources`: ~~我這邊安裝的是deepstream 6.2版,可以用 `dpkg -L deepstream-6.2`指令查找安裝位置,按Linux系統慣例果然在/opt/之下,我查到的安裝位置是:`/opt/nvidia/deepstream/deepstream-6.2/`~~ ``` cd /opt/nvidia/deepstream/deepstream/sources git clone https://github.com/NVIDIA-AI-IOT/deepstream_python_apps ``` #### 補充 執行步驟2 Compiling the bindings時出現錯誤 按照官方指示執行到2.1 Quick build (x86-ubuntu-20.04 | python 3.8 | Deepstream 6.2)時 ``` cd deepstream_python_apps/bindings mkdir build cd build cmake .. make ``` 按照官方預設指令先執行`make ..`,再接在執行`make`以後,會在`/opt/nvidia/deepstream/deepstream/sources/deepstream_python_apps/bindings/build`下產出 'dist/pyds-1.1.6-py3-none-linux_x86_64.whl' 檔案,後面接著執行 ``` pip3 install ./pyds-1.1.6-py3-none*.whl ``` 會出現錯誤訊息"ERROR: pyds-1.1.6-py3-none-linux_x86_64.whl is not a supported wheel on this platform." - 錯誤原因分析 - 因為預設預設平台環境錯誤 > x86_64 is for dGPU, please add -DPIP_PLATFORM=linux_aarch64 when executing cmake. > by [nvidia開發者論壇](https://forums.developer.nvidia.com/t/error-pyds-1-1-0-py3-none-linux-x86-64-whl-is-not-a-supported-wheel-on-this-platform/248952) - 解決方案 - 執行cmake指令時,後面需指定運作的硬體環境,指令應該為 `cmake .. -DPIP_PLATFORM=linux_aarch64 ` ::: --- ### 範例 - [範例檔案使用說明](https://docs.nvidia.com/metropolis/deepstream/dev-guide/text/DS_Python_Sample_Apps.html) - 官方文件內有提供各種應用情境範例的.py檔及配置文件,包含串聯不同模型、multistream、結合Triton或直接使用本機TRT直接推論 ![](https://hackmd.io/_uploads/SkGTJ0FVh.png =600x) --- ## 模型轉換 ### 模型轉換流程與格式 - Nvidia官方指引[Using the TensorRT Runtime API](https://docs.nvidia.com/deeplearning/tensorrt/quick-start-guide/index.html#runtime) - 以pytorch > onnx > TensorRT 為例 - model.pt → model.onnx → ==model.engine== - TensorRT格式有 `.engine` 與 `model.trt`兩種 - `.engine`格式 - 是TensorRT模型的序列化表示形式,它存儲了經過優化和編譯的模型和相關參數 - DeepStream 所必需的讀入格式 - `model.trt`格式 - 實際上與.engine文件相同,是.engine文件的另一種後綴名稱,可以互相轉換使用 - DeepStream無法直接使用 - 在python環境中可以透過import `tensorrt`、`pycuda`等模組讀取二進位的.trt檔,對其序列化後使用 - 在DeepStream文件設定中,指定以`model-engine-file`讀取==.engine==參數配置的必要性 :::info 第一次啟用時,DeepStream app時會根據設定文件的配置,載入.onnx檔構建 TensorRT引擎後產出model.engine檔案,此後再指定“model-engine-file=model.engine”啟動就會快很多 - 在 DeepStream 中,如果已經指定了 "onnx-file",那麽指定 "model-engine-file" 的設置通常是可選的。 - "onnx-file" 參數用於指定 ONNX 模型文件的路徑,DeepStream 將根據該文件構建 TensorRT 引擎。這種方式需要在運行時動態地將 ONNX 模型轉換為 TensorRT 引擎。這可能需要一些額外的時間和計算資源來完成模型的轉換和優化,因為在每次運行應用程序時都需要進行這個過程(在AGX Xavier要20-30min)。 當指定 "model-engine-file" 參數時,它用於指定預先構建好的 TensorRT 引擎文件的路徑。預先構建引擎意味著將 ONNX 模型轉換為 TensorRT 引擎的過程已經提前執行,引擎文件已經生成並存儲在磁盤上。在應用程序運行時,DeepStream 將直接加載該引擎文件,而無需再次進行模型轉換和優化的過程。這可以節省啟動時間並加快執行速度。 如果指定了 "model-engine-file",DeepStream 將忽略 "onnx-file" 的設置,而直接加載和使用預先構建的引擎文件 ::: ### YOLOv7模型格式轉換(Onnx → TensorRT Engine) - 參見[NVIDIA-AI-IOT/yolo_deepstream/tensorrt_yolov7](https://github.com/NVIDIA-AI-IOT/yolo_deepstream/tree/main/tensorrt_yolov7) - 官方有提供客製的編譯檔與環境, 編譯環境準備請參考官方指示 #### 模型取得 :::spoiler [NVIDIA-AI-IOT/yolo_deepstream/yolov7_qat](https://github.com/NVIDIA-AI-IOT/yolo_deepstream/tree/main/yolov7_qat) - [NVIDIA-AI-IOT/yolo_deepstream/yolov7_qat](https://github.com/NVIDIA-AI-IOT/yolo_deepstream/tree/main/yolov7_qat)可直接下載量化過後的int8版本 - [x] Quantization Aware Training(QAT-INT8)訓練過的模型版本(`yolov7_qat_640.onnx`) - [Model Quantization Note 模型量化筆記](https://hackmd.io/riYLcrp1RuKHpVI22oEAXA) 下載後下一步要在自己的硬體平台先轉為TensorRT Engine格式 ::: #### 準備 TensorRT engines :::spoiler convert onnx model(.onnx) to TensorRT-engine(.engine ) - 轉換方式參考自[NVIDIA-AI-IOT/yolo_deepstream/ tensorrt_yolov7](https://github.com/NVIDIA-AI-IOT/yolo_deepstream/tree/main/tensorrt_yolov7)) - 由於後面流程要串接多路攝影機,因此我選擇轉為動態批次的設定 ``` # int8 QAT model, the onnx model with Q&DQ nodes /usr/src/tensorrt/bin/trtexec --onnx=yolov7_qat_640.onnx \ --saveEngine=yolov7QAT_640.engine --fp16 --int8 # if you want dynamic_patch for batch inference /usr/src/tensorrt/bin/trtexec --onnx=yolov7_qat_640.onnx \ --minShapes=images:1x3x640x640 \ --optShapes=images:12x3x640x640 \ --maxShapes=images:16x3x640x640 \ --saveEngine=yolov7QAT_640.engine --fp16 --int8 ``` - 如果後面串接的數量固定 - '--minShapes'、'--optShapes'、'--maxShapes'都設為一致,似乎可以開啟NV的[DLA (Deep Learning Accelerator)](https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#dla_topic)加速(待確認) ::: --- ## 使用DeepStream部署YOLO系列模型 主要流程參照NVIDIA官方文件[NVIDIA-AI-IOT/yolo_deepstream](https://github.com/NVIDIA-AI-IOT/yolo_deepstream/tree/main/deepstream_yolo) 另外這篇非官方的導覽也可以參考[marcoslucianops/DeepStream-Yolo/customModels](https://github.com/marcoslucianops/DeepStream-Yolo/blob/master/docs/customModels.md) ### 手動修改DeepStream Python Binding範例文件,並指定使用客製模型(YOLOv7) - 資料配置方式,在`/opt/nvidia/deepstream/deepstream`目錄內 ![](https://hackmd.io/_uploads/HJXX2V_B2.png =400x) 以下分兩部分來說明: #### 1. DeepStream 模型檔案配置與客製模型編譯 這裡採取的作法是把客製模型放在模型專用目錄下,方便未來其他專案重複取用 `samples/models/tao_pretrained_models/yolov7/` - 編譯過程詳見[NVIDIA-AI-IOT/yolo_deepstream/deepstream_yolo](https://github.com/NVIDIA-AI-IOT/yolo_deepstream/tree/main/deepstream_yolo)內的操作指示 :::spoiler - 詳細編譯過程與檔案配置位置 :::spoiler - 編譯與設定文件取得 - 為方便閱讀,這邊將`/opt/nvidia/deepstream/deepstream/samples/models/tao_pretrained_models/yolov7`指定為`path_yolov7`變數,方便之後取用 ``` cd ~/ sudo git clone https://github.com/NVIDIA-AI-IOT/yolo_deepstream.git path_yolov7=/opt/nvidia/deepstream/deepstream/samples/models/tao_pretrained_models/yolov7 sudo mmkdir $path_yolov7 sudo mcp -vr /yolo_deepstream/deepstream_yolo/* $path_yolov7/ ``` 這時可以看到`nvdsinfer_custom_impl_Yolo/`內的資料結構包含3個檔案 ``` nvdsinfer_custom_impl_Yolo/ ├── Makefile # 用於編譯程式碼的。包含了編譯和建構這個自訂推論實現所需的指令和規則。 ├── nvdsparsebbox_Yolo.cpp # C++ 檔案,包含了用於解析和處理YOLO模型的邊界框的程式碼 ├── nvdsparsebbox_Yolo_cuda.cu # CUDA 檔案,包含了在 GPU 上執行的加速程式碼 # 針對 YOLO 模型的邊界框解析進行加速計算的 CUDA 程式碼 # YOLO post-processing(decoce yolo result, not include NMS) ``` 進入`nvdsinfer_custom_impl_Yolo`目錄內開始編譯 ``` cd $path_yolov7/nvdsinfer_custom_impl_Yolo sudo make cd .. ``` 編譯成功後,就會得到1個主要的.so檔及2個編譯好的物件檔案(.o) ``` nvdsinfer_custom_impl_Yolo/ ├── Makefile ├── nvdsparsebbox_Yolo.cpp ├── nvdsparsebbox_Yolo_cuda.cu ├── libnvdsinfer_custom_impl_Yolo.so # 共享程式庫(shared library),用於執行時的動態連結。 # 這個程式庫提供了用於推論(inference)的自訂實現 ├── nvdsparsebbox_Yolo_cuda.o # 已編譯的 CUDA 目標檔案(object file),用於在連結階段 # 將 CUDA 代碼與其他目標檔案一起連結成最終的執行檔案或共享程式庫 └── nvdsparsebbox_Yolo.o # 已編譯的 C++ 目標檔案,用於在連結階段將 C++ 程式碼 # 與其他目標檔案一起連結成最終的執行檔案或共享程式庫 ``` ::: ::: #### 2. DeepStream app(python API)與配置文件 python biding的範例檔位於`/opt/nvidia/deepstream/deepstream/sources/deepstream_python_apps/apps` ##### 2.1 這邊以[`deepstream_test1_rtsp_in_rtsp_out/`](https://github.com/NVIDIA-AI-IOT/deepstream_python_apps/tree/master/apps/deepstream-rtsp-in-rtsp-out)為例 - [修改的檔案下載處](https://github.com/YunghuiHsu/deepstream_python_apps/blob/master/apps/deepstream-rtsp-in-rtsp-out/README) - `deepstream_test1_rtsp_in_rtsp_out_getconf.py` - `dstest1_pgie_inferserver_config.txt` :::spoiler - 資料輸入 - 這個範例中可以使用rtsp(使用`rtsp://`或`file:/`)讀入多種格式的影片檔案(.h264、.mp4、.mov等) - 資料輸出 - 使用rtsp接收 - 本機(直接在server/jetson上檢視) - 在瀏覽器輸入`rtsp://localhost:8554/ds-test` - 遠端連線檢視 - 我這邊使用的方案是VLC Player - 在"媒體/開啟網路"串流的設定中輸入指定的位址`rtsp://<your_server_ip>:8554/ds-test` - 相關設定參考[dusty-nv/jetson-inference/Camera Streaming and Multimedia](https://github.com/dusty-nv/jetson-inference/blob/master/docs/aux-streaming.md) - ![](https://hackmd.io/_uploads/S1ZA3yqS2.png =400x) - 修改[`deepstream_test1_rtsp_in_rtsp_out.py`](https://github.com/NVIDIA-AI-IOT/deepstream_python_apps/blob/master/apps/deepstream-rtsp-in-rtsp-out/deepstream_test1_rtsp_in_rtsp_out.py#305)讀取設定檔的路徑 :::spoiler ```python=305 pgie.set_property("config-file-path", config_file_path) ``` ```python=397 def parse_args(): parser.add_argument("-config", "--config-file", default='dstest1_pgie_config.txt', help="Set the config file path", type=str) args = parser.parse_args() global config_file_path config_file_path = args.config_file ``` ::: ::: ##### 2.2 模型配置文件修改 - 參照[NVIDIA-AI-IOT/yolo_deepstream/deepstream_yoloconfig_infer_primary_yoloV7.txt](https://github.com/NVIDIA-AI-IOT/yolo_deepstream/blob/main/deepstream_yolo/config_infer_primary_yoloV7.txt)的文件配置建立`dstest1_pgie_yolov7_config.txt` :::spoiler pgie_config.txt 客製模型配置文件說明 ![](https://hackmd.io/_uploads/HyiRigcB2.png) - pgie_config.txt 客製模型配置文件說明 以下說明改動部分,主要是跟模型存放路徑有關 - 客製模型檔案路徑 為方便閱讀,這邊以your_model_path 代替完整路徑`/opt/nvidia/deepstream/deepstream/samples/models/tao_pretrained_models/yolov7/` * `onnx-file=your_model_path/yolov7.onnx` * `model-engine-file= your_model_path/yolov7.onnx_b16_gpu0_fp16.engine` * 在第一次載入model.onnx後,DeepStream會動態地將 ONNX 模型轉換為 TensorRT 引擎(AGX Xavier耗時2-30min),然後自動產出已命名好的.engine檔案 * `labelfile-path=your_model_path/labels.txt` - 模型設定 * `batch-size ` * 模型轉為.onnxe格式時記得要開啟動態批次(),詳見前文[TensorRT轉檔啟用動態批次 Dynamic_batch](https://hackmd.io/_oaJhYNqTvyL_h01X1Fdmw?both)說明 * ~~至於在deepstream中該設多大呢? 參考前人經驗並非越大越好,還需要實際測試,但可以先抓跟你要輸入的camera數量接近~~ * 根據官方文件對於效能提升的提示[DeepStream best practices](https://docs.nvidia.com/metropolis/deepstream/dev-guide/text/DS_ref_app_deepstream.html?highlight=batch%20processing#deepstream-best-practices),批次大小設置與輸入源(batch == sources)一致,達到的效能提升效果最好 * `parse-bbox-func-name=NvDsInferParseCustomYoloV7_cuda` # 使用cuda做後處裡 * `custom-lib-path=your_model_path/nvdsinfer_custom_impl_Yolo/libnvdsinfer_custom_impl_Yolo.so` * 這邊放預先`make`編譯好的.so檔 * 詳細編譯過程與檔案配置見"DeepStream 模型檔案配置與編譯"小節 ::: :::spoiler dstest1_pgie_yolov7_config.txt 詳細配置說明 ```plaintext= [property] gpu-id=0 # 使用的GPU設備ID,這裡設定為0 net-scale-factor=0.0039215697906911373 # 圖像預處理的縮放因子,將像素值轉換為0到1之間的浮點數,這個值等於1/255 model-engine-file=../../../../samples/models/tao_pretrained_models/yolov7/yolov7.onnx_b16_gpu0_fp16.engine # 模型引擎文件的路徑和文件名,這是已經編譯好的TensorRT引擎文件 onnx-file=../../../../samples/models/tao_pretrained_models/yolov7/yolov7.onnx # 原始ONNX模型文件的路徑和文件名 labelfile-path=../../../../samples/models//tao_pretrained_models/yolov7/labels.txt # 包含類別標籤的文件路徑和文件名,每行包含一個類別標籤 force-implicit-batch-dim=1 # 強制隱式批次維度為1,用於不支援顯式批次維度的模型 batch-size=1 #batch-size=12 # 模型的批次大小,即一次送入模型推論的圖像數量 # 如果模型格式沒有設定動態批次的話請指定為1 # 批次大小應與輸入的stream數量相等以取得較好的推理效能 ## 0=FP32, 1=INT8, 2=FP16 mode network-mode=2 # 網路模型的運算精度模式,這裡設定為FP16模式 num-detected-classes=80 # 模型能夠檢測的目標類別數量 gie-unique-id=1 # DeepStream GIE (GPU Inference Engine) 的唯一ID network-type=0 # 網路模型的類型,這裡設定為0,表示物件檢測模型 #is-classifier=0 # 是否為分類器模型的標誌,這裡註解掉了,因此不使用分類器模型 ## 1=DBSCAN, 2=NMS, 3=DBSCAN+NMS Hybrid, 4=None(No clustering) cluster-mode=2 # 物件聚類模式的設定,這裡設定為NMS模式 maintain-aspect-ratio=1 # 是否保持圖像的長寬比例,這裡設定為保持比例 symmetric-padding=1 # 是否對圖像進行對稱填充,這裡設定為對稱填充 ## Bilinear Interpolation scaling-filter=1 # 圖像縮放時使用的插值方法,這裡設定為雙線性插值 #parse-bbox-func-name=NvDsInferParseCustomYoloV7 parse-bbox-func-name=NvDsInferParseCustomYoloV7_cuda # 物件檢測結果解析的函數名稱,這裡設定為NvDsInferParseCustomYoloV7_cuda #disable-output-host-copy=0 #disable-output-host-copy=1 # 是否禁用主機複製輸出,這裡註解掉了,因此未禁用複製輸出 custom-lib-path=../../../../samples/models/tao_pretrained_models/yolov7/nvdsinfer_custom_impl_Yolo/libnvdsinfer_custom_impl_Yolo.so # 自定義的物件檢測實現庫的路徑和文件名 #scaling-compute-hw=0 # 圖像縮放計算硬體的設定,這裡註解掉了,因此未設定 ## start from DS6.2 crop-objects-to-roi-boundary=1 # 是否將物件裁剪到ROI邊界,這裡設定為是 [class-attrs-all] #nms-iou-threshold=0.3 #threshold=0.7 nms-iou-threshold=0.65 # 非最大抑制 (NMS) 的IoU閾值,用於去除重疊的檢測框 pre-cluster-threshold=0.25 # 物件聚類前的閾值,用於過濾低置信度的檢測框 topk=300 # 每張圖像最多保留的檢測框數量 ``` ::: --- ## Case Study [deepstream sdk-api](https://docs.nvidia.com/metropolis/deepstream/dev-guide/sdk-api/struct\_\_NvDsUserMeta.html) ### 範例 :讓螢幕畫面的類別標籤展示信任分數confidence scores 完整的程式碼放在[YunghuiHsu/deepstream_python_apps/apps/deepstream-rtsp-in-rtsp-out](https://github.com/YunghuiHsu/deepstream_python_apps/blob/master/apps/deepstream-rtsp-in-rtsp-out) 流程如示意圖,主要在Metadata進入`nvdosd`物件(負責螢幕顯示工作)前加入Probe,告知`nvdosd`該如何顯示想要的資訊,螢幕追蹤資訊要如何呈現,則在`def tiler_src_pad_buffer_probe()`中定義 ![](https://hackmd.io/_uploads/HJqrgC6Hn.png =500x) ![](https://hackmd.io/_uploads/rJKax0pSh.png =400x) #### 取出並顯示信賴分數(Confidence score) ##### 在`def tiler_src_pad_buffer_probe()`中撈出meta資料 :::spoiler - `obj_meta.text_params.display_text` : 顯示meta資料文字 - `obj_meta.confidence` : meta物件資料中預定義提取信賴分數的關鍵字 - 當在設定檔中指定為偵測模型時,會解析偵測模型類別的meta物件格式 - [`deepstream_test1_rtsp_in_rtsp_out_getconf.py`](https://github.com/YunghuiHsu/deepstream_python_apps/blob/master/apps/deepstream-rtsp-in-rtsp-out/deepstream_test1_rtsp_in_rtsp_out_getconf.py#L112) ```python= def tiler_src_pad_buffer_probe(pad, info, u_data): batch_meta = pyds.gst_buffer_get_nvds_batch_meta(hash(gst_buffer)) l_frame = batch_meta.frame_meta_list while l_frame is not None: try: frame_meta = pyds.NvDsFrameMeta.cast(l_frame.data) except StopIteration: break l_obj = frame_meta.obj_meta_list while l_obj is not None: # Casting l_obj.data to pyds.NvDsObjectMeta obj_meta = pyds.NvDsObjectMeta.cast(l_obj.data) msg = f"{class_names[obj_meta.class_id]:s}" msg += f" {obj_meta.confidence:3.2f}" obj_meta.text_params.display_text = msg ``` 接下來還需要加入探針才能更新要顯示的資訊 ::: ##### 在`nvdosd`物件接口前加入探針(probe)更新要顯示的資訊 :::spoiler - [`deepstream_test1_rtsp_in_rtsp_out_getconf.py`](https://github.com/YunghuiHsu/deepstream_python_apps/blob/master/apps/deepstream-rtsp-in-rtsp-out/deepstream_test1_rtsp_in_rtsp_out_getconf.py#L411) ```python= # Add probe to get informed of the meta data generated, we add probe to # the sink pad of the osd element, since by that time, the buffer would have # had got all the metadata. # either nvosd.get_static_pad("sink") or pgie.get_static_pad("src") works osdsinkpad = nvosd.get_static_pad("sink") if not osdsinkpad. sys.stderr.write(" Unable to get sink pad of nvosd \n") osdsinkpad.add_probe(Gst.PadProbeType.BUFFER, tiler_src_pad_buffer_probe, 0) ``` - 執行範例 ``` python3 deepstream_test1_rtsp_in_rtsp_out_getconf.py \ -i file:///opt/nvidia/deepstream/deepstream/samples/streams/sample_720p.h264 \ file:///opt/nvidia/deepstream/deepstream/samples/streams/sample_qHD.mp4 \ -config dstest1_pgie_yolov7_config.txt ``` - Reference for display confidence scores - [How to include YOLOv4 confidence score in the deepstream-app output?](https://forums.developer.nvidia.com/t/how-to-include-yolov4-confidence-score-in-the-deepstream-app-output/214613) - [https://forums.developer.nvidia.com/t/how-to-display-confidence-with-label-in-deepstream-like-person-0-81/199878/9](https://forums.developer.nvidia.com/t/how-to-display-confidence-with-label-in-deepstream-like-person-0-81/199878/9) ::: ### 自定義傳遞訊息資料 #### 使用NvDsEventMsgMeta物件 - 可用於跟server間溝通傳遞訊息 <div style="text-align: center;"> <figure> <img src="https://docs.nvidia.com/metropolis/deepstream/dev-guide/sdk-api/structNvDsEventMsgMeta__coll__graph.png" alt="structNvDsEventMsgMeta__coll__graph.png" width="8900"> <figcaption>structNvDsEventMsgMeta</figcaption> </figure> </div> ::: spoiler NvDsEventMsgMeta 資料結構中有pose的定義 - `/opt/nvidia/deepstream/deepstream-6.2/sources/includes/nvdsmeta\_schema.h` ```c++= /** * Holds event message meta data. * * You can attach various types of objects (vehicle, person, face, etc.) * to an event by setting a pointer to the object in @a extMsg. * * Similarly, you can attach a custom object to an event by setting a pointer to the object in @a extMsg. * A custom object must be handled by the metadata parsing module accordingly. */ typedef struct NvDsEventMsgMeta { /** Holds the event's type. */ NvDsEventType type; /** Holds the object's type. */ NvDsObjectType objType; /** Holds the object's bounding box. */ NvDsRect bbox; /** Holds the object's geolocation. */ NvDsGeoLocation location; /** Holds the object's coordinates. */ NvDsCoordinate coordinate; /** Holds the object's signature. */ NvDsObjectSignature objSignature; /** Holds the object's class ID. */ gint objClassId; /** Holds the ID of the sensor that generated the event. */ gint sensorId; /** Holds the ID of the analytics module that generated the event. */ gint moduleId; /** Holds the ID of the place related to the object. */ gint placeId; /** Holds the ID of the component (plugin) that generated this event. */ gint componentId; /** Holds the video frame ID of this event. */ gint frameId; /** Holds the confidence level of the inference. */ gdouble confidence; /** Holds the object's tracking ID. */ guint64 trackingId; /** Holds a pointer to the generated event's timestamp. */ gchar *ts; /** Holds a pointer to the detected or inferred object's ID. */ gchar *objectId; /** Holds a pointer to a string containing the sensor's identity. */ gchar *sensorStr; /** Holds a pointer to a string containing other attributes associated with the object. */ gchar *otherAttrs; /** Holds a pointer to the name of the video file. */ gchar *videoPath; /** Holds a pointer to event message meta data. This can be used to hold data that can't be accommodated in the existing fields, or an associated object (representing a vehicle, person, face, etc.). */ gpointer extMsg; /** Holds the size of the custom object at @a extMsg. */ guint extMsgSize; /** Holds the object's pose information */ NvDsJoints pose; /** Holds the object's embedding information */ NvDsEmbedding embedding; } NvDsEventMsgMeta; ``` ::: ### 自定義修改及撈取MetaData 關於客製meta data存放、修改與撈出 #### 使用自定義 NvDsUserMeta物件 <div style="text-align: center;"> <figure> <img src="https://docs.nvidia.com/metropolis/deepstream/dev-guide/sdk-api/struct__NvDsUserMeta__coll__graph.png" alt="struct__NvDsUserMeta__coll__graph.png" width="500"> <figcaption>NvDsUserMeta</figcaption> </figure> </div> #### NvDsInferTensorMeta 在`[Gst-nvinfer`\]階段,可直接從TensorRT inference engine讀取原始(預測的)輸出張量,轉為meta格式 <div style="text-align: center;"> <figure> <img src="https://docs.nvidia.com/metropolis/deepstream/dev-guide/sdk-api/structNvDsInferTensorMeta__coll__graph.png" alt="structNvDsInferTensorMeta__coll__graph.png" width="300"> <figcaption>NvDsInferTensorMeta</figcaption> </figure> </div> ### 從Deepstream Buffer中取出影像與meta資料進行客製操作 * python biding相關範例見[apps/deepstream-imagedata-multistream](https://github.com/NVIDIA-AI-IOT/deepstream_python_apps/tree/master/apps/deepstream-imagedata-multistream) 從流程圖中可見,分別從FRAME BUFFER與<INFERENCE>模塊撈出影像與模型預測的張量 <div style="text-align: center;"> <figure> <img src="https://github.com/NVIDIA-AI-IOT/deepstream_python_apps/blob/master/apps/deepstream-imagedata-multistream-redaction/imagedata-app-block-diagram.png?raw=true" alt="imagedata-app-block-diagram.png" width="800"> <figcaption>imagedata diagram</figcaption> </figure> </div> - 更詳細解說見 [Use Deepstream python API to extract the model output tensor and customize model post-processing (e.g., YOLO-Pose)<br>使用Deepstream python API提取模型輸出張量並定製模型后處理(如:YOLO-Pose)](https://hackmd.io/@YungHuiHsu/rk41ISKY2) #### NvBufSurface NvBufSurface 是 NVIDIA DeepStream SDK 中的一個結構,用於表示經過解碼和處理的視訊幀的圖像資料 <div style="text-align: center;"> <figure> <img src="https://docs.nvidia.com/metropolis/deepstream/sdk-api/structNvBufSurface__coll__graph.png" alt="structNvBufSurface__coll__graph.png" width="500"> <figcaption>structNvBufSurface</figcaption> </figure> </div> :::spoiler NvBufSurface 的功能如下: * 維護視訊幀的元數據,如圖像的寬度、高度、畫素格式等。 * 提供了對視訊幀數據的訪問和操作接口,如讀取和寫入像素值、設置和獲取 ROI(Region of Interest)等。 * 支持對視訊幀數據進行硬件加速處理,如 GPU 轉換和編碼等。 ::: ##### code 範例(c++) :::spoiler code 範例(c++) ```c++ #include <nvbufsurftransform.h> void process_nvbufsurface(NvBufSurface *surface) { // 獲取視訊幀的元數據 int width = surface->surfaceList[0].width; int height = surface->surfaceList[0].height; int pitch = surface->surfaceList[0].pitch[0]; NvBufColorFormat colorFormat = surface->surfaceList[0].colorFormat; // 設置 ROI NvBufSurfTransformRect src_rect; src_rect.top = 0; src_rect.left = 0; src_rect.width = width; src_rect.height = height; NvBufSurfTransformRect dst_rect = src_rect; // 讀取像素值 unsigned char *buffer = surface->surfaceList[0].mappedAddr.addr[0]; for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { unsigned char pixel_value = buffer[row * pitch + col]; // 對像素值進行處理 // ... } } // 寫入像素值 for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { buffer[row * pitch + col] = 255; // 將所有像素設置為白色 } } // 釋放視訊幀資源 NvBufSurfaceParams params; memset(&params, 0, sizeof(params)); params.gpuId = surface->surfaceList[0].gpuId; params.width = width; params.height = height; params.pitch = pitch; params.colorFormat = colorFormat; params.nvbuf_tag = surface->surfaceList[0].nvbuf_tag; nvbufsurface_dma_unmap(&surface->surfaceList[0], -1, -1); nvbufsurface_free(surface); } ``` ::: --- ## DeepStream效能優化的基本原則 :::spoiler ### DeepStream best practices 以下羅列幾項基本設置,更多請參考官方文件對於效能提升的提示[DeepStream best practices](https://docs.nvidia.com/metropolis/deepstream/dev-guide/text/DS_ref_app_deepstream.html?highlight=batch%20processing#deepstream-best-practices) - 批次大小設置為等於輸入源(batch == sources) - streammux的高度和寬度設置為輸入解析度 - 如果從RTSP 從USB 傳輸,配置文件的[streammux]設置live-source=1,可以確保正確的時間戳記 - 視覺輸出(Tiling and visual output )會占用GPU資源。在不需要在屏幕上渲染輸出時,以下三個方法可以禁用以最大限度提高吞吐量 - 關閉OSD或屏幕顯示 - 在配置文件將[osd]參數中設置enable=0 - 平鋪器(tiler)為顯示輸出流創建了一個NxM網格 - 將 [tiled-display]參數中設置enable=0 - 關閉輸出接收器(output sink) - 將[sink]參數選擇fakesink,即type=1 ### Jetson optimization 確保Jetson時脈開至最大 ``` $ sudo nvpmodel -m <mode> --for MAX perf and power mode is 0 $ sudo jetson_clocks ``` ::: --- ## 參考資料 ### NVIDIA DeepStream官方文件 - [DeepStream SDK | NVIDIA Developer](https://developer.nvidia.com/deepstream-sdk) - 官方文件及載點 - docker [DeepStream-l4t](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/deepstream-l4t) - [範例檔案使用說明](https://docs.nvidia.com/metropolis/deepstream/dev-guide/text/DS_Python_Sample_Apps.html) - 官方文件內有提供各種應用情境範例的.py檔及配置文件,包含串聯不同模型、multistream、結合Triton或直接使用本機TRT直接推論 ![](https://hackmd.io/_uploads/SkGTJ0FVh.png =600x) - 部署YOLO模型的文件配置[NVIDIA-AI-IOT/yolo_deepstream](https://github.com/NVIDIA-AI-IOT/yolo_deepstream/tree/main/deepstream_yolo) - [Building a Real-time Redaction App Using NVIDIA DeepStream, Part 2: Deployment | NVIDIA Technical Blog](https://developer.nvidia.com/blog/real-time-redaction-app-nvidia-deepstream-part-2-deployment/) #### NVIDIA 官方範例程式 - [NVIDIA-AI-IOT/deepstream_python_apps: DeepStream SDK Python bindings and sample applications (github.com)](https://github.com/NVIDIA-AI-IOT/deepstream_python_apps) - 提供python接口的範例 - [NVIDIA Jetson Nano 2GB 系列文章(35):Python版test1实战说明](https://zhuanlan.zhihu.com/p/415054673) #### 其他不錯的DeepStream概念介紹 - [2022。Galliot。NVIDIA DeepStream Python Bindings; Customize your Applications](https://galliot.us/blog/deepstream-python-bindings-customization/) ![](https://hackmd.io/_uploads/By_1wFhSh.png =600x) Figure 2: A DeepStream Pipeline with two processing elements - [2021。Kavika Roy。Nvidia DeepStream — A Simplistic Guide](https://www.datatobiz.com/blog/nvidia-deepstream-guide/) #### NVIDIA 教學影片 ##### [2023/01。NVIDIA DeepStream Technical Deep Dive: DeepStream Inference Options with Triton & TensorRT](https://www.youtube.com/watch?v=eM4nKWy6anA) :::spoiler - outline 1. 使用DeepStream的推理選項來處理Tensorflow、Pytorch和ONNX模型。 2. 與TensorRT和DeepStream合作,進行優化模型。 3. 使用Triton服務器來支持單個或多個DeepStream管道。 4. 使用DeepStream的前/後處理插件。 ###### 帶有DS-TensorRT(gst-nvinfer)插件的DeepStream-app設置 - 應用程序和插件的配置文件 - DS-TRT首次將ONNX/TAO/Caffe模型在線轉化為TensorRT引擎文件 ![](https://hackmd.io/_uploads/H1Fd4twE2.png =600x) ###### DeepStream-app with DS-Triton (gst-nvinferserver) Server CAPI * 推理方法 1(本機): * ==Triton Server CAPI: 在單一程序(Process)中直接加載模型Repo== ![](https://hackmd.io/_uploads/ryBH_tD42.png =400x) ###### DeepStream-app with DS-Triton (gst-nvinferserver) gRPC Inference * 推理方法 2(遠端): * ==Triton gRPC Remote: 通過gRPC向遠程tritonserver-app發送INPUT並等待響應== ![](https://hackmd.io/_uploads/BJ-mdFPNn.png =400x) | | Inference Approach 1:<br> Triton Server CAPI | Inference Approach 2:<br> Triton gRPC Remote | |:---- |:----------------------------------------------------------------------:|:----------------------------------------------------------------------------- | | 優點 | - 直接在本機加載模型並使用,效能較佳<br>- 不需要通過網絡傳輸數據 | - 可以將模型部署在遠程服務器上(雲端)<br>- 可以使用Triton Server提供的所有特性 | | 缺點 | - 受限於單個進程中的記憶體和處理能力<br>- 不支持在遠程服務器上運行模型 | - 需要通過網絡傳輸數據,可能會影響推理性能<br>- 遠程服務器必須支持gRPC協議 | - 在NVIDIA Triton Inference Server中,"CAPI"和"gRPC"都是指不同的推理模式。 - CAPI - 代表“C API”。這是一種本地推理模式,其中應用程序直接使用Triton的C++推理庫來加載和運行模型。這種推理模式通常用於將推理服務嵌入到應用程序中,以便實現最佳性能和低延遲。在這種模式下,模型存儲庫直接加載到應用程序進程中,因此可以避免網絡傳輸和通信開銷。 - gRPC - 代表“general-purpose Remote Procedure Call”。這是一種遠程推理模式,其中客戶端通過網絡連接到遠程Triton推理服務器,使用gRPC協議發送輸入數據,並等待服務器返回推理結果。在這種模式下,應用程序不需要在本地加載模型,因為模型存儲庫是由Triton推理服務器加載和管理的。這種模式通常用於客戶端和服務器之間的跨網絡推理,例如在雲上運行的推理服務。 ###### 推理前的DeepStream批(Batching)處理 * `nvstreammux`在推理前將所有的輸入數據流分批輸入 在推理前一起進行 * `nvstreammux`的批處理策略適用於兩個推理插件 * DS-Triton(gst-nvinferserver)插件 * DS-TensorRT (gst-nvinfer) 插件 * 通過配置文件設置批量(batch)大小 ![](https://hackmd.io/_uploads/Hy23jYvE2.png =600x) ![](https://hackmd.io/_uploads/ByZghKPVh.png =800x) ###### 解析推理數據的DeepStream示例應用程序 ![](https://hackmd.io/_uploads/HybTntwN2.png =900x) ![](https://hackmd.io/_uploads/HyBBCYD42.png =500x) ###### DeepStream Triton推理數據流 - ![](https://hackmd.io/_uploads/r1gdEycw4n.png) ::: ##### [2022/06。NVIDIA DeepStream Technical Deep Dive : Multi-Object Tracker](https://www.youtube.com/watch?v=4nV-GtqggEw) ##### [2021/01。Implementing Real-time Vision AI Apps Using NVIDIA DeepStream SDK](https://www.youtube.com/watch?v=hSegX0P170s) ### C++ python bindings #### [python-bindings-overview](https://realpython.com/python-bindings-overview/) - 中文版 [Python Bindings - 从 Python 调用 C/C++](https://zhuanlan.zhihu.com/p/143356193) - [给 Python 算法插上性能的翅膀——pybind11 落地实践](https://zhuanlan.zhihu.com/p/444805518) - [Deepstream Python官方範例解說](https://blog.csdn.net/zyctimes/article/details/122601921) ### YOLO - DeepStream 以下幾個倉儲有提供C++的範例程式,不過如果要結合PYTHON BINDING的範例程式執行,要手動修改的部分還蠻複雜的 #### GITHUB - [NVIDIA-AI-IOT/yolo_deepstream](https://github.com/NVIDIA-AI-IOT/yolo_deepstream) - 官方範例檔 - 提供C++的範例程式 - [marcoslucianops/DeepStream-Yolo](https://github.com/marcoslucianops/DeepStream-Yolo) - 非官方範例,有提供YOLO系列架構支援 - 提供C++的範例程式 - 說明與支援較完整 - [visualcortex-official/yolov7-deepstream](https://github.com/visualcortex-official/yolov7-deepstream) - 提供整合`efficientNMS`外掛的支援 #### Forum - [Tutorial: How to run YOLOv7 on Deepstream](https://forums.developer.nvidia.com/t/tutorial-how-to-run-yolov7-on-deepstream/229045) - 有討論到如何在多媒體串流範例程式上如何修改 deepstream_python_apps/apps/deepstream-rtsp-in-rtsp-out at master · NVIDIA-AI-IOT/deepstream_python_apps - [Deepstream python app yolov7 integration issue](https://forums.developer.nvidia.com/t/deepstream-python-app-yolov7-integration-issue/246034) - `python3 deepstream_test_1.py /opt/nvidia/deepstream/deepstream-6.2/samples/streams/sample_720p.h264` :::spoiler > - dstest1_pgie_config.txt → from config_infer_primary_yoloV7.txt 1 under yolo_deepstream/deepstream_yolo at main · NVIDIA-AI-IOT/yolo_deepstream · GitHub > - nvdsinfer_custom_impl_Yolo 1 → from nvdsinfer_custom_impl_Yolo 1 under yolo_deepstream/deepstream_yolo at main · NVIDIA-AI-IOT/yolo_deepstream · GitHub > - labels.txt → from labels.txt under yolo_deepstream/deepstream_yolo at main · NVIDIA-AI-IOT/yolo_deepstream · GitHub > - yolov7.onnx → from yolov7.onnx under yolo_deepstream/yolov7_qat at main · NVIDIA-AI-IOT/yolo_deepstream · GitHub ::: #### GStreamer - [GStreamer 簡介與筆記](https://hackmd.io/@YungHuiHsu/ryhRTZpt3) DeepStream的功能是建基於GStreamer上,所以在使用前最好對後者的原理有點概念會比較好修改 [gstreamer/documentation](https://gstreamer.freedesktop.org/documentation/) #### [Deploy YOLOv8 on NVIDIA Jetson using TensorRT and DeepStream SDK](https://wiki.seeedstudio.com/YOLOv8-DeepStream-TRT-Jetson/)