摘要:本案例將使用YOLOX、SCNet兩個模型,實作一個簡單的隔空作畫趣味應用
本文分享自華為云社區《ModelBox開發案例 - 隔空作畫》,作者:吳小魚,
本案例將使用 YOLOX、SCNet 兩個模型,實作一個簡單的隔空作畫趣味應用,最終效果如下所示:

案例所需資源(代碼、模型、測驗資料等)均可從obs桶下載,
模型訓練
我們使用面向開發者的一站式AI開發平臺ModelArts進行模型的訓練:
ModelArts提供了包括資料標注,訓練環境,預置演算法在內的豐富的功能,甚至可以通過訂閱預置演算法實作0代碼的模型訓練作業,當然你也可以在本地訓練自己的模型,我們假設你現在已經擁有了訓練好的模型,接下來我們需要將訓練好的模型轉換成為可以在開發板上運行的模型,
模型轉換
我們發布了開發板模型轉換案例,參見RK3568模型轉換驗證案例 :
在這個案例中我們演示了從環境適配到模型的轉換驗證的全流程樣例代碼,開發者可以通過“Run in ModelArts”一鍵將Notebook案例在ModelArts控制臺快速打開、運行以及進行二次開發等操作,
開發環境部署
使用開發板進行ModelBox AI應用開發有兩種方式,一是開發板連接顯示幕和鍵盤滑鼠,安裝Ubuntu桌面,直接在開發板上進行開發;二是使用遠程連接工具(如VS Code中的Remote-SSH)從PC端登錄開發板進行開發,這里我們推薦第二種方式,因為PC端可以使用功能更豐富、界面更友好的IDE,
1.配置網路
PC連接開發板需要知道開發板的ip,但是開發板默認沒有固定ip,我們提供了ModelBox PC Tool,可以自動為開發板配置ip,也可以在推理階段很方便的進行視頻推流拉流,
PC Tool位于SDK的connect_wizard目錄中:
雙擊connect_wizard.exe,在頁面中可以看到有兩種開發板連接方式,我們使用網線連接開發板的方式:
按照指引斷開或連接網線:


等待一小段時間,可以看到來到了第三步,此時開發板已設定為默認ip:192.168.2.111,PC使用該ip即可SSH登錄:
2. 遠程連接開發板
我們推薦在PC端使用VS Code遠程連接開發板來對設備操作,
使用VS Code連接開發板可以參考我們發布的ModelBox 端云協同AI開發套件(RK3568)上手指南,同時,上手指南也介紹了如何將開發板注冊到HiLens管理控制臺進行更方便的在線管理,
應用開發
接下來我們會以隔空作畫demo為例,介紹如何使用ModelBox開發一個AI應用,
1.創建工程
SDK提供了工程腳本create.py,可以使用./create.py -h查看腳本幫助:
ModelBox提供了可視化圖編排工具:Editor,可以使用./create.py -t editor開啟圖編排服務:
服務默認ip即為192.168.2.111,如需配置其他ip或埠,可以通過-i ip:port引數進行配置,
點擊鏈接即可進入可視化編輯界面,我們點擊編排進入工程開發界面,如果進一步了解ModelBox相關內容,可以點擊右上角幫助:
進入編排界面,點擊右上角新建專案:
專案路徑填寫workspace,專案名稱填寫hand_painting, 確認:
可以看到我們已經擁有了一個帶有http收發單元的默認圖:
其中,區域1為SDK預置的高性能通用流單元,區域2為可視化編排界面,區域3為對應的圖組態檔內容,同時,VS Code對應目錄下也出現了hand_painting專案:
2.創建推理功能單元
接下來,我們創建推理流單元:

對于手檢測模型,我們將流單元命名為hand_detection,模型檔案名即為轉換好的檢測模型名:yolox_hand.rknn,此模型輸入為image,輸出為feature map,所以我們添加int型別的輸入埠與float型別的輸出埠,關于開發板的推理流單元創建,在處理型別時我們選擇cuda,即為npu推理,推理引擎可選任意一款,目前開發板SDK可以自動進行識別轉換,最后將功能單元分組修改為inference,點擊確認,即可看到,在右側inference頁簽下出現了:
同時,在VS Code工程model目錄下可以看到創建好的推理流單元:
同樣的,我們創建pose_detection推理流單元:
3.創建后處理功能單元
除了推理流單元外,隔空作畫demo還需要一些通用功能單元:檢測后處理單元、感興趣區域提取單元、作畫單元,我們新建三個python功能單元來滿足上述需求,
對于檢測后處理單元,我們希望通過原圖和hand_detection的輸出解碼出手檢測框,所以該單元應該有兩個輸入,此外,對于畫幅中有手或者沒有檢測到手兩種狀態,我們希望該功能單元分情況處理,檢測到手時,將檢測結果送入感興趣區域提取單元,沒有檢測到手時,直接回傳,因此功能單元型別選擇:IF_ELSE,新建單元如下:
同樣的,根據輸入輸出與功能單元狀態,我們創建extract_roi和painting兩個功能單元:

