NVIDIA Jetson Nano機器學習應用-你戴口罩了嗎?結合Line通訊平台進行即時監控

*本文由RS components 贊助發表,轉載自DesignSpark部落格原文連結

作者/攝影張嘉鈞
難度

★★☆☆☆(普通)

材料表

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

關於IFTTT

IFTTT 的全名為「If This Then That」,及是觸發了A事件之後就會做出B動作,基本上不用寫程式,也可以有幾百個服務可以進行串接,目前有三個免費API的額度,如果使用三個以上的API就要開始收費了。

釐清思緒

在開始之前先來確認一下本次目標,我們希望當辨識到特定物品的時候使用LINE進行即時通知。以IFTTT的角度來說就是「if 辨識到東西 then 傳送訊息到Line」,但是IFTTT沒辦法進行AI辨識,必須透過工具:Webhook, Webhook的功能是當特定網頁有被觸及的時候就會提醒對方,我可以在辨識到特定物品的時候觸及特定網頁並且上傳數值,這時候Webhook會收到回饋,啟動Line的傳送訊息功能,而Line的傳送訊息功能也可以接收剛剛上傳的數值。

申請IFTTT & 創建Applet

1.進入IFTTT首頁,選擇登入帳號或申請新帳號

2.請選擇一種帳號進行登入

3.右上角選擇選擇Create新服務

4.點選Add,進入服務設定。

5.搜尋 Webhooks 服務並點選該圖示

6.點擊 Receive a web request

7.創建事件名稱並點擊Create trigger 啟動輸入觸發。

8.接著點擊Then That 的 Add。

9.搜尋Line並且點擊LINE的icon

10.點擊Send Message

11.因為要連接到 LINE,需要進行登入LINE 的動作。

12.IFTTT透過 LINE Notify 的通知進行推播,選擇同意並連動

連動後會在LINE上彈出這段訊息

13.設定傳送 LINE 訊息的內容

可以選擇要推播到特定群組,或是一對一的 LINE Notify,再來是訊息格式,1個 Webhooks 可以傳送3個數值,後續會教大家怎麼修改。

14.點擊 Continue

15.輸入app的標題簡述,接著可以按Finish

16.完成之後的畫面如下,接著要點擊Webhook的圖標

17.點擊左上角的IFTTT圖案回到首頁,並且點擊Create by me就可以看到剛剛創建的APP

18.點擊右上角的 Documentation

19.進到文件狀態後修改內容進行測試,event欄位為剛剛Webhook創建的名稱 ( jetsonnano_line ),value隨意輸入。

執行結果可以到 Line Notify中查看

編輯Line的訊息格式

20.回到APP的頁面,點擊LINE的圖標

21.點擊 jetsonnano_line 的app方框

22.點擊右上角的setting

23.選擇Send Message右上角的edit

24.修改訊息資訊,變數可以從 Add ingredient選取,有EventName、三個Value以及 OccurredAt ( 最近的時間 ) 可以選擇。

25.回到Webhook的Documentation測試一下結果

到目前就已經完成基本的IFTTT設定了。

使用Python程式連動Line

解析Webhook

開始之前我們先解析一下剛剛在Webhook的Documentation。

Make a POST or GET web request to:

代表我們可以使用POST或GET的方式去觸及Webhook,其中GET通常是下載網頁資訊而POST則是上傳資料,但是在這邊事都可以通用的。

With an optional JSON body of:

他可以使用自定義的參數但是需要使用JSON格式的內容,強制使用類似Python的字典形式,Key的位置是固定的所以寫程式的時候要注意,Key後面的內容則是可以隨機定義的,而這部分就是可以更換成我們辨識的結果。

對這些參數稍微有一點概念了之後就可以開始寫程式囉!

實作Python Code

這邊需要使用到Python的套件 Request,來完成與網頁互動的功能。首先,我們先撰寫了一個副函式叫「send_to_webhook」,並且給予所有能夠修改的參數,像是網址的部分就要包含「事件名稱」、「金鑰」,而後面的引數總共有三個「數值」;利用post或get都可以只要能觸發Webhook的事件即可,我們可以使用”status_code”做個判斷,如果成功的話會回傳response 200,也可以直接使用“codes.ok”。

import requests

def send_to_webhook(event='', key='', val_1='', val_2='', val_3=''):   
    
    trg_url = ('https://maker.ifttt.com/trigger/{event}/with/key/{key}'.format(event=event, key=key))
    
    trg_params = {
        'value1': f'{val_1}',
        'value2': f'{val_2}',
        'value3' : f'{val_3}'
        }

    req = requests.post(trg_url, trg_params)

    if req.status_code == requests.codes.ok:
        print('傳送成功')
    else:
        print('傳送失敗')

接著就可以在外部修改參數了:

if __name__ == '__main__':

    event = 'jetsonnano_line'
    key = 'i3_S_gIAsOty30yvIg4vg'

    val_1 = '使用'
    val_2 = 'Python'
    val_3 = '連動'

    ret = send_to_webhook(event, key, val_1, val_2, val_3)

結合簡易的AI辨識

這部分可以結合各種形式,像是YOLOv4、Jetson Inference等API,在稍微修改一下即可,這邊我提供一個更簡單且我們常用的方法 Teachable Machine,這邊就不介紹太多了,他是一個線上的AI體驗工具。

除了線上訓練之外還可以提供Tensorflow、Tensorflow Lite以及Coral的模型,這邊我順便教大家怎麼去安裝 Tensorflow以及Tensorflow Lite。

使用Teachable Machine訓練

