上一篇文章已經帶著大家安裝 DeepStream 的 Python 開發環境,并且執行最簡單的 deepstream-test1.py,讓大家體驗一下這個范例的效果。本文則進一步以這個 Python 代碼講解 DeepStream 插件工作流,并且擴充 USB 攝像頭作為輸入,以及將輸出透過 RTSP 轉發到其他電腦上觀看。
如果還未安裝 Python 環境或下載 Python 范例的,請至前一篇文章中找安裝與下載的步驟,這里不再重復。
前面文章中已經簡單提過 DeepStream 所用到的插件內容,但那只是整個框架中非常基礎的一小部分,本文要用代碼開始解說范例的時候,還是得將 Gstreamer 一些重要構成元素之間的關系說明清楚,這樣才能讓大家在代碼過程得以一目了然。
現在先把這個 test1 范例的執行流程先講解清楚,這樣在閱讀后面的代碼就會更加容易掌握上下之間的交互關系。這里的流程對 C/C++ 版本與 Python 版本是完全一樣的,只不過代碼不過用 Python 來說明:
首先 filesrc 數據源元件負責從磁盤上讀取視頻數據
nvv4l2decoder 編碼器元件負責對數據進行解碼
nvstreammux 流多路復用器元件負責批處理幀以實現最佳推理性能
nvinfer 推理元件負責實現加速推理
nvvideoconvert 轉換器元件負責將數據格式轉換為輸出顯示支持的格式
nvdsosd 可視化元件負責將邊框與文本等信息繪制到圖像中
nvegltransform 渲染元件和 nveglglessink 接收器元件負責輸出到屏幕上
建立 DeepStream 應用程式的步驟與 Gstreamer 幾乎一樣,都是有固定的步驟,只要熟悉之后就會發現其實并沒有什么難度,接下去就開始我們的執行步驟。
創建 DeepStream 應用的7大步驟初始化 Gstreamer 與創建管道(pipeline)
1. 初始化 Gstreamer 與創建管道(pipeline)
# 從“def main(args):”開始
GObject.threads_init()
# 標準GStreamer初始化
Gst.init(None)
# 創建Gst物件與初始化
pipeline = Gst.Pipeline()
# 創建與其他元素相連接的管道元素
2. 創建所有需要的元件(element):用Gst.ElementFactory.make() 創建所需要的元素,每個元素內指定插件類別(粗體部分)并給定名稱(自行設定):
# 階段1-處理輸入源的插件:
# 建立“源”元素負責從文件讀入數據
source = Gst.ElementFactory.make(“filesrc”, “file-source”)
# 解析文件是否為要求的h264格式
h264parser = Gst.ElementFactory.make(“h264parse”, “h264-parser”)
# 調用NVIDIA的nvdec_h264硬件解碼器
decoder = Gst.ElementFactory.make(“nvv4l2decoder”, “nvv4l2-decoder”)
# 創建nvstreammux實例,將單個或多個源數據,復用成一個“批(batch)”
streammux = Gst.ElementFactory.make(“nvstreammux”, “Stream-muxer”)
# 階段2-執行推理的插件:
# 使用NVINFERE對解碼器的輸出執行推理,推理行為是通過配置文件設置
pgie = Gst.ElementFactory.make(“nvinfer”, “primary-inference”)
# 階段3-處理輸出的插件:
# 根據nvosd的要求,使用轉換器將NV12轉換為RGBA
nvvidconv = Gst.ElementFactory.make(“nvvideoconvert”, “convertor”)
# 創建OSD以在轉換的RGBA緩沖區上繪制
nvosd = Gst.ElementFactory.make(“nvdsosd”, “onscreendisplay”)
# 最后將osd的繪制,進行渲染后在屏幕上顯示結果
transform=Gst.ElementFactory.make(“nvegltransform”, “egltransform”)
sink = Gst.ElementFactory.make(“nveglglessink”, “nvvideo-renderer”)
3. 配置元件的參數:
# 以args[1]給定的文件名為輸入源視頻文件
source.set_property(‘location’, args[1])
# 設定流復用器的尺寸、數量
streammux.set_property(‘width’, 1920)
streammux.set_property(‘height’, 1080)
streammux.set_property(‘batch-size’, 1)
streammux.set_property(‘batched-push-timeout’, 4000000)
# 設定pgie的配置文件
pgie.set_property(‘config-file-path’, “dstest1_pgie_config.txt”)
4. 將元件添加到導管之中:用pipeline.add()
pipeline.add(source)
pipeline.add(h264parser)
pipeline.add(decoder)
pipeline.add(streammux)
pipeline.add(pgie)
pipeline.add(nvvidconv)
pipeline.add(nvosd)
pipeline.add(sink)
if is_aarch64():
pipeline.add(transform)
5. 將元件按照要求連接起來:本范例的管道流為file-source -》 h264-parser -》 nvh264-decoder -》 streammux -》 nvinfer -》 nvvidconv -》 nvosd -》 video-renderer
source.link(h264parser) # file-source -》 h264-parser
h264parser.link(decoder) # h264-parser -》 nvh264-decoder
# 下面粗線的三行,是streammux的特殊處理方式
sinkpad = streammux.get_request_pad(“sink_0”)
srcpad = decoder.get_static_pad(“src”)
srcpad.link(sinkpad)
streammux.link(pgie) # streammux -》 nvinfer
pgie.link(nvvidconv) # nvinfer -》 nvvidconv
nvvidconv.link(nvosd) # nvvidconv -》 nvosd
nvosd.link(transform) # nvosd -》 transform
transform.link(sink) # transform -》 video-renderer
前面5個步驟都是比較靜態的固定步驟,只要將想開發的應用所需要的插件元件進行“創建”、“給值”、“連接”就可以。
接下去的部分是整個應用中非常關鍵的靈魂,就是我們得為整個應用去建構“信息(message)傳遞系統”,這樣才能讓這個應用與插件元件之間形成互動,進而正確執行我們想要得到的結果。其相互關系圖如下,這里并不花時間去講解調用細節,想了解的請自行參考 Gstreamer 框架的詳細使用。
6. 創建一個事件循環(evnet loop):將信息(mesages)傳入并監控bus的信號
loop = GObject.MainLoop()
bus = pipeline.get_bus()
bus.add_signal_watch()
bus.connect (“message”, bus_call, loop)
# 用osdsinkpad來確認nvosd插件是否獲得輸入
osdsinkpad = nvosd.get_static_pad(“sink”)
# 添加探針(probe)以獲得生成的元數據的通知,我們將probe添加到osd元素的接收器板中,因為到那時,緩沖區將具有已經得到了所有的元數據。
osdsinkpad.add_probe(Gst.PadProbeType.BUFFER,
osd_sink_pad_buffer_probe, 0)
注意粗體“osd_sink_pad_buffer_probe”部分,這是代碼中另一個重點,需要自行撰寫代碼去執行的部分,就是代碼中第41~126行的內容,這里面的處理以“幀”為單位(在“while l_frame is not None:”里面),將該幀所檢測到的物件種類進行加總,并且將物件根據種類的顏色畫出框框。
事實上在這80+行代碼中,真正與數據處理相關的部分,只有20行左右的內容,注釋的部分占用不小的篇幅,這是作者為大家提供非常重要的說明,只要耐心地去閱讀,就能輕松地掌握里面的要領。
7. 播放并收聽事件:這部分就是個“啟動器”,如同汽車鑰匙“執行發動”功能一樣。
# 配置導管狀態為PLAYING就可以
pipeline.set_state(Gst.State.PLAYING)
try:
loop.run() # 執行前面創建的事件循環
except:
pass
# 執行結束之后,需要清除導管,將狀態為NULL就可以
pipeline.set_state(Gst.State.NULL)
以上就是建立DeepStream應用的標準步驟,可以將“def main(args):”部分的代碼當作是個模板去加以利用。
至于“osd_sink_pad_buffer_probe”函數的作用,就是從osd接收器提取接收的元數據,并更新繪圖矩形、對象信息等的參數,里面的代碼也都是標準內容,可以在別的應用在重復套用。更多參數優化的細節部分,須花時間詳細閱讀DeepStream開發手冊。
接下來就實際執行一下Python版本的deepstream-test1代碼,看看效果如何!
執行deepstream_test_1.py
前面文章中已經將NVIDIA/AI-IOT/deepstream-python-apps項目下載到Jetson Nano 2GB上的《deepstream《 span=“”》根目錄》/sources下面,現在就到這個目錄下去執行
cd《deepstream《 span=“”》根目錄》/sources/deepstream_python_apps/apps
cd deepstream-test1
下面有執行文件deepstream_test_1.py、配置文件dstest1_pgie_config.txt與說明文件README,這個配置文件就是步驟3最后“pgie.set_property”里面指定的文件,在執行文件里看不到任何與推理模型相關的內容,原來都放在設定文件里面去指定了。
關于設定文件的參數設定部分,是相對容易了解的,這里不多花時間說明,接下去直接執行以下指令看看執行結果:
python3 deepstream_test_1.py 。。/。。/。。/。。/samples/streams/sample_720p.h264
就能跑出我們熟悉的結果,
如果覺得左上方顯示的字體太小,請自行改動代碼第110行的字體號數。字體放大到20號時候的顯示結果,現在就可以看到很清楚了。
到這里,相信您應該對DeepStream代碼有更深層次的了解,在了解整個框架與工作流程之后,可以發現要開發一個基礎應用,并不是一件太困難的事情,不過建議您多反復閱讀代碼內的每一行說明,并且自行適度修改些參數看看效果會有什么變化,一旦熟悉這些邏輯與交互關系之后,就會覺得DeepStream其實很簡單。
編輯:jq
-
數據
+關注
關注
8文章
7002瀏覽量
88943 -
代碼
+關注
關注
30文章
4779瀏覽量
68525 -
python
+關注
關注
56文章
4792瀏覽量
84628
原文標題:NVIDIA Jetson Nano 2GB 系列文章(35):Python版test1實戰說明
文章出處:【微信號:NVIDIA-Enterprise,微信公眾號:NVIDIA英偉達企業解決方案】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論