4.流程圖編排
拖拽
需要的功能單元全部創建好后,我們可以著手編排流程圖,我們編排一個視頻處理的圖,暫時不需要http收發單元,可以洗掉不需要的單元:
在Generic串列下將虛擬輸入單元input和我們剛剛創建的三個功能單元拖入畫布:
在Image串列下將模型推理需要用到的預處理單元resize拖入畫布,因為我們需要兩個resize單元,所以重復拖入:
值得注意的是,resize單元需要配置引數,需要點擊該單元進行配置:
在Input串列下拖入輸入決議單元data_source_parser:
在Video串列下拖入視頻處理需要的單元video_demuxer、video_decoder、video_out:
最后,在Inference串列下拖入我們創建的兩個推理單元:
編排
將功能單元按照處理邏輯進行連接:虛擬輸入input連接輸入決議data_source_parser,決議后送入視頻解包與解碼單元:
解碼輸出送入預處理后可直接進行推理:
推理后處理需要輸入原圖與推理結果,沒有結果則直接連接視頻輸入單元,有結果則連接感興趣區域提取單元:
提取結果送入預處理與推理:
最后,得到的關鍵點結果與原圖送入作畫單元,作畫結果送入視頻輸出單元進行保存:
這樣,我們就完成了流程圖的編排,可以看到在GraphViz區域也出現了完整的圖表述:
保存專案,轉到VS Code進行每個單元的代碼實作:
5.代碼補全
可視化編排中,創建的推理單元位于專案的model目錄下,通用單元位于etc/flowunit目錄下,流程圖位于graph目錄下,可以看到創建的單元與圖都已同步過來:
其中,video_decoder需要指定型別:
video_decoder7 [ type=flowunit flowunit=video_decoder device=rknpu, deviceid="0", pix_fmt=bgr label="{{<in_video_packet> in_video_packet}|video_decoder7|{<out_video_frame> out_video_frame}}" ]
推理單元
首先完善推理單元,對于推理功能單元,只需要提供獨立的toml組態檔,指定推理功能單元的基本屬性即可,目錄結構為:
[flowunit-name] |---[flowunit-name].toml #推理功能單元配置 |---[model].rknn #模型檔案 |---[infer-plugin].so #推理自定義插件 ModelBox框架在初始化時,會掃描目錄中的toml后綴的檔案,并讀取相關的推理功能單元資訊,[infer-plugin].so是推理所需插件,推理功能單元支持加載自定義插件,開發者可以實作自定義算子,
將模型拷入對應檔案夾,以hand_detection為例我們看一下推理功能單元組態檔:
組態檔中有一些單元型別、模型名稱、輸入輸出的基本配置,可以酌情修改,
通用單元
Python通用單元需要提供獨立的toml組態檔,指定python功能單元的基本屬性,一般情況,目錄結構為:
[FlowUnitName] |---[FlowUnitName].toml |---[FlowUnitName].py |---xxx.py
相較于推理單元而言,通用單元不但有組態檔,還需要完善具體的功能代碼,以yolox_post為例,首先是功能單元組態檔:
# Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. # Basic config [base] name = "yolox_post" # The FlowUnit name device = "cpu" # The flowunit runs on cpu version = "1.0.0" # The version of the flowunit type = "python" # Fixed value, do not change description = "description" # The description of the flowunit entry = "yolox_post@yolox_postFlowUnit" # Python flowunit entry function group_type = "generic" # flowunit group attribution, change as input/output/image ... # Flowunit Type stream = false # Whether the flowunit is a stream flowunit condition = true # Whether the flowunit is a condition flowunit collapse = false # Whether the flowunit is a collapse flowunit collapse_all = false # Whether the flowunit will collapse all the data expand = false # Whether the flowunit is a expand flowunit # The default Flowunit config [config] item = "value" # Input ports description [input] [input.input1] # Input port number, the format is input.input[N] name = "in_image" # Input port name type = "uint8" # input port data type ,e.g. float or uint8 device = "cpu" # input buffer type [input.input2] # Input port number, the format is input.input[N] name = "in_feat" # Input port name type = "uint8" # input port data type ,e.g. float or uint8 device = "cpu" # input buffer type # Output ports description [output] [output.output1] # Output port number, the format is output.output[N] name = "has_hand" # Output port name type = "float" # output port data type ,e.g. float or uint8 [output.output2] # Output port number, the format is output.output[N] name = "no_hand" # Output port name type = "float" # output port data type ,e.g. float or uint8
Basic config是一些單元名等基本配置,Flowunit Type是功能單元型別,yolox_post是一個條件單元,所以可以看到condition為true,此外還有一些展開、歸攏等性質,可以在AI Gallery ModelBox)板塊下看到更多案例,
config為單元需要配置的一些屬性,如本單元需要一些特征圖size、閾值等資訊,所以在組態檔中修改config為:
[config] net_h = 320 net_w = 320 num_classes = 2 conf_threshold = 0.5 iou_threshold = 0.5
此外,輸入輸出type根據實際邏輯可能進行一些修改:
# Input ports description [input] [input.input1] # Input port number, the format is input.input[N] name = "in_image" # Input port name type = "uint8" # input port data type ,e.g. float or uint8 device = "cpu" # input buffer type [input.input2] # Input port number, the format is input.input[N] name = "in_feat" # Input port name type = "float" # input port data type ,e.g. float or uint8 device = "cpu" # input buffer type # Output ports description [output] [output.output1] # Output port number, the format is output.output[N] name = "has_hand" # Output port name type = "uint8" # output port data type ,e.g. float or uint8 [output.output2] # Output port number, the format is output.output[N] name = "no_hand" # Output port name type = "uint8" # output port data type ,e.g. float or uint8
接下來,我們查看yolox_post.py,可以看到創建單元時已經生成了基本介面:
# Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. #!/usr/bin/env python # -*- coding: utf-8 -*- import _flowunit as modelbox class yolox_postFlowUnit(modelbox.FlowUnit): # Derived from modelbox.FlowUnit def __init__(self): super().__init__() def open(self, config): # Open the flowunit to obtain configuration information return modelbox.Status.StatusCode.STATUS_SUCCESS def process(self, data_context): # Process the data in_data = data_context.input("in_1") out_data = data_context.output("out_1") # yolox_post process code. # Remove the following code and add your own code here. for buffer in in_data: response = "Hello World " + buffer.as_object() result = response.encode('utf-8').strip() add_buffer = modelbox.Buffer(self.get_bind_device(), result) out_data.push_back(add_buffer) return modelbox.Status.StatusCode.STATUS_SUCCESS def close(self): # Close the flowunit return modelbox.Status() def data_pre(self, data_context): # Before streaming data starts return modelbox.Status() def data_post(self, data_context): # After streaming data ends return modelbox.Status() def data_group_pre(self, data_context): # Before all streaming data starts return modelbox.Status() def data_group_post(self, data_context): # After all streaming data ends return modelbox.Status()
如果功能單元的作業模式是stream = false時,功能單元會呼叫open、process、close介面;如果功能單元的作業模式是stream = true時,功能單元會呼叫open、data_group_pre、data_pre、process、data_post、data_group_post、close介面;用戶可根據實際需求實作對應介面,
根據單元性質,我們主要需要完善open、process介面:
import _flowunit as modelbox import numpy as np from yolox_utils import postprocess, expand_bboxes_with_filter, draw_color_palette class yolox_postFlowUnit(modelbox.FlowUnit): # Derived from modelbox.FlowUnit def __init__(self): super().__init__() def open(self, config): self.net_h = config.get_int('net_h', 320) self.net_w = config.get_int('net_w', 320) self.num_classes = config.get_int('num_classes', 2) self.num_grids = int((self.net_h / 32) * (self.net_w / 32)) * (1 + 2*2 + 4*4) self.conf_thre = config.get_float('conf_threshold', 0.3) self.nms_thre = config.get_float('iou_threshold', 0.4) return modelbox.Status.StatusCode.STATUS_SUCCESS def process(self, data_context): modelbox.info("YOLOX POST") in_image = data_context.input("in_image") in_feat = data_context.input("in_feat") has_hand = data_context.output("has_hand") no_hand = data_context.output("no_hand") for buffer_img, buffer_feat in zip(in_image, in_feat): width = buffer_img.get('width') height = buffer_img.get('height') channel = buffer_img.get('channel') img_data = np.array(buffer_img.as_object(), copy=False) img_data = img_data.reshape((height, width, channel)) feat_data = np.array(buffer_feat.as_object(), copy=False) feat_data = feat_data.reshape((self.num_grids, self.num_classes + 5)) ratio = (self.net_h / height, self.net_w / width) bboxes = postprocess(feat_data, (self.net_h, self.net_w), self.conf_thre, self.nms_thre, ratio) box = expand_bboxes_with_filter(bboxes, width, height) if box: buffer_img.set("bboxes", box) has_hand.push_back(buffer_img) else: draw_color_palette(img_data) img_buffer = modelbox.Buffer(self.get_bind_device(), img_data) img_buffer.copy_meta(buffer_img) no_hand.push_back(img_buffer) return modelbox.Status.StatusCode.STATUS_SUCCESS def close(self): # Close the flowunit return modelbox.Status()
可以看到,在open中我們進行了一些引數獲取,process進行邏輯處理,輸入輸出可以通過data_context來獲取,值得注意的是輸出時我們回傳的是圖,在檢測到手時為圖附加了檢測框資訊,該資訊可以被下一單元獲取,
同樣的,完善其余通用功能單元,具體可以參考我們提供的代碼,
應用運行
我們需要準備一個mp4檔案拷貝到data檔案夾下,我們提供了測驗視頻hand.mp4,然后打開工程目錄下bin/mock_task.toml檔案,修改其中的任務輸入和任務輸出配置為如下內容:
# 任務輸入,mock模擬目前僅支持一路rtsp或者本地url # rtsp攝像頭,type = "rtsp", url里面寫入rtsp地址 # 其它用"url",比如可以是本地檔案地址, 或者httpserver的地址,(攝像頭 url = "0") [input] type = "url" url = "../data/hand.mp4" # 任務輸出,目前僅支持"webhook", 和本地輸出"local"(輸出到螢屏,url="0", 輸出到rtsp,填寫rtsp地址) # (local 還可以輸出到本地檔案,這個時候注意,檔案可以是相對路徑,是相對這個mock_task.toml檔案本身) [output] type = "local" url = "../hilens_data_dir/paint.mp4"
配置好后在工程路徑下執行build_project.sh進行工程構建:
rock@rock-3a:~/lxy/examples$ cd workspace/hand_painting/ rock@rock-3a:~/lxy/examples/workspace/hand_painting$ ./build_project.sh dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/graph/hand_painting.toml to Unix format... dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/graph/modelbox.conf to Unix format... dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/etc/flowunit/extract_roi/extract_roi.toml to Unix format... dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/etc/flowunit/painting/painting.toml to Unix format... dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/etc/flowunit/yolox_post/yolox_post.toml to Unix format... dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/model/hand_detection/hand_detection.toml to Unix format... dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/model/pose_detection/pose_detection.toml to Unix format... dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/bin/mock_task.toml to Unix format... build success: you can run main.sh in ./bin folder rock@rock-3a:~/lxy/examples/workspace/hand_painting$
構建完成后運行專案:
rock@rock-3a:~/lxy/examples/workspace/hand_painting$ ./bin/main.sh
等待稍許即可以在hilens_data_dir檔案夾下看到運行結果:
除了mp4外我們也支持很多其他型別的輸入輸出,ModelBox PC TOOL也提供了推流與拉流功能,選擇輸入實時視頻流,啟動:
運行程式時配置輸出地址為推流地址,即可在本機網頁中查看到運行結果:
如果需要對應用進行性能評估,只需要在流程圖組態檔中開啟profile:
[profile] profile=true # 啟用profile trace=true # 啟用traceing dir="/tmp/modelbox/perf" # 設定跟蹤檔案路徑
配置啟動后,啟動運行流程圖,profile會每隔60s記錄一次統計資訊,trace會在任務執行程序中和結束時,輸出統計資訊,
運行流程圖后,會生成性能相關的json檔案,通過將json檔案加載到瀏覽器中即可查看timeline資訊,
- 打開chrome瀏覽器,
- 瀏覽器中輸入chrome://tracing/,
- 點擊界面中的Load按鈕,加載trace的json檔案,
- 加載成功后,將看到類似下面的timeline視圖:
打包部署
打包
除錯完成后,同樣可以通過create.py腳本將應用打包發布:
./create.py -t rpm -n hand_painting
控制臺中輸出:
sdk version is modelbox-rk-aarch64-1.0.8.8 call mb-pkg-tool pack [folder] > [rpm file] to building rpm, waiting... success: create hand_painting.rpm in /home/rock/lxy/examples/workspace/hand_painting
等待稍許,可以看到專案工程下已經生成了rpm檔案夾和打包好的應用:
部署
將打包好的應用上傳至華為云賬號下的obs桶中:
在專業版設備管理中找到開發板,點擊創建部署:
技能包選擇剛剛上傳的應用:
如果需要,可以配置一些啟動引數,否則默認完成即可:
這樣我們就已經完成了一個AI應用,從模型訓練到轉換到開發到部署的全部流程,關于ModelBox核心概念、功能單元和流程圖開發的更多介紹,可查看ModelBox手冊,
點擊關注,第一時間了解華為云新鮮技術~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/504428.html
標籤:其他
上一篇:資料鏈路層
