工程筆記本 / NVIDIA

NVIDIA

NVIDIA Jetson Nano應用-多執行緒平行處理,以專案「你帶口罩了嗎?」為例

鄭 建彥📅 2021-05-20👁 15
*本文由RS components 贊助發表,轉載自DesignSpark部落格原文連結
作者/攝影 張嘉鈞
難度

★★☆☆☆(普通)

材料表 RK-NVIDIA® Jetson Nano™ Developer Kit B01 套件

思路分析

原先使用主線程運行影像辨識以及IFTTT進行即時監控,光是執行影像辨識就會要等待推論的時間,而後如果要傳送至IFTTT則又有一個傳送的等待Request時間,如此便會影響到While迴圈裡的即時影像,這邊有很多種方法可以改善,最快且較為簡單的解決方式是將即時影像放到另一個線程中去運行,這樣顯示即時影像與推論的線程是同步進行的,即時影像就不會因此被推論以及等待網頁的時間給延遲,只需要專心處理即時影像的部分即可。

平行運算中的多執行緒

在Python的平行運算中有分兩種,一個是Multi-Thread另一個是Multi-Process;Process (進程 ) 跟Thread (執行緒 ) 其實大家平常都會聽到,在購買電腦的時候常常會聽到幾核幾緒( 例如 : 四核八緒 )就是類似的概念,幾個觀念重點介紹:
  1. 每個CPU都只能運行一個Process,每個Process彼此之間是獨立的。
  2. 每個Process可以有多個Thread運行,彼此共享記憶體、變數。
由於Thread無法回傳值所以要使用Queue (佇列)去儲存資料,那這部分我就不多作介紹因為網路上已經有很多相關的參考了,不過,這邊我沒有使用queue的方式去撰寫程式。

增加即時影像的線程到程式中

我使用class的方式去寫因為可以直接省略queue去儲存、取得變數,算是一個偷吃步的小技巧,因為我這邊除了讀取幀之外就只有回傳的動作,應該不會導致搶資源或同步的問題。

客製化的即時影像物件

為了符合我們的需求,我客製了一個類別提供了幾個所需的功能,首先在initialize的部分,比較特別的地方在我使用了 isStop的參數用來中斷線程並且宣告了t為即時影像線程的物件。 [pastacode lang="python" manual="%23%20%E5%AE%A2%E8%A3%BD%E5%8C%96%E7%9A%84%E5%BD%B1%E5%83%8F%E6%93%B7%E5%8F%96%E7%A8%8B%E5%BC%8F%0A%0Aclass%20CustomVideoCapture()%3A%0A%0A%20%20%20%20%0A%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%20%E9%A0%90%E8%A8%AD%E7%9A%84%E6%94%9D%E5%BD%B1%E6%A9%9F%E8%A3%9D%E7%BD%AE%E7%82%BA%200%0A%0A%20%20%20%20def%20__init__(self%2C%20dev%3D0)%3A%0A%0A%20%20%20%20%20%20%20%20self.cap%20%3D%20cv2.VideoCapture(dev)%0A%20%20%20%20%20%20%20%20self.ret%20%3D%20''%0A%20%20%20%20%20%20%20%20self.frame%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20self.win_title%20%3D%20'Modified%20with%20set_title()'%0A%20%20%20%20%20%20%20%20self.info%20%3D%20''%0A%20%20%20%20%20%20%20%20self.fps%20%3D%200%0A%20%20%20%20%20%20%20%20self.fps_time%20%3D%200%0A%0A%20%20%20%20%20%20%20%20self.isStop%20%3D%20False%0A%20%20%20%20%20%20%20%20self.t%20%3D%20threading.Thread(target%3Dself.video%2C%20name%3D'stream')" message="" highlight="" provider="manual"/] 接著先宣告了一些可以從外部控制線程的函式,像是 start_stream就是開啟線程;stop_stream關閉線程;get_current_frame就是取得當前的畫面,使用get_current_frame可以讓外部直接獲取線程更新的畫面,算是一個使用Thread運行OpenCV常用的方法;最後還提供了一個set_title可以修改視窗的名稱: [pastacode lang="python" manual="%23%20%E5%8F%AF%E4%BB%A5%E9%80%8F%E9%81%8E%E9%80%99%E5%80%8B%E5%87%BD%E5%BC%8F%20%E9%96%8B%E5%95%9F%20Thread%20%0A%0A%20%20%20%20def%20start_stream(self)%3A%0A%20%20%20%20%20%20%20%20self.t.start()%0A%20%20%20%20%0A%20%20%20%20%0A%23%20%E9%97%9C%E9%96%89%20Thread%20%E8%88%87%20Camera%0A%0A%20%20%20%20def%20stop_stream(self)%3A%0A%20%20%20%20%20%20%20%20self.isStop%20%3D%20True%0A%20%20%20%20%20%20%20%20self.cap.release()%0A%20%20%20%20%20%20%20%20cv2.destroyAllWindows()%0A%0A%20%20%20%20%0A%23%20%E5%8F%96%E5%BE%97%E6%9C%80%E8%BF%91%E4%B8%80%E6%AC%A1%E7%9A%84%E5%B9%80%0A%0A%20%20%20%20def%20get_current_frame(self)%3A%0A%20%20%20%20%20%20%20%20return%20self.ret%2C%20self.frame%0A%0A%20%20%20%20def%20get_fps(self)%3A%0A%20%20%20%20%20%20%20%20return%20self.fps%0A%0A%20%20%20%20%0A%23%20%E8%A8%AD%E5%AE%9A%E9%A1%AF%E7%A4%BA%E8%A6%96%E7%AA%97%E7%9A%84%E5%90%8D%E7%A8%B1%0A%0A%20%20%20%20def%20set_title(self%2C%20txt)%3A%0A%20%20%20%20%20%20%20%20self.win_title%20%3D%20txt" message="" highlight="" provider="manual"/] 最後宣告了多線程要運作的函式,由於要不斷更新畫面所以使用while,透過isStop控制是否跳出迴圈,其中做的事情就是取得當前影像,設定要印上去的資訊並顯示出來,當按下q的時候會退出迴圈並且使用stop_stream終止迴圈: [pastacode lang="python" manual="%23%20Thread%E4%B8%BB%E8%A6%81%E9%81%8B%E8%A1%8C%E7%9A%84%E5%87%BD%E5%BC%8F%0A%0A%20%20%20%20def%20video(self)%3A%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20while(not%20self.isStop)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.fps_time%20%3D%20time.time()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.ret%2C%20self.frame%20%3D%20self.cap.read()%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20self.info%20is%20not%20''%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cv2.putText(self.frame%2C%20self.info%2C%20(10%2C40)%2C%20cv2.FONT_HERSHEY_SIMPLEX%2C%201%2C%20(0%2C0%2C255)%2C%202)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cv2.imshow(self.win_title%2C%20self.frame)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20cv2.waitKey(1)%20%3D%3D%20ord('q')%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.fps%20%3D%20int(1%2F(time.time()%20-%20self.fps_time))%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.stop_stream()%0A%20%20%20%20%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.stop_stream()" message="" highlight="" provider="manual"/] 我建立了一個tools.py存放所有會用到的副函式 ( 包含上述的客製化影像類別 ),這邊開始介紹其他副函式,preprocess專門在處理輸入前的資料,針對該資料進行縮放、正規化、轉換成含有批次大小的格式: [pastacode lang="python" manual="%23%20%E7%94%A8%E6%96%BC%E8%B3%87%E6%96%99%E5%89%8D%E8%99%95%E7%90%86%E7%9A%84%E7%A8%8B%E5%BC%8F%0A%0Adef%20preprocess(frame%2C%20resize%3D(224%2C%20224)%2C%20norm%3DTrue)%3A%0A%20%20%20%20'''%0A%20%20%20%20%E8%A8%AD%E5%AE%9A%E6%A0%BC%E5%BC%8F%20(%201%2C%20224%2C%20224%2C%203)%E3%80%81%E7%B8%AE%E6%94%BE%E5%A4%A7%E3%80%81%E6%AD%A3%E8%A6%8F%E5%8C%96%E3%80%81%E6%94%BE%E5%85%A5%E8%B3%87%E6%96%99%E4%B8%A6%E5%9B%9E%E5%82%B3%E6%AD%A3%E7%A2%BA%E6%A0%BC%E5%BC%8F%E7%9A%84%E8%B3%87%E6%96%99%0A%20%20%20%20'''%0A%20%20%20%20input_format%20%3D%20np.ndarray(shape%3D(1%2C%20224%2C%20224%2C%203)%2C%20dtype%3Dnp.float32)%0A%20%20%20%20frame_resize%20%3D%20cv2.resize(frame%2C%20resize)%0A%20%20%20%20frame_norm%20%3D%20%20((frame_resize.astype(np.float32)%20%2F%20127.0)%20-%201)%20if%20norm%20else%20frame_resize%0A%20%20%20%20input_format%5B0%5D%3Dframe_norm%0A%20%20%20%20return%20input_format" message="" highlight="" provider="manual"/] load_model_folder則是載入模型與標籤,這邊寫成只需要輸入存放模型與標籤的目錄路徑即可,兩者須放置在一起,程式會靠副檔名去判斷: [pastacode lang="python" manual="%23%20%E8%AE%80%E5%8F%96%20%E6%A8%A1%E5%9E%8B%20%E8%88%87%20%E6%A8%99%E7%B1%A4%0A%0Adef%20load_model_folder(trg_dir)%20-%3E%20%22'trg_dir'%20is%20the%20path%20include%20model%20file%20and%20labels%20file.%20return%20(model%2C%20label).%22%3A%0A%0A%20%20%20%20model_type%20%3D%20%5B%20'trt'%2C'engine'%2C'h5'%5D%0A%20%20%20%20label_type%20%3D%20%5B%20'txt'%5D%0A%0A%20%20%20%20for%20f%20in%20os.listdir(trg_dir)%3A%0A%20%20%20%20%20%20%20%20extension%20%3D%20f.split('.')%5B-1%5D%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20if%20extension%20in%20model_type%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20model_dir%20%3D%20os.path.join(trg_dir%2C%20f)%0A%20%20%20%20%20%20%20%20elif%20extension%20in%20label_type%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20lable_dir%20%3D%20os.path.join(trg_dir%2C%20f)%0A%0A%20%20%20%20return%20get_model(model_dir)%2C%20get_label(lable_dir)" message="" highlight="" provider="manual"/] 剛剛輸出的時候有用到兩個副函式 get_model、get_label,分別去取得模型與標籤檔的物件: [pastacode lang="python" manual="%23%20%E8%AE%80%E5%8F%96%E6%A8%A1%E5%9E%8B%0A%0Adef%20get_model(model_dir)%20-%3E%20%22support%20keras%20and%20tensorrt%20model%22%3A%0A%20%20%20%20%0A%20%20%20%20if%20model_dir.split('.')%5B-1%5D%20%3D%3D%20'h5'%3A%0A%20%20%20%20%20%20%20%20print('Load%20Keras%20Model')%0A%20%20%20%20%20%20%20%20model%20%3D%20tf.keras.models.load_model(model_dir)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20print('Load%20TensorRT%20Engine')%0A%20%20%20%20%20%20%20%20model%20%3D%20load_engine(model_dir)%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20return%20model%0A%20%20%20%20%0A%0A%23%20%E8%AE%80%E5%8F%96%E6%A8%99%E7%B1%A4%0A%0Adef%20get_label(lable_dir)%20-%3E%20'return%20dict%20of%20labels'%3A%0A%0A%20%20%20%20label%20%3D%20%7B%7D%0A%0A%20%20%20%20with%20open(lable_dir)%20as%20f%3A%20%20%20%20%0A%20%20%20%20%20%20%20%20for%20line%20in%20f.readlines()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20idx%2C%20name%20%3D%20line.strip().split('%20')%0A%20%20%20%20%20%20%20%20%20%20%20%20label%5Bint(idx)%5D%3Dname%0A%0A%20%20%20%20return%20label%0A%0A%23%20%E8%AE%80%E5%8F%96TensorRT%E6%A8%A1%E5%9E%8B%0A%0Adef%20load_engine(engine_path)%3A%0A%0A%20%20%20%20if%20trt_found%3A%0A%0A%20%20%20%20%20%20%20%20TRT_LOGGER%20%3D%20trt.Logger(trt.Logger.WARNING)%0A%20%20%20%20%20%20%20%20trt_runtime%20%3D%20trt.Runtime(TRT_LOGGER)%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20with%20open(engine_path%2C%20'rb')%20as%20f%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20engine_data%20%3D%20f.read()%0A%20%20%20%20%20%20%20%20engine%20%3D%20trt_runtime.deserialize_cuda_engine(engine_data)%0A%0A%20%20%20%20%20%20%20%20return%20engine%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20print(%22Can%20not%20load%20load_engine%20because%20there%20is%20no%20tensorrt%20module%22)%0A%20%20%20%20%20%20%20%20exit(1)" message="" highlight="" provider="manual"/] 接著是解析預測結果的副函式,通常我們會取得到一組預測的信心指數,我們需要針對這組數據去解析出最大數值是在哪一個位置,而該位置又屬於哪一個類別: [pastacode lang="python" manual="%23%20%E8%A7%A3%E6%9E%90%E8%BC%B8%E5%87%BA%E8%B3%87%E8%A8%8A%0A%0Adef%20parse_output(preds%2C%20label)%20-%3E%20'return%20(%20class%20id%2C%20class%20name%2C%20probobility)%20'%3A%0A%20%20%20%20%0A%20%20%20%20preds%20%3D%20preds%5B0%5D%20if%20len(preds.shape)%3D%3D4%20else%20preds%0A%20%20%20%20trg_id%20%3D%20np.argmax(preds)%0A%20%20%20%20trg_name%20%3D%20label%5Btrg_id%5D%0A%20%20%20%20trg_prob%20%3D%20preds%5Btrg_id%5D%0A%20%20%20%20return%20(%20trg_id%2C%20trg_name%2C%20trg_prob)" message="" highlight="" provider="manual"/] 截至目前為止的程式,我都將其放在tools.py裡,後續只要做import的動作即可將這些功能導入。 最後來到主程式的部分,這部分須要涵蓋IFTTT以及Inference,流程大致如下: 1.取得模型與標籤、開啟即時影像的線程: [pastacode lang="python" manual="%23%20%E5%8F%96%E5%BE%97%E6%A8%A1%E5%9E%8B%E8%88%87%E6%A8%99%E7%B1%A4%0A%0Amodel%2C%20label%20%3D%20load_model_folder('keras_models')%0A%0A%23%20%E8%A8%AD%E5%AE%9A%E5%BD%B1%E5%83%8F%E6%93%B7%E5%8F%96%0A%0Avid%20%3D%20CustomVideoCapture()%0Avid.set_title('%7Bsys%7D%20-%20%7Bframework%7D'.format(sys%3D'Jetson%20Nano'%2C%20framework%3D'Tensorflow'))%0Avid.start_stream()" message="" highlight="" provider="manual"/] 2.設定辨識的參數,主要用於控制幾秒辨識一次 ( t_delay ),與上次辨識結果不同才進行傳送 ( pre_id ): [pastacode lang="python" manual="%23%20%E8%A8%AD%E5%AE%9A%E5%B9%BE%E7%A7%92%E8%BE%A8%E8%AD%98%E4%B8%80%E6%AC%A1%EF%BC%8C%E9%99%8D%E4%BD%8E%E9%81%8B%E8%A1%8C%E8%B2%A0%E6%93%94%0A%0At_check%20%3D%200%0At_delay%20%3D%202%0At_start%20%3D%200%0A%23%20%E5%84%B2%E5%AD%98%E4%B8%8A%E4%B8%80%E6%AC%A1%E8%BE%A8%E8%AD%98%E7%9A%84%E7%B5%90%E6%9E%9C%EF%BC%8C%E5%A6%82%E6%9E%9C%E6%94%B9%E8%AE%8A%E6%89%8D%E5%82%B3%E9%80%81%EF%BC%8C%E9%98%B2%E6%AD%A2ifttt%E8%B2%A0%E6%93%94%E5%A4%AA%E5%A4%A7%0A%0Apre_id%20%3D%20-1" message="" highlight="" provider="manual"/] 3.設定IFTTT的參數: [pastacode lang="python" manual="%23%20%E8%A8%AD%E5%AE%9A%E3%80%8CLine%E8%A8%8A%E6%81%AF%E3%80%8D%E8%B3%87%E8%A8%8A%0A%0Aevent%20%3D%20'jetsonnano_line'%0Akey%20%3D%20'i3_S_gIAsOty30yvIg4vg'%0Astatus%20%3D%20%7B%0A%20%20%20%200%3A%5B'%E6%98%AF%E6%9C%AC%E4%BA%BA'%2C%20'%E7%A2%BA%E5%AE%9A%E6%9C%89%E5%81%9A%E5%A5%BD%E9%98%B2%E7%96%AB%E5%B7%A5%E4%BD%9C'%5D%2C%0A%20%20%20%201%3A%5B'%E6%98%AF%E6%9C%AC%E4%BA%BA'%2C%20'%E6%B3%A8%E6%84%8F%EF%BC%8C%E5%B7%B2%E6%88%90%E7%82%BA%E9%98%B2%E7%96%AB%E7%A0%B4%E5%8F%A3'%5D%2C%20%0A%20%20%20%202%3A%5B'%E9%9B%A2%E9%96%8B%E4%BD%8D%E7%BD%AE'%2C%20''%5D%2C%20%0A%20%20%20%203%3A%5B'%E9%9D%9E%E6%9C%AC%E4%BA%BA'%2C%20'%E6%B3%A8%E6%84%8F%E6%82%A8%E7%9A%84%E8%B2%A1%E7%94%A2'%5D%0A%20%20%20%20%7D" message="" highlight="" provider="manual"/] 4.使用While不斷進行即時的辨識與LINE監控,這邊設定了如果大於預設的delay時間則進行辨識: [pastacode lang="python" manual="%23%20%E9%96%8B%E5%A7%8B%E5%8D%B3%E6%99%82%E8%BE%A8%E8%AD%98%0A%0At_start%20%3D%20time.time()%0Awhile(not%20vid.isStop)%3A%0A%0A%20%20%20%20%0A%23%20%E8%A8%88%E7%AE%97%E6%99%82%E9%96%93%E5%A6%82%E6%9E%9C%E5%A4%A7%E6%96%BC%E9%A0%90%E8%A8%AD%E5%BB%B6%E9%81%B2%E6%99%82%E9%96%93%E5%89%87%E9%80%B2%E8%A1%8C%E8%BE%A8%E8%AD%98%E8%88%87%E7%99%BC%E9%80%81%0A%0A%20%20%20%20t_check%20%3D%20time.time()%20-%20t_start%0A%0A%20%20%20%20if%20(t_check%20%3E%3D%20t_delay)%20or%20(%20not%20vid.fps)%3A%0A%0A%20%20%20%20%20%20%20%20%0A%23%20%E5%8F%96%E5%BE%97%E7%95%B6%E5%89%8D%E5%9C%96%E7%89%87%0A%0A%20%20%20%20%20%20%20%20ret%2C%20frame%20%3D%20vid.get_current_frame()%0A%0A%20%20%20%20%20%20%20%20%0A%23%20%E5%A6%82%E6%9E%9C%E6%B2%92%E6%9C%89%E5%B9%80%E5%89%87%E9%87%8D%E6%96%B0%E5%9F%B7%E8%A1%8C%0A%0A%20%20%20%20%20%20%20%20if%20not%20ret%3A%20continue" message="" highlight="" provider="manual"/] 5.進行推論以及取得辨識結果,最後設定顯示在即時影像上的資訊: [pastacode lang="python" manual="%23%20%E9%80%B2%E8%A1%8C%E8%99%95%E7%90%86%E8%88%87%E6%8E%A8%E8%AB%96%0A%0A%20%20%20%20%20%20%20%20data%20%3D%20preprocess(frame%2C%20resize%3D(224%2C224)%2C%20norm%3DTrue)%0A%20%20%20%20%20%20%20%20prediction%20%3D%20model(data)%5B0%5D%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%0A%23%20%E8%A7%A3%E6%9E%90%20%E8%BE%A8%E8%AD%98%E7%B5%90%E6%9E%9C%0A%0A%20%20%20%20%20%20%20%20trg_id%2C%20trg_class%2C%20trg_prob%20%3Dparse_output(prediction%2C%20label)%0A%0A%20%20%20%20%20%20%20%20%0A%23%20%E8%A8%AD%E5%AE%9A%E9%A1%AF%E7%A4%BA%E8%B3%87%E8%A8%8A%0A%0A%20%20%20%20%20%20%20%20vid.info%20%3D%20'%7B%7D%20%3A%20%7B%3A.3f%7D%20%2C%20FPS%20%7B%7D'.format(trg_class%2C%20trg_prob%2C%20vid.get_fps())" message="" highlight="" provider="manual"/] 6.如果辨識結果與上次的不同,則回傳給LINE: [pastacode lang="python" manual="%20%20%20%20%20%20%20%20if%20pre_id%20!%3D%20trg_id%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20ifttt.send_to_webhook(event%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20key%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20'%E7%92%B0%E5%A2%83%E8%AE%8A%E5%8B%95'%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20status%5Btrg_id%5D%5B0%5D%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20status%5Btrg_id%5D%5B1%5D%20if%20status%5Btrg_id%5D%5B1%5D%20else%20'')%0A%20%20%20%20%20%20%20%20%20%20%20%20pre_id%20%3D%20trg_id%0A%0A%20%20%20%20%20%20%20%20%0A%23%20%E6%9B%B4%E6%96%B0%20time%0A%0A%20%20%20%20%20%20%20%20t_start%20%3D%20time.time()" message="" highlight="" provider="manual"/] 7.最後在While的外部需要確認一下Thread是否都有關閉了,寫多線程很常遇到的問題就是開了線程,但是忘記關閉導致資源被用完,所以做個DoubleCheck會是不錯的選擇: [pastacode lang="python" manual="%23%20%E8%B7%B3%E5%87%BA%20while%20%E8%BF%B4%E5%9C%88%E9%9C%80%E8%A6%81%E6%AA%A2%E6%9F%A5%E5%A4%9A%E7%B7%9A%E7%A8%8B%E6%98%AF%E5%90%A6%E5%B7%B2%E7%B6%93%E9%97%9C%E9%96%89%0A%0Atime.sleep(1)%0Aprint('-'*30)%0Aprint(f'%E5%BD%B1%E5%83%8F%E4%B8%B2%E6%B5%81%E7%9A%84%E7%B7%9A%E7%A8%8B%E6%98%AF%E5%90%A6%E5%B7%B2%E9%97%9C%E9%96%89%20%3A%20%7Bnot%20vid.t.is_alive()%7D')%0Aprint('%E9%9B%A2%E9%96%8B%E7%A8%8B%E5%BC%8F')" message="" highlight="" provider="manual"/] 完整主程式如下: [pastacode lang="python" manual="%23%25%25%0A%0Aimport%20cv2%0Aimport%20threading%0Aimport%20os%2C%20time%2C%20random%0Aimport%20ifttt%0Aimport%20numpy%20as%20np%0Aimport%20tensorflow%20as%20tf%0Aimport%20platform%20as%20plt%0Afrom%20tools%20import%20CustomVideoCapture%2C%20preprocess%2C%20load_model_folder%2C%20parse_output%0Aimport%20time%0A%0A%23%20%E5%8F%96%E5%BE%97%E6%A8%A1%E5%9E%8B%E8%88%87%E6%A8%99%E7%B1%A4%0A%0Amodel%2C%20label%20%3D%20load_model_folder('keras_models')%0A%0A%23%20%E8%A8%AD%E5%AE%9A%E5%BD%B1%E5%83%8F%E6%93%B7%E5%8F%96%0A%0Avid%20%3D%20CustomVideoCapture()%0Avid.set_title('%7Bsys%7D%20-%20%7Bframework%7D'.format(sys%3D'Jetson%20Nano'%2C%20framework%3D'Tensorflow'))%0Avid.start_stream()%0A%0A%23%20%E8%A8%AD%E5%AE%9A%E5%B9%BE%E7%A7%92%E8%BE%A8%E8%AD%98%E4%B8%80%E6%AC%A1%EF%BC%8C%E9%99%8D%E4%BD%8E%E9%81%8B%E8%A1%8C%E8%B2%A0%E6%93%94%0A%0At_check%20%3D%200%0At_delay%20%3D%202%0At_start%20%3D%200%0A%23%20%E5%84%B2%E5%AD%98%E4%B8%8A%E4%B8%80%E6%AC%A1%E8%BE%A8%E8%AD%98%E7%9A%84%E7%B5%90%E6%9E%9C%EF%BC%8C%E5%A6%82%E6%9E%9C%E6%94%B9%E8%AE%8A%E6%89%8D%E5%82%B3%E9%80%81%EF%BC%8C%E9%98%B2%E6%AD%A2ifttt%E8%B2%A0%E6%93%94%E5%A4%AA%E5%A4%A7%0A%0Apre_id%20%3D%20-1%0A%0A%23%20%E8%A8%AD%E5%AE%9A%E3%80%8CLine%E8%A8%8A%E6%81%AF%E3%80%8D%E8%B3%87%E8%A8%8A%0A%0Aevent%20%3D%20'jetsonnano_line'%0Akey%20%3D%20'i3_S_gIAsOty30yvIg4vg'%0Astatus%20%3D%20%7B%0A%20%20%20%200%3A%5B'%E6%98%AF%E6%9C%AC%E4%BA%BA'%2C%20'%E7%A2%BA%E5%AE%9A%E6%9C%89%E5%81%9A%E5%A5%BD%E9%98%B2%E7%96%AB%E5%B7%A5%E4%BD%9C'%5D%2C%0A%20%20%20%201%3A%5B'%E6%98%AF%E6%9C%AC%E4%BA%BA'%2C%20'%E6%B3%A8%E6%84%8F%EF%BC%8C%E5%B7%B2%E6%88%90%E7%82%BA%E9%98%B2%E7%96%AB%E7%A0%B4%E5%8F%A3'%5D%2C%20%0A%20%20%20%202%3A%5B'%E9%9B%A2%E9%96%8B%E4%BD%8D%E7%BD%AE'%2C%20''%5D%2C%20%0A%20%20%20%203%3A%5B'%E9%9D%9E%E6%9C%AC%E4%BA%BA'%2C%20'%E6%B3%A8%E6%84%8F%E6%82%A8%E7%9A%84%E8%B2%A1%E7%94%A2'%5D%0A%20%20%20%20%7D%0A%23%25%25%0A%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%23%20%E9%96%8B%E5%A7%8B%E5%8D%B3%E6%99%82%E8%BE%A8%E8%AD%98%0A%0At_start%20%3D%20time.time()%0Awhile(not%20vid.isStop)%3A%0A%0A%20%20%20%20%0A%23%20%E8%A8%88%E7%AE%97%E6%99%82%E9%96%93%E5%A6%82%E6%9E%9C%E5%A4%A7%E6%96%BC%E9%A0%90%E8%A8%AD%E5%BB%B6%E9%81%B2%E6%99%82%E9%96%93%E5%89%87%E9%80%B2%E8%A1%8C%E8%BE%A8%E8%AD%98%E8%88%87%E7%99%BC%E9%80%81%0A%0A%20%20%20%20t_check%20%3D%20time.time()%20-%20t_start%0A%0A%20%20%20%20if%20(t_check%20%3E%3D%20t_delay)%20or%20(%20not%20vid.fps)%3A%0A%0A%20%20%20%20%20%20%20%20%0A%23%20%E5%8F%96%E5%BE%97%E7%95%B6%E5%89%8D%E5%9C%96%E7%89%87%0A%0A%20%20%20%20%20%20%20%20ret%2C%20frame%20%3D%20vid.get_current_frame()%0A%0A%20%20%20%20%20%20%20%20%0A%23%20%E5%A6%82%E6%9E%9C%E6%B2%92%E6%9C%89%E5%B9%80%E5%89%87%E9%87%8D%E6%96%B0%E5%9F%B7%E8%A1%8C%0A%0A%20%20%20%20%20%20%20%20if%20not%20ret%3A%20continue%0A%0A%20%20%20%20%20%20%20%20%0A%23%20%E9%80%B2%E8%A1%8C%E8%99%95%E7%90%86%E8%88%87%E6%8E%A8%E8%AB%96%0A%0A%20%20%20%20%20%20%20%20data%20%3D%20preprocess(frame%2C%20resize%3D(224%2C224)%2C%20norm%3DTrue)%0A%20%20%20%20%20%20%20%20prediction%20%3D%20model(data)%5B0%5D%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%0A%23%20%E8%A7%A3%E6%9E%90%20%E8%BE%A8%E8%AD%98%E7%B5%90%E6%9E%9C%0A%0A%20%20%20%20%20%20%20%20trg_id%2C%20trg_class%2C%20trg_prob%20%3Dparse_output(prediction%2C%20label)%0A%0A%20%20%20%20%20%20%20%20%0A%23%20%E8%A8%AD%E5%AE%9A%E9%A1%AF%E7%A4%BA%E8%B3%87%E8%A8%8A%0A%0A%20%20%20%20%20%20%20%20vid.info%20%3D%20'%7B%7D%20%3A%20%7B%3A.3f%7D%20%2C%20FPS%20%7B%7D'.format(trg_class%2C%20trg_prob%2C%20vid.get_fps())%0A%0A%20%20%20%20%20%20%20%20%0A%23%20%E5%A6%82%E6%9E%9C%E8%88%87%E4%B8%8A%E6%AC%A1%E8%BE%A8%E8%AD%98%E4%B8%8D%E5%90%8C%EF%BC%8C%E5%89%87%E5%B0%87%E8%BE%A8%E8%AD%98%E5%88%B0%E7%9A%84%E7%B5%90%E6%9E%9C%E5%82%B3%E9%80%81%E8%87%B3Line%0A%0A%20%20%20%20%20%20%20%20if%20pre_id%20!%3D%20trg_id%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20ifttt.send_to_webhook(event%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20key%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20'%E7%92%B0%E5%A2%83%E8%AE%8A%E5%8B%95'%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20status%5Btrg_id%5D%5B0%5D%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20status%5Btrg_id%5D%5B1%5D%20if%20status%5Btrg_id%5D%5B1%5D%20else%20'')%0A%20%20%20%20%20%20%20%20%20%20%20%20pre_id%20%3D%20trg_id%0A%0A%20%20%20%20%20%20%20%20%0A%23%20%E6%9B%B4%E6%96%B0%20time%0A%0A%20%20%20%20%20%20%20%20t_start%20%3D%20time.time()%0A%0A%23%20%E8%B7%B3%E5%87%BA%20while%20%E8%BF%B4%E5%9C%88%E9%9C%80%E8%A6%81%E6%AA%A2%E6%9F%A5%E5%A4%9A%E7%B7%9A%E7%A8%8B%E6%98%AF%E5%90%A6%E5%B7%B2%E7%B6%93%E9%97%9C%E9%96%89%0A%0Atime.sleep(1)%0Aprint('-'*30)%0Aprint(f'%E5%BD%B1%E5%83%8F%E4%B8%B2%E6%B5%81%E7%9A%84%E7%B7%9A%E7%A8%8B%E6%98%AF%E5%90%A6%E5%B7%B2%E9%97%9C%E9%96%89%20%3A%20%7Bnot%20vid.t.is_alive()%7D')%0Aprint('%E9%9B%A2%E9%96%8B%E7%A8%8B%E5%BC%8F')" message="" highlight="" provider="manual"/] 可以發現使用Thread來運行影像就完全不會受到IFTTT的影響,FPS都可以維持在30甚至以上,而主線程只需要關注於辨識以及傳送資料給IFTTT即可。 https://youtu.be/Vk7pFz6L-Cw

使用TensorRT引擎加速推論

剛剛使用了Thread來改善IFTTT傳送卡頓的問題,我們也可以針對AI推論來做改善,我們使用Jetson Nano最大的優勢就在於可以使用TensorRT引擎加速處理,所以這邊教大家怎麼從Teachable Machine下載模型並轉換成TensorRT引擎。

概略介紹

TensorRT是一個支援NVIDIA CUDA核心的加速引擎,透過對神經網路模型進行重構與資料縮減來達到加速的目的,在Jetson Nano中使用TensorRT絕對是做AI Inference的首選,那如何將神經網路模型轉換成TensorRT去運行呢? 1.需要先將模型轉換成 Onnx 的通用格式 2.接著在轉換成 TensorRT 引擎可運作的格式 在Jetson Nano中已經帶有TensorRT轉換的工具,但是怎麼將模型轉換成Onnx還需要安裝額外的工具,所以我們先來安裝一下tf2onnx這個套件吧。

環境版本

JetPack 4.4.1
Python 3.6.9
pip 21.0
tensorflow 2.3.1+nv20.12
onnx 1.8.1

安裝 tf2onnx並將模型轉換成onnx

首先需要將tensorflow的模型轉換成onnx,我們將使用tf2onnx這個套件,在安裝之前需要先確保onnx已經被安裝了,這邊提供相依套件以onnx的安裝命令: [pastacode lang="python" manual="%24%20sudo%20apt-get%20install%20protobuf-compiler%20libprotoc-dev%20%0A%23%20onnx%20%E7%9B%B8%E4%BE%9D%E5%A5%97%E4%BB%B6%0A%0A%24%20pip3%20install%20onnx%0A%24%20pip%20install%20onnxruntime" message="" highlight="" provider="manual"/] 升級numpy (可有可無): [pastacode lang="python" manual="%24%20python3%20-m%20pip%20install%20-U%20numpy%20--no-cache-dir%20--no-binary%20numpy" message="" highlight="" provider="manual"/] 安裝tf2onnx: [pastacode lang="python" manual="%24%20pip3%20install%20tf2onnx" message="" highlight="" provider="manual"/] 宣告OpenBLAS的核心架構,在JetsonNano上少了這步應該會報錯誤訊息” Illegal instruction(core dumped)”: [pastacode lang="python" manual="%24%20nano%20~%2F.bashrc%0Aexport%20OPENBLAS_CORETYPE%3DARMV8%0A%24%20source%20~%2F.bashrc%09" message="" highlight="" provider="manual"/] 安裝完之後可以回到上次教學的Teachable Machine,這次要下載的檔案格式必須選擇成TensorFlow > Savemodel,如下圖所示: Savemodel是Tensorflow模型「序列化」的格式,由於Onnx的格式也是序列化的,所以在一開始就轉換成Savemodel在後續轉換Onnx比較不容易出錯。我們可以使用執行下列指令轉換成onnx模型: [pastacode lang="python" manual="%24%20python3%20-m%20tf2onnx.convert%20--saved-model%20.%2Fsavemodel%20--output%20.%2Ftest_opset_default.onnx" message="" highlight="" provider="manual"/]

