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

在Raspberry Pi 4設計GUI介面,匯入機器學習模型實現人臉辨識篇

 

2005_001_92a96afc175eb88a255aea58cf07169e1208bdeb.jpg

 

作者

許鈺莨/曾俊霖

難度

簡單

材料表

Raspberry Pi 4 (4GB)

SD卡(16G)

Mini HDMI傳輸線

一支網路攝影機

喇叭

螢幕、鍵盤、滑鼠

 

本文章先備知識教學文章

使用Google Teachable Machine來實現Raspberry Pi 4 的影像分類推論

在Raspberry Pi 4設計GUI介面,匯入機器學習模型實現商品結帳應用

本篇文章完整範例程式請由文章最下方附件下載

 

筆者在使用Teachable Machine時,發現網站除了可以分辨物件的圖像外,也能分辨人臉,故智慧商店才將此功能結合進去。本主題會將焦點會放在臉部的圖像分類,在Teachable Machine收集人臉資料後,匯出人臉的模型檔案後,再使用Tkinter 的圖形化介面設計一套分辨人臉系統。

 

本文將分成下列幾個步驟來完成此專題

1.在Teachable Machine 收集人臉資料,並可以在網站上即時推論人臉圖像

2.將模型匯入到RPI4,並可執行人臉圖像分類程式

3.設計人臉GUI圖形化介面之程式說明,並執行智慧商店人臉程式

 

 

1.在Teachable Machine 收集人臉資料,並可以在網站上即時推論人臉圖像

先在Teachable Machine網頁訓練人臉模型,相關步驟可以參考"使用Google Teachable Machine 來實現Raspberry Pi 4 的影像分類推論",考慮到一般的商店的結帳系統,只要有人即可結帳,並不會去紀錄人名,所以在Teachable Machine 的模型標籤只有兩種情形,有人和無人。

2005_011_8a283b761f3d5e4ebadcf6360527ff0ae3a07d17.jpg

 

因為只要判別是否有人接近,所以筆者是到網路上收集男性和女性,東方人或西方人的臉孔圖片約莫共120張,直接上傳至"people"的標籤中,讀者也可以程式下載區Store-->face_dataset資料夾中找到圖片,並且全選上傳。另外當初也有想過拍照,但是拍的人數樣本太少,不如直接上網收集人臉圖片,也考慮到不同的膚色、年齡、地方相關的因素。

而收集沒有人臉的圖片則是可以直接設定用Webcam拍照即可。

2005_021_bdcccc2f1313992b518cacca6c5514492c9c6379.jpg

2005_031_9e1d2af4e714e297bb28e415c8bd95a2ad8d32fd.jpg

 

設定電腦使用的攝影機,本次使用Webcam的是Logi C170

2005_051_b90c49e8ed48010f0bf4588ed8888b7391ac6e5f.jpg

 

另外,建議可以在電腦外接一個Webcam,收集圖片較為方便。

2005_061_6d2f36e6a7cdf9677f5a619a5bcfb3abd634df98.jpg

 

按下"Train Model" 即可開始訓練模型。

2005_071_9798947d6ab5a613545d896b0caf9e82b4267281.jpg

 

因為在訓練集中放入了相同兩張有帶口罩的人臉,所以即使戴著口罩,推論出來的結果也可以分類得出來是"people"。

2005_081_71ba8ee2eb29efcc67b66af0788a89de61955f57.jpg

 

無人臉接近則是"no_people"

2005_091_7393f1e1c8be7dc4931f64547008f4463a0274fd.jpg

 

但如果人臉太遠離鏡頭,則也會被分類到"no_people"

2005_101_a7f4cddf29bd0491ca7e914e2692f3e940bc196c.jpg

 

輸出模型

2005_111_219ec871b5bb68dbf36f201b38182909ff6dd281.jpg

 

2.將模型匯入到RPI4,並可執行人臉圖像分類程式

點選Tensorflow Lite -->Quantize-->Download my model

2005_121_9b5e87400c44a23d16e8688d3d052ad877c820c2.jpg

 

下載得到副檔名[專案名稱]+.txt、.tflite的兩個檔案

2005_131_762ba914a3bed76ca1ab5d5c36fdcfd595b2e279.jpg

 

請將下載的資料夾解壓縮後,將兩個檔名分別改成"label_face.txt"和"model_face.tflite",並

傳送資料到Rpi4 的"Store"資料夾中。

2005_141_9ec69f6194a01f98f20df2ce9dc3dcaa4a0b4bdf.jpg

 

在"Store"資料夾中有"TM2_tflite_new.py",可以執行程式,並將有無人臉的狀態顯示及預測值顯示在視窗上面。

先移動資料夾位置到Store

$ cd Store/

2005_151_c04d3291cc9254f743ae651048c344a41f6e3e08.jpg

 

執行程式

$ python3 TM2_tflite_new.py --model model_face.tflite --label labels_face.txt 

2005_161_8939636c89fd2f568d3d4c1f3d63a5e31cbd3e44.jpg

 

2005_171_64e27666ba8ec1591729c026cb8260e86cd182aa.jpg

 

在執行程式的視窗中,筆者有將結果顯示在視窗上,"0"為標籤、"people"為預測出人臉的名稱、"0.9921875"為預測值,說明能分辨出人臉類別的預測值為99%。

 

3.設計人臉GUI圖形化介面之程式說明,並執行智慧商店人臉程式

 

3-1人臉GUI圖形化介面程式說明

設定視窗大小 300*300像素

 self.window.geometry('300x300')
 self.window.resizable(False, False)

 

2005_181_7da45df30a501735d1c599d52ac791e723c33ccf.jpg

 

開啟Webcam攝影機

self.vid_0 = MyVideoCapture(self.video_source_0)

 

建立人臉尺寸為240*180像素的圖像畫布

self.canvas_face = tkinter.Canvas(window, width = 240, height = 180)

 

設置畫布尺寸視窗中的於第0行第0列,在grid網格中使用"sticky"參數,是west向左對齊

self.canvas_face.grid(row=0, column=0, sticky="w")

 

建立具有拍照功能按鈕,並名稱為"Face",排列在第1行第0列

tkinter.Button(window, text="Face", command=self.face).grid(row=1, column=0)

 

Webcam得到影像後,必須使用PIL套件,才能在Tkinter 畫布中顯示動態影像。

  def face(self):
        ret_0, frame_0 = self.vid_0.get_frame()

        if (ret_0):
            Face_class(frame_0)
            

    def update(self):
        ret_0, frame_0 = self.vid_0.get_frame()
        
        
        #調整Webcam影像之畫面大小,需和Tkinter畫布同尺寸
        frame_0_small=cv2.resize(frame_0,(240,180))
        
      
        if ret_0:
            self.photo_2 = PIL.ImageTk.PhotoImage(image = PIL.Image.fromarray(frame_0_small))
            self.canvas_face.create_image(241,0,image = self.photo_2, anchor="ne")
   

        self.window.after(self.delay, self.update)

 

Webcam影像畫面相關尺寸大小設定

class MyVideoCapture:
    def __init__(self, video_source):
    
        #將原本Webcam影像畫面大小設定為320X240
        self.vid = cv2.VideoCapture(video_source)
        self.vid.set(cv2.CAP_PROP_FRAME_WIDTH,320)
        self.vid.set(cv2.CAP_PROP_FRAME_HEIGHT,240)
        

        if not self.vid.isOpened():
            raise ValueError("Unable to open video source", video_source)

        # 設定視訊來源的尺寸
        self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)
        self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)

    def get_frame(self):
        if self.vid.isOpened():
            ret, frame = self.vid.read()
            if ret:
                # 將影像畫面轉換成RGB格式
                return (ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
            else:
                return (ret, None)
        else:
            return (ret, None)

    # 釋放影像資源
    def __del__(self):
        if self.vid.isOpened():
            self.vid.release()

 

做人臉圖像推論,若偵測到人臉時則顯示"歡迎光臨智慧商店",訊息會排序在第2行第0列中顯示出來,並播放歡迎詞音訊,也同時啟動拍照功能。

class Face_class:
    def load_labels(self,path):
        with open(path, 'r') as f:
            return {i: line.strip() for i, line in enumerate(f.readlines())}

    def set_input_tensor(self, interpreter, image):
        tensor_index = interpreter.get_input_details()[0]['index']
        input_tensor = interpreter.tensor(tensor_index)()[0]
        input_tensor[:, :] = image


    def classify_image(self, interpreter, image, top_k=1):
        self.set_input_tensor(interpreter, image)
        interpreter.invoke()
        output_details = interpreter.get_output_details()[0]
        output = np.squeeze(interpreter.get_tensor(output_details['index']))

        # If the model is quantized (uint8 data), then dequantize the results
        if output_details['dtype'] == np.uint8:
            scale, zero_point = output_details['quantization']
            output = scale * (output - zero_point)

        ordered = np.argpartition(-output, top_k)
        return [(i, output[i]) for i in ordered[:top_k]]

    def __init__(self,image_src):
        labels = self.load_labels('/home/pi/Store/labels_face.txt')

        interpreter = Interpreter('/home/pi/Store/model_face.tflite')
        interpreter.allocate_tensors()
        _, height, width, _ = interpreter.get_input_details()[0]['shape']

        image=cv2.resize(image_src,(224,224))

        results = self.classify_image(interpreter, image)
        label_id, prob = results[0]
        
        
        if (labels[label_id] == "1 no_people"):
            labelExample = tkinter.Label(text="____________________")
            labelExample.grid(row=2, column=0)
       
            
        else :
            labelExample = tkinter.Label(text="____________________")
            labelExample.grid(row=2, column=0)
            labelExample = tkinter.Label(text="歡迎光臨智慧商店")
            labelExample.grid(row=2, column=0)

            #開啟拍照功能並存在face_collect資料夾中
            cv2.imwrite("face_collect/" + "face-" + time.strftime("%d-%m-%Y-%H-%M-%S") + ".jpg", cv2.cvtColor(image_src, cv2.COLOR_RGB2BGR))

            #播放歡迎光臨音訊檔
            file=r'/home/pi/Store/welcome.mp3'
            while (pygame.mixer.music.get_busy()!=1):
                track = pygame.mixer.music.load(file) 
                pygame.mixer.music.play()


#視窗標題為"智慧商店"
App(tkinter.Tk(), "智慧商店")

 

3-2 執行程式畫面及元件位置

 

執行Python程式

$python3 tk_cv_face.py

2005_191_55ae235f729e8c52d225edbf15f82cd9904bb725.jpg

2005_201_fb8740c1bd02398044e505e77bf0412beac4e5e7.jpg

 

Tkinter排列的元件,分別為畫布(Canvas)在第0行第0列、"Face"按鈕(Button)在第1行第0列、文字標籤(Label)在第2行第0列。偵測到人臉時,除了可以播放音訊檔案外,也同時會開啟拍照功能,其目的在於收集使用者臉孔,可以達成兩種目的,一、可以大致推論出性別或年齡,哪種族群較多人使用。二、可重新訓練模型,雖然google taechable machine在一般的情況下已經可以偵測人臉,但資料集中卻沒有使用者的照片,會使得預測有稍微的偏差,若要符合真實情形,則需要將收集到的人臉照片到google taechable machine再做一次訓練。

 

 

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