嘿!您似乎在 United States,您想使用我们的 English 网站吗?
Switch to English site
Skip to main content

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

作者

張嘉鈞

難度

普通

材料表

 

關於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首頁,選擇登入帳號或申請新帳號

image0015_0e025d18d3bffd9eb499534ff030f96ee72b1bf7.png

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

image0021_aa83be2e4876be2d8be71047059de2c90a1413e7.png

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

image0033_7171689926faee002d241ab2656fdfb68c394c41.png

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

image0044_061378560b9a19410f640eefb37b22ebcb8f56f4.png

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

image0054_1214b5c7984a022c199308e47427090c33e5acec.png

6.點擊 Receive a web request

image0068_1b7a4c646f639431cc13ac1b70d0459a50cbc0aa.png

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

image0078_3a426d5a2ade993551237627139ddf3b92e1e92d.png

8.接著點擊Then That 的 Add。

image0082_27c5aac9f32c64f6147cfa17f56b3e471127cc2d.png

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

image0093_0d2782b3ae01319e0ab0401acc4dbf7c62ede84f.png

10.點擊Send Message

image0101_d5bf6903a760ffe7db35aeb019252849d33c574b.png

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

image0112_27fccce4a48cc88364cda432132fc386f0888fab.png

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

image0122_066f41add1991725ab5cb68d2d477a2f8e2a8a95.png

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

image0135_29914d38d55437d22ff7f8589f90d1687d6560e6.png

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

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

image014_0dac45c320babd00600ef264f717df1c6635b001.png

14.點擊 Continue

image0154_d309b6b1c697de50306e9a5cd38f2cd1a6b6ec27.png

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

image016_166c311089b3077fbaf6903b87c918891d87faab.png

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

image0174_db71b666ca7996b094fca18f38336f521fb36412.png

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

image018_07cd2e17893e473d45444cd38adbf0d24c6d61ac.png

18.點擊右上角的 Documentation

image0193_b3848618beb35a2686e7e654ce9bc9d5898367c4.png

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

image020_56440736c08e2e40cc51f0af8be6f69305e5909d.png

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

image0213_a93283123b9b7c7aa0d55bfc3a69f1311fb9215b.png

編輯Line的訊息格式

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

image022_5b94621c422d06c18269bb0b6ceb5fbe50000dd2.png

21.點擊 jetsonnano_line 的app方框

image0232_98ec09f06170335de927b380b80daeccd27cae5e.png

22.點擊右上角的setting

image024_f11bf792b5c1f7f6207845d51d0d794fb3b3a0d4.png

23.選擇Send Message右上角的edit

image0252_309ec7721287d27b76693c7589b0837dc8176ebe.png

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

image026_2478a960a60eec2289e8da5e92c1b510efa0f9c9.png

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

image0272_47cd92c86e56c7350aa42a01ea739212521664c4.png

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

使用Python程式連動Line

解析Webhook

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

Make a POST or GET web request to:

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

image027_12_aa21e1264dd13684724b023a15e0cb24112f2820.png

With an optional JSON body of:

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

image027_2_48d263f391b987c5cfa19b4158745a8ac130e30f.png

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

實作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訓練

image0292_16994a677ccd1a8589b117fa97327a51b6cce763.png

匯出Tensorflow模型

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

image0301_3ff75fcab91280aa351e53aec8b2b6c728686738.png

在JetsonNano中運行即時影像辨識

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

image0313_4c11748fb6678d39ea33f5ddcf6d2bec57b32398.png

快速參考指令如下:

$ 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()會獲取兩個參數,第一個通常被用來代表是否擷取成功 ( 布林值 ),第二個則是影像陣列;接著將圖片丟入模型前還需要進行前處理,縮放大小至224x224,正規化,以及修改成輸入資料的格式:

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 ...')

執行結果如下:

image032_a78add35fc111c49d350e611f9db6cc29a7bec9d.png

可以看到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左右,一旦傳送資料就會整個

結論

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

CAVEDU Education is devoted into robotics education and maker movement since 2008, and is intensively active in teaching fundamental knowledge and skills. We had published many books for readers in all ages, topics including Deep Learning, edge computing, App Inventor, IoT and robotics. Please check CAVEDU's website for more information: http://www.cavedu.com, http://www.appinventor.tw
DesignSpark Electrical Logolinkedin