透過Jetson Nano內建工具轉換成TensorRT

接著可以使用JetsonNano的原生工具 (trtexec) 轉換成TensorRT: [pastacode lang="python" manual="%24%20%2Fusr%2Fsrc%2Ftensorrt%2Fbin%2Ftrtexec%20--onnx%3D%2Fhome%2Fdlinano%2FTM2%2Ftest_opset_default.onnx%20--saveEngine%3D%2Fhome%2Fdlinano%2FTM2%2Ftest.trt%20--shapes%3Dinput0%3A1x3x224x224" message="" highlight="" provider="manual"/] 同時需要安裝pycuda,安裝步驟當中有一個nvcc是用來確認是否有抓到cuda,若沒有加入環境變數則會報錯,同時也無法安裝pycuda: [pastacode lang="python" manual="%24%20nano%20~%2F.bashrc%0Aexport%20PATH%3D%24%7BPATH%7D%3A%2Fusr%2Flocal%2Fcuda%2Fbin%0Aexport%20LD_LIBRARY_PATH%3D%24%7BLD_LIBRARY_PATH%7D%3A%2Fusr%2Flocal%2Fcuda%2Flib64%0A%24%20source%20~%2F.bashrc%0A%24%20nvcc%20-V%0A%24%20pip3%20install%20pycuda" message="" highlight="" provider="manual"/] 由於我們會使用到tensorrt提供的範例common.py,所以先直接複製一份: [pastacode lang="python" manual="%24%20cp%20%2Fusr%2Fsrc%2Ftensorrt%2Fsamples%2Fpython%2Fcommon.py%20.%2Fcommon.py" message="" highlight="" provider="manual"/] 經過繁瑣的操作後,終於可以運行程式了: [pastacode lang="python" manual="%24%20python3%20tm_tensorrt.py" message="" highlight="" provider="manual"/] 這個程式比照上一篇的方法所撰寫,可以注意到FPS相較於之前的推論程式都高非常多,已經可以到順跑的程度了。

程式講解

導入函式庫以及設定TRT的基本參數 [pastacode lang="python" manual="import%20cv2%0Aimport%20tensorrt%20as%20trt%0Aimport%20numpy%20as%20np%0Aimport%20common%0Aimport%20platform%20as%20plt%0Aimport%20time%0Afrom%20tools%20import%20preprocess%2C%20load_model_folder%0A%0ATRT_LOGGER%20%3D%20trt.Logger(trt.Logger.WARNING)%0Atrt_runtime%20%3D%20trt.Runtime(TRT_LOGGER)" message="" highlight="" provider="manual"/] 先取得 TensorRT引擎,透過先前撰寫好的副函式 ( load_model_folder ) 來取得 engine、label;再導入之前我們需要預先定義好buffer給TensorRT;接著解析TensorRT物件取得該「執行文本」: [pastacode lang="python" manual="%20load%20trt%20engine%0Aprint('%E5%8F%96%E5%BE%97TRT%E5%BC%95%E6%93%8E%E8%88%87%E6%A8%99%E7%B1%A4')%0Aengine%2C%20label%20%3D%20load_model_folder('tensorrt_engine')%0A%0A%23%20allocate%20buffers%0A%0Aprint('%E5%88%86%E9%85%8D%20buffers%20%E7%B5%A6%20TensorRT%20%E6%89%80%E9%A0%88%E7%9A%84%E7%89%A9%E4%BB%B6')%0Ainputs%2C%20outputs%2C%20bindings%2C%20stream%20%3D%20common.allocate_buffers(engine)%0A%0Aprint('%E5%89%B5%E5%BB%BA%E5%9F%B7%E8%A1%8C%E6%96%87%E6%9C%AC%20(%20context%20)')%0Acontext%20%3D%20engine.create_execution_context()" message="" highlight="" provider="manual"/] 接著我們使用與上一篇雷同的OpenCV程式完成即時影像辨識,最大的區別在於TensorRT引擎導入資料的方法與推論的方法: [pastacode lang="python" manual="print('%E9%96%8B%E5%95%9F%E5%8D%B3%E6%99%82%E5%BD%B1%E5%83%8F')%0Afps%20%3D%20-1%0Acap%20%3D%20cv2.VideoCapture(0%2C%20cv2.CAP_GSTREAMER)%0A%0Awhile(True)%3A%0A%0A%20%20%20%20t_start%20%3D%20time.time()%0A%0A%20%20%20%20%0A%23%20%E8%AE%80%E5%8F%96%E5%9C%96%E7%89%87%0A%0A%20%20%20%20ret%2C%20frame%20%3D%20cap.read()%0A%0A%20%20%20%20%0A%23%20%E5%B0%87%E5%9C%96%E7%89%87%E9%80%B2%E8%A1%8C%E5%89%8D%E8%99%95%E7%90%86%E4%B8%A6%E6%94%BE%E5%85%A5%E8%BC%B8%E5%85%A5%E8%B3%87%E6%96%99%E4%B8%AD%0A%0A%20%20%20%20inputs%5B0%5D.host%20%3D%20preprocess(frame)%0A%0A%20%20%20%20%0A%23%20%E9%80%B2%E8%A1%8C%20Inference%0A%0A%20%20%20%20trt_outputs%20%3D%20common.do_inference(context%2C%20bindings%3Dbindings%2C%20inputs%3Dinputs%2C%20outputs%3Doutputs%2C%20stream%3Dstream)%0A%20%20%20%20%0A%20%20%20%20%0A%23%20%E8%A7%A3%E6%9E%90%E8%BC%B8%E5%87%BA%E8%B3%87%E6%96%99%0A%0A%20%20%20%20trg_idx%2C%20trg_class%2C%20trg_prob%20%3D%20parse_output(trt_outputs%5B0%5D%2C%20label)%0A%0A%20%20%20%20%0A%23%20%E8%A8%AD%E5%AE%9A%E9%A1%AF%E7%A4%BA%E8%B3%87%E6%96%99%0A%0A%20%20%20%20info%20%3D%20'%7B%7D%20%3A%20%7B%3A.3f%7D%20%2C%20FPS%20%7B%7D'.format(trg_class%2C%20trg_prob%2C%20fps)%0A%0A%20%20%20%20%0A%23%20%E5%B0%87%E9%A1%AF%E7%A4%BA%E8%B3%87%E6%96%99%E7%B9%AA%E8%A3%BD%E5%9C%A8%E5%9C%96%E7%89%87%E4%B8%8A%0A%0A%20%20%20%20cv2.putText(frame%2C%20info%2C%20(10%2C40)%2C%20cv2.FONT_HERSHEY_SIMPLEX%2C%201%2C%20(0%2C0%2C255)%2C%202)%0A%20%20%20%20cv2.imshow('TensorRT'%2C%20frame)%0A%0A%20%20%20%20if%20cv2.waitKey(1)%20%3D%3D%20ord('q')%3A%0A%20%20%20%20%20%20%20%20break%0A%20%20%20%20%0A%23%20%E6%9B%B4%E6%96%B0FPS%E8%88%87%E6%99%82%E9%96%93%E9%BB%9E%0A%0A%20%20%20%20fps%20%3D%20int(1%2F(time.time()-t_start))%0A%20%20%20%20t_start%20%3D%20time.time()" message="" highlight="" provider="manual"/] 最後離開的時候一樣要做確認的動作: [pastacode lang="python" manual="cap.release()%0Acv2.destroyAllWindows()%0Aprint('%E9%9B%A2%E9%96%8B%E7%A8%8B%E5%BC%8F')" message="" highlight="" provider="manual"/]