匯出Tensorflow模型

我們直接使用Keras模型即可,選擇Tensorflow並選擇Model conversion type為Keras,點選Download my model即可進行轉換與下載:

在JetsonNano中運行即時影像辨識

首先需要安裝Tensorflow,我們可以直接到這裡照個原廠推薦的方法操作,由於我的Nano 是JetPack 4.4.1,所以要找到 Python3.6 + JetsonPack 4.4的位置:

快速參考指令如下:

$ sudo apt-get install libhdf5-serial-dev hdf5-tools libhdf5-dev zlib1g-dev zip libjpeg8-dev liblapack-dev libblas-dev gfortran
$ sudo apt-get install python3-pip
$ sudo pip3 install -U pip
$ sudo pip3 install -U pip testresources setuptools numpy==1.16.1 future==0.17.1 mock==3.0.5 h5py==2.9.0 keras_preprocessing==1.0.5 keras_applications==1.0.8 gast==0.2.2 futures protobuf pybind11
# TF-2.x

$ sudo pip3 install --pre --extra-index-url https://developer.download.nvidia.com/compute/redist/jp/v44 tensorflow==2.3.1+nv20.12

安裝完之後我們需要使用Teachable Machine匯出的位置下方有提供範例程式,我稍微修改了一下。首先載入相關套件:

import tensorflow.keras
import numpy as np
import cv2
import time
import platform as plt

由於標籤檔是一個文字檔,我寫了一個程式去解析內容並儲存成一個字典:

def get_label(label_path):

    label = {}
    with open(label_path) as f:
        for line in f.readlines():
            idx,name = line.strip().split(' ')
            label[int(idx)] = name
    return label

接著進入主程式,設定np顯示的格式、載入神經網路模型與標籤;data是設定輸入資料形狀 (1, 224, 224, 3);最後fps用來計算即時影像每秒會有幾幀,通常fps越高越好,到了60對於人眼就幾乎感受不到延遲了;cap是儲存攝影機設備:

print('Setting ...')
np.set_printoptions(suppress=True)

print('Load Model & Labels ...')
model = tensorflow.keras.models.load_model('keras_models/model.h5')
label = get_label('keras_models/labels.txt')
data = np.ndarray(shape=(1, 224, 224, 3), dtype=np.float32)

print('Start Stream ...')
fps = -1
cap = cv2.VideoCapture(0)

使用While不斷讀取最近一次的影像;t_start用於計算fps;使用cap.read()會獲取兩個參數,第一個通常被用來代表是否擷取成功 ( 布林值 ),第二個則是影像陣列;接著將圖片丟入模型前還需要進行前處理,縮放大小至224×224,正規化,以及修改成輸入資料的格式:

while(True):

    t_start = time.time()

    ret, frame = cap.read()

    size = (224, 224)
    frame_resize = cv2.resize(frame, size)
    
    frame_norm = (frame_resize.astype(np.float32) / 127.0) - 1

    data[0] = frame_norm

接著使用predict即可完成推論,我們先取得到數值最大的欄位idx,並設定預計要顯示在畫面上的資訊 (result) 再使用 putText將資訊繪製到影像上,最後顯示出來 (imshow),標題可以自己修改;當按下q的時候可以離開,最後釋放攝影機物件並刪除所有視窗:

  prediction = model.predict(data)[0]    
  
  idx = int(np.argmax(prediction))

    result = '{} : {:.3f}, FPS {}'.format(label[idx], prediction[idx], fps)

    cv2.putText(frame, result, (10,40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0 , 0, 255), 1)

    cv2.imshow(win_title, frame)

    if cv2.waitKey(1) == ord('q'):
        break

    fps = int(1/(time.time() - t_start))

cap.release()
cv2.destroyAllWindows()

print('Quit ...')

執行結果如下:

可以看到FPS只有4,不過準確度還蠻高的!但是TensorFlow在JetsonNano上開啟需要一段時間,所以我會推薦轉換成TensorRT的形式,下篇會再介紹。

將辨識結果傳送給Line

剛剛程式當中已經取得了結果 ( results ),我們只需要將這個結果上傳到Line即可,稍早我們寫的ifttt程式將其儲存為ifttt.py方便導入,首先設定事件名稱與金鑰,status用來手動設定辨識結果對應的傳送內容;pre_idx則是用來儲存前一個辨識的結果:

import ifttt

def main(win_title='test'):

    
# 設定「Line訊息」資訊

    event = 'jetsonnano_line'
    key = 'i3_S_gIAsOty30yvIg4vg'
    status = {  0:['是本人', '確定有做好防疫工作'],
                1:['是本人', '注意,已成為防疫破口'], 
                2:['離開位置', ''], 
                3:['非本人', '注意您的財產']    }
    pre_idx = -1

接著將程式碼放到prediction的後面,send_to_webhook詳細說明請往上翻找:

# 判斷是否跟上次辨識一樣的結果,如果不同則進行上傳

if pre_idx != idx:

    ifttt.send_to_webhook(  event, 
                            key, 
                            '環境變動', 
                            status[idx][0], 
                            status[idx][1] if status[idx][1] else '')
    
    pre_idx = idx

執行結果

可以注意到如果沒有傳送資料的時候,FPS都還可以落在4左右,一旦傳送資料就會整co

結論

我們已經學會怎麼將JetsonNano與Line結合的方式了,下一篇我們將帶大家嘗試去將整個影像體驗的效果提升。

 

*本文由RS components 贊助發表,轉載自DesignSpark部落格原文連結(本篇文章完整範例程式請至原文下載)

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *