嘿!您似乎在 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首页,选择登入账号或申请新账号

image0016_0e025d18d3bffd9eb499534ff030f96ee72b1bf7.png

2.请选择一种账号进行登入

image0022_aa83be2e4876be2d8be71047059de2c90a1413e7.png

3.右上角选择选择Create新服务

image0034_7171689926faee002d241ab2656fdfb68c394c41.png

4.点选Add,进入服务设定。

image0045_061378560b9a19410f640eefb37b22ebcb8f56f4.png

5.搜寻 Webhooks 服务并点选该图示

image0055_1214b5c7984a022c199308e47427090c33e5acec.png

6.点击 Receive a web request

image0069_1b7a4c646f639431cc13ac1b70d0459a50cbc0aa.png

7.创建事件名称并点击Create trigger 启动输入触发。

image0079_3a426d5a2ade993551237627139ddf3b92e1e92d.png

8.接着点击Then That 的 Add。

image0083_27c5aac9f32c64f6147cfa17f56b3e471127cc2d.png

9.搜寻Line并且点击LINE的icon

image0094_0d2782b3ae01319e0ab0401acc4dbf7c62ede84f.png

10.点击Send Message

image0102_d5bf6903a760ffe7db35aeb019252849d33c574b.png

11.因为要连接到 LINE,需要进行登入LINE 的动作。

image0113_27fccce4a48cc88364cda432132fc386f0888fab.png

12.IFTTT透过 LINE Notify 的通知进行推播,选择同意并连动

image0123_066f41add1991725ab5cb68d2d477a2f8e2a8a95.png

连动后会在LINE上弹出这段讯息

image0136_29914d38d55437d22ff7f8589f90d1687d6560e6.png

13.设定传送 LINE 讯息的内容

可以选择要推播到特定群组,或是一对一的 LINE Notify,再来是讯息格式,1个 Webhooks 可以传送3个数值,后续会教大家怎么修改。

image0141_0dac45c320babd00600ef264f717df1c6635b001.png

14.点击 Continue

image0155_d309b6b1c697de50306e9a5cd38f2cd1a6b6ec27.png

15.输入app的标题简述,接着可以按Finish

image0161_166c311089b3077fbaf6903b87c918891d87faab.png

16.完成之后的画面如下,接着要点击Webhook的图标

image0175_db71b666ca7996b094fca18f38336f521fb36412.png

17.点击左上角的IFTTT图案回到首页,并且点击Create by me就可以看到刚刚创建的APP

image0181_07cd2e17893e473d45444cd38adbf0d24c6d61ac.png

18.点击右上角的 Documentation

image0194_b3848618beb35a2686e7e654ce9bc9d5898367c4.png

19.进到文件状态后修改内容进行测试,event字段为刚刚Webhook创建的名称 ( jetsonnano_line ),value随意输入。

image0201_56440736c08e2e40cc51f0af8be6f69305e5909d.png

执行结果可以到 Line Notify中查看

image0214_a93283123b9b7c7aa0d55bfc3a69f1311fb9215b.png

编辑Line的讯息格式

20.回到APP的页面,点击LINE的图标

image0221_5b94621c422d06c18269bb0b6ceb5fbe50000dd2.png

21.点击 jetsonnano_line 的app方框

image0233_98ec09f06170335de927b380b80daeccd27cae5e.png

22.点击右上角的setting

image0241_f11bf792b5c1f7f6207845d51d0d794fb3b3a0d4.png

23.选择Send Message右上角的edit

image0253_309ec7721287d27b76693c7589b0837dc8176ebe.png

24.修改讯息信息,变量可以从 Add ingredient选取,有EventName、三个Value以及 OccurredAt ( 最近的时间 ) 可以选择。

image0261_2478a960a60eec2289e8da5e92c1b510efa0f9c9.png

25.回到Webhook的Documentation测试一下结果

image0273_47cd92c86e56c7350aa42a01ea739212521664c4.png

到目前就已经完成基本的IFTTT设定了。

使用Python程序连动Line

解析Webhook

开始之前我们先解析一下刚刚在Webhook的Documentation。

Make a POST or GET web request to:

代表我们可以使用POST或GET的方式去触及Webhook,其中GET通常是下载网页信息而POST则是上传数据,但是在这边事都可以通用的。

image027_13_aa21e1264dd13684724b023a15e0cb24112f2820.png

With an optional JSON body of:

他可以使用自定义的参数但是需要使用JSON格式的内容,强制使用类似Python的字典形式,Key的位置是固定的所以写程序的时候要注意,Key后面的内容则是可以随机定义的,而这部分就是可以更换成我们辨识的结果。

image027_21_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)

我们执行程序测试一下,可以看到LINE有确实收到讯息:

image028_0acb8d6d7787e8700ee63b1d728ae22ebe8ad569.png

结合简易的AI辨识

这部分可以结合各种形式,像是YOLOv4、Jetson Inference等API,在稍微修改一下即可,这边我提供一个更简单且我们常用的方法 Teachable Machine,这边就不介绍太多了,他是一个在线的AI体验工具。

除了在线训练之外还可以提供Tensorflow、Tensorflow Lite以及Coral的模型,这边我顺便教大家怎么去安装 Tensorflow以及Tensorflow Lite。

使用Teachable Machine训练

image0293_16994a677ccd1a8589b117fa97327a51b6cce763.png

导出Tensorflow模型

我们直接使用Keras模型即可,选择Tensorflow并选择Model conversion type为Keras,点选Download my model即可进行转换与下载:

image0302_3ff75fcab91280aa351e53aec8b2b6c728686738.png

在JetsonNano中运行实时影像辨识

首先需要安装Tensorflow,我们可以直接到这里照个原厂推荐的方法操作,由于我的Nano 是JetPack 4.4.1,所以要找到 Python3.6 + JetsonPack 4.4的位置:

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

执行结果如下:

image0321_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