三種框架比較

既然都做到TensorRT加速了,我們還是得來比較一下速度差距(僅供參考): 可以注意到Tensorflow的速度最慢但是準確度最高;Tensorflow Lite則是犧牲準確度換取高效能的表現;而TensorRT就更優秀了,優化的時候保留更多準確度,效能也能有效提高。

TensorRT結合Thread與IFTTT

建構的方式與上述雷同,所以就直接提供完整程式: [pastacode lang="python" manual="import%20cv2%0Aimport%20tensorrt%20as%20trt%0Aimport%20numpy%20as%20np%09%0Aimport%20common%0Aimport%20platform%20as%20plt%0Aimport%20time%0Aimport%20ifttt%0Aimport%20threading%0Afrom%20tools%20import%20CustomVideoCapture%2C%20preprocess%2C%20load_model_folder%2C%20parse_output%0A%0ATRT_LOGGER%20%3D%20trt.Logger(trt.Logger.WARNING)%0Atrt_runtime%20%3D%20trt.Runtime(TRT_LOGGER)%0A%0Adef%20main()%3A%0A%0A%20%20%20%20pre_idx%20%3D%20-1%0A%0A%20%20%20%20print('%E5%8F%96%E5%BE%97TRT%E5%BC%95%E6%93%8E%E8%88%87%E6%A8%99%E7%B1%A4')%0A%20%20%20%20engine%2C%20label%20%3D%20load_model_folder('tensorrt_engine')%0A%0A%20%20%20%20print('%E5%88%86%E9%85%8D%20buffers%20%E7%B5%A6%20TensorRT%20%E6%89%80%E9%A0%88%E7%9A%84%E7%89%A9%E4%BB%B6')%0A%20%20%20%20inputs%2C%20outputs%2C%20bindings%2C%20stream%20%3D%20common.allocate_buffers(engine)%0A%0A%20%20%20%20print('%E5%89%B5%E5%BB%BA%E5%9F%B7%E8%A1%8C%E6%96%87%E6%9C%AC%20(%20context%20)')%0A%20%20%20%20context%20%3D%20engine.create_execution_context()%0A%0A%20%20%20%20print('%E8%A8%AD%E5%AE%9A%E5%8D%B3%E6%99%82%E5%BD%B1%E5%83%8F%E5%8F%83%E6%95%B8')%0A%20%20%20%20vid%20%3D%20CustomVideoCapture()%0A%20%20%20%20vid.set_title('%7Bsys%7D%20-%20%7Bframework%7D'.format(sys%3D'Jetson%20Nano'%2C%20framework%3D'TensorRT'))%0A%20%20%20%20vid.start_stream()%0A%20%20%20%20%0A%20%20%20%20%0A%23%20%E8%A8%AD%E5%AE%9A%E5%B9%BE%E7%A7%92%E8%BE%A8%E8%AD%98%E4%B8%80%E6%AC%A1%EF%BC%8C%E7%82%BA%E4%BA%86%E9%85%8D%E5%90%88%20ifttt%20%E7%9A%84%E5%BB%B6%E9%81%B2%E9%80%9A%E7%9F%A5%0A%0A%20%20%20%20t_check%20%3D%200%0A%20%20%20%20t_delay%20%3D%201%0A%20%20%20%20t_start%20%3D%200%0A%0A%20%20%20%20%0A%23%20%E5%84%B2%E5%AD%98%E4%B8%8A%E4%B8%80%E6%AC%A1%E8%BE%A8%E8%AD%98%E7%9A%84%E7%B5%90%E6%9E%9C%EF%BC%8C%E5%A6%82%E6%9E%9C%E6%94%B9%E8%AE%8A%E6%89%8D%E5%82%B3%E9%80%81%EF%BC%8C%E9%98%B2%E6%AD%A2ifttt%E8%B2%A0%E6%93%94%E5%A4%AA%E5%A4%A7%0A%0A%20%20%20%20pre_id%20%3D%20-1%0A%0A%20%20%20%20%0A%23%20%E8%A8%AD%E5%AE%9A%E3%80%8CLine%E8%A8%8A%E6%81%AF%E3%80%8D%E8%B3%87%E8%A8%8A%0A%0A%20%20%20%20print('%E8%A8%AD%E5%AE%9AIFTTT%E5%8F%83%E6%95%B8')%0A%20%20%20%20event%20%3D%20'jetsonnano_line'%0A%20%20%20%20key%20%3D%20'i3_S_gIAsOty30yvIg4vg'%0A%20%20%20%20status%20%3D%20%7B%0A%20%20%20%20%20%20%20%200%3A%5B'%E6%98%AF%E6%9C%AC%E4%BA%BA'%2C%20'%E7%A2%BA%E5%AE%9A%E6%9C%89%E5%81%9A%E5%A5%BD%E9%98%B2%E7%96%AB%E5%B7%A5%E4%BD%9C'%5D%2C%0A%20%20%20%20%20%20%20%201%3A%5B'%E6%98%AF%E6%9C%AC%E4%BA%BA'%2C%20'%E6%B3%A8%E6%84%8F%EF%BC%8C%E5%B7%B2%E6%88%90%E7%82%BA%E9%98%B2%E7%96%AB%E7%A0%B4%E5%8F%A3'%5D%2C%20%0A%20%20%20%20%20%20%20%202%3A%5B'%E9%9B%A2%E9%96%8B%E4%BD%8D%E7%BD%AE'%2C%20''%5D%2C%20%0A%20%20%20%20%20%20%20%203%3A%5B'%E9%9D%9E%E6%9C%AC%E4%BA%BA'%2C%20'%E6%B3%A8%E6%84%8F%E6%82%A8%E7%9A%84%E8%B2%A1%E7%94%A2'%5D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20t_start%20%3D%20time.time()%0A%20%20%20%20%0A%20%20%20%20while(not%20vid.isStop)%3A%0A%0A%20%20%20%20%20%20%20%20%0A%23%20%E8%A8%88%E7%AE%97%E6%99%82%E9%96%93%E5%A6%82%E6%9E%9C%E5%A4%A7%E6%96%BC%E9%A0%90%E8%A8%AD%E5%BB%B6%E9%81%B2%E6%99%82%E9%96%93%E5%89%87%E9%80%B2%E8%A1%8C%E8%BE%A8%E8%AD%98%E8%88%87%E7%99%BC%E9%80%81%0A%0A%20%20%20%20%20%20%20%20t_check%20%3D%20time.time()-t_start%0A%20%20%20%20%20%20%20%20if%20t_check%20%3E%3D%20t_delay%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20ret%2C%20frame%20%3D%20vid.get_current_frame()%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20not%20ret%3A%20continue%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20inputs%5B0%5D.host%20%3D%20preprocess(frame%2C%20resize%3D(224%2C%20224)%2C%20norm%3DTrue)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20infer_time%20%3D%20time.time()%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%23%20with%20engine.create_execution_context()%20as%20context%3A%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20trt_outputs%20%3D%20common.do_inference(context%2C%20bindings%3Dbindings%2C%20inputs%3Dinputs%2C%20outputs%3Doutputs%2C%20stream%3Dstream)%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20infer_time%20%3D%20time.time()%20-%20infer_time%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20preds%20%3D%20trt_outputs%5B0%5D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20trg_id%2C%20trg_class%2C%20trg_prob%20%3D%20parse_output(preds%2C%20label)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20vid.info%20%3D%20'%7B%7D%20%3A%20%7B%3A.3f%7D%20%2C%20FPS%20%3A%20%7B%3A.3f%7D'.format(trg_class%2C%20trg_prob%2C%20vid.get_fps())%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20pre_id%20!%3D%20trg_id%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ifttt.send_to_webhook(event%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20key%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20'%E7%92%B0%E5%A2%83%E8%AE%8A%E5%8B%95'%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20status%5Btrg_id%5D%5B0%5D%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20status%5Btrg_id%5D%5B1%5D%20if%20status%5Btrg_id%5D%5B1%5D%20else%20'')%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pre_id%20%3D%20trg_id%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20t_start%20%3D%20time.time()%0A%20%20%20%20%0A%20%20%20%20%0A%23%20%E8%B7%B3%E5%87%BA%20while%20%E8%BF%B4%E5%9C%88%E9%9C%80%E8%A6%81%E6%AA%A2%E6%9F%A5%E5%A4%9A%E7%B7%9A%E7%A8%8B%E6%98%AF%E5%90%A6%E5%B7%B2%E7%B6%93%E9%97%9C%E9%96%89%0A%0A%20%20%20%20time.sleep(1)%0A%20%20%20%20print('-'*30%2C%20'%5Cn')%0A%20%20%20%20print(f'%E5%BD%B1%E5%83%8F%E4%B8%B2%E6%B5%81%E7%9A%84%E7%B7%9A%E7%A8%8B%E6%98%AF%E5%90%A6%E5%B7%B2%E9%97%9C%E9%96%89%20%3A%20%7Bnot%20vid.t.is_alive()%7D')%0A%0Aif%20__name__%20%3D%3D%20'__main__'%3A%0A%20%20%20%20%0A%20%20%20%20main()" message="" highlight="" provider="manual"/]

結語

這次我們使用了兩種方式來進行改造、加速,其實透過Thread就能有不錯的成果了,但是TensorRT又能再減少一些負擔,讓 AI辨識與Line的監控訊息可以變得更加確實、快速。

相關文章

Onnx-tensorrt Github Program/Process/Thread 差異   *本文由RS components 贊助發表,轉載自DesignSpark部落格原文連結(本篇文章完整範例程式請至原文下載)

相關文章 📎

留言 💬 (0)

還沒有留言,來當第一個。