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

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

作者/攝影 許钰莨/曾俊霖
難度

★☆☆☆☆

使用材料 RaspberryPi4 套件-連結
製作時間 2~3小時

本文章先備知識教學文章

筆者在使用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 的模型標籤只有兩種情形,有人和無人。

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

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

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

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

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

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

無人臉接近則是”no_people”

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

輸出模型

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

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

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

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

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

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

先移動資料夾位置到Store

[pastacode lang=”python” manual=”%24%20cd%20Store%2F” message=”” highlight=”” provider=”manual”/]

執行程式

[pastacode lang=”python” manual=”%24%20python3%20TM2_tflite_new.py%20–model%20model_face.tflite%20–label%20labels_face.txt%20″ message=”” highlight=”” provider=”manual”/]

 

 

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

 

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

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

設定視窗大小 300*300像素

[pastacode lang=”python” manual=”self.window.geometry(‘300×300’)%0A%20self.window.resizable(False%2C%20False)” message=”” highlight=”” provider=”manual”/]

開啟Webcam攝影機

[pastacode lang=”python” manual=”self.vid_0%20%3D%20MyVideoCapture(self.video_source_0)” message=”” highlight=”” provider=”manual”/]

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

[pastacode lang=”python” manual=”self.canvas_face%20%3D%20tkinter.Canvas(window%2C%20width%20%3D%20240%2C%20height%20%3D%20180)” message=”” highlight=”” provider=”manual”/]

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

[pastacode lang=”python” manual=”self.canvas_face.grid(row%3D0%2C%20column%3D0%2C%20sticky%3D%22w%22)” message=”” highlight=”” provider=”manual”/]

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

[pastacode lang=”python” manual=”tkinter.Button(window%2C%20text%3D%22Face%22%2C%20command%3Dself.face).grid(row%3D1%2C%20column%3D0)” message=”” highlight=”” provider=”manual”/]

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

[pastacode lang=”python” manual=”%20%20def%20face(self)%3A%0A%20%20%20%20%20%20%20%20ret_0%2C%20frame_0%20%3D%20self.vid_0.get_frame()%0A%0A%20%20%20%20%20%20%20%20if%20(ret_0)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20Face_class(frame_0)%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%0A%20%20%20%20def%20update(self)%3A%0A%20%20%20%20%20%20%20%20ret_0%2C%20frame_0%20%3D%20self.vid_0.get_frame()%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%23%E8%AA%BF%E6%95%B4Webcam%E5%BD%B1%E5%83%8F%E4%B9%8B%E7%95%AB%E9%9D%A2%E5%A4%A7%E5%B0%8F%EF%BC%8C%E9%9C%80%E5%92%8CTkinter%E7%95%AB%E5%B8%83%E5%90%8C%E5%B0%BA%E5%AF%B8%0A%20%20%20%20%20%20%20%20frame_0_small%3Dcv2.resize(frame_0%2C(240%2C180))%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20if%20ret_0%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.photo_2%20%3D%20PIL.ImageTk.PhotoImage(image%20%3D%20PIL.Image.fromarray(frame_0_small))%0A%20%20%20%20%20%20%20%20%20%20%20%20self.canvas_face.create_image(241%2C0%2Cimage%20%3D%20self.photo_2%2C%20anchor%3D%22ne%22)%0A%20%20%20%0A%0A%20%20%20%20%20%20%20%20self.window.after(self.delay%2C%20self.update)” message=”” highlight=”” provider=”manual”/]

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

[pastacode lang=”python” manual=”class%20MyVideoCapture%3A%0A%20%20%20%20def%20__init__(self%2C%20video_source)%3A%0A%20%20%20%20%0A%20%20%20%20%20%20%20%20%23%E5%B0%87%E5%8E%9F%E6%9C%ACWebcam%E5%BD%B1%E5%83%8F%E7%95%AB%E9%9D%A2%E5%A4%A7%E5%B0%8F%E8%A8%AD%E5%AE%9A%E7%82%BA320X240%0A%20%20%20%20%20%20%20%20self.vid%20%3D%20cv2.VideoCapture(video_source)%0A%20%20%20%20%20%20%20%20self.vid.set(cv2.CAP_PROP_FRAME_WIDTH%2C320)%0A%20%20%20%20%20%20%20%20self.vid.set(cv2.CAP_PROP_FRAME_HEIGHT%2C240)%0A%20%20%20%20%20%20%20%20%0A%0A%20%20%20%20%20%20%20%20if%20not%20self.vid.isOpened()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20ValueError(%22Unable%20to%20open%20video%20source%22%2C%20video_source)%0A%0A%20%20%20%20%20%20%20%20%23%20%E8%A8%AD%E5%AE%9A%E8%A6%96%E8%A8%8A%E4%BE%86%E6%BA%90%E7%9A%84%E5%B0%BA%E5%AF%B8%0A%20%20%20%20%20%20%20%20self.width%20%3D%20self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)%0A%20%20%20%20%20%20%20%20self.height%20%3D%20self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)%0A%0A%20%20%20%20def%20get_frame(self)%3A%0A%20%20%20%20%20%20%20%20if%20self.vid.isOpened()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20ret%2C%20frame%20%3D%20self.vid.read()%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20ret%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E5%B0%87%E5%BD%B1%E5%83%8F%E7%95%AB%E9%9D%A2%E8%BD%89%E6%8F%9B%E6%88%90RGB%E6%A0%BC%E5%BC%8F%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20(ret%2C%20cv2.cvtColor(frame%2C%20cv2.COLOR_BGR2RGB))%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20(ret%2C%20None)%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20(ret%2C%20None)%0A%0A%20%20%20%20%23%20%E9%87%8B%E6%94%BE%E5%BD%B1%E5%83%8F%E8%B3%87%E6%BA%90%0A%20%20%20%20def%20__del__(self)%3A%0A%20%20%20%20%20%20%20%20if%20self.vid.isOpened()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.vid.release()” message=”” highlight=”” provider=”manual”/]

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

[pastacode lang=”python” manual=”class%20Face_class%3A%0A%20%20%20%20def%20load_labels(self%2Cpath)%3A%0A%20%20%20%20%20%20%20%20with%20open(path%2C%20’r’)%20as%20f%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%7Bi%3A%20line.strip()%20for%20i%2C%20line%20in%20enumerate(f.readlines())%7D%0A%0A%20%20%20%20def%20set_input_tensor(self%2C%20interpreter%2C%20image)%3A%0A%20%20%20%20%20%20%20%20tensor_index%20%3D%20interpreter.get_input_details()%5B0%5D%5B’index’%5D%0A%20%20%20%20%20%20%20%20input_tensor%20%3D%20interpreter.tensor(tensor_index)()%5B0%5D%0A%20%20%20%20%20%20%20%20input_tensor%5B%3A%2C%20%3A%5D%20%3D%20image%0A%0A%0A%20%20%20%20def%20classify_image(self%2C%20interpreter%2C%20image%2C%20top_k%3D1)%3A%0A%20%20%20%20%20%20%20%20self.set_input_tensor(interpreter%2C%20image)%0A%20%20%20%20%20%20%20%20interpreter.invoke()%0A%20%20%20%20%20%20%20%20output_details%20%3D%20interpreter.get_output_details()%5B0%5D%0A%20%20%20%20%20%20%20%20output%20%3D%20np.squeeze(interpreter.get_tensor(output_details%5B’index’%5D))%0A%0A%20%20%20%20%20%20%20%20%23%20If%20the%20model%20is%20quantized%20(uint8%20data)%2C%20then%20dequantize%20the%20results%0A%20%20%20%20%20%20%20%20if%20output_details%5B’dtype’%5D%20%3D%3D%20np.uint8%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20scale%2C%20zero_point%20%3D%20output_details%5B’quantization’%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20output%20%3D%20scale%20*%20(output%20-%20zero_point)%0A%0A%20%20%20%20%20%20%20%20ordered%20%3D%20np.argpartition(-output%2C%20top_k)%0A%20%20%20%20%20%20%20%20return%20%5B(i%2C%20output%5Bi%5D)%20for%20i%20in%20ordered%5B%3Atop_k%5D%5D%0A%0A%20%20%20%20def%20__init__(self%2Cimage_src)%3A%0A%20%20%20%20%20%20%20%20labels%20%3D%20self.load_labels(‘%2Fhome%2Fpi%2FStore%2Flabels_face.txt’)%0A%0A%20%20%20%20%20%20%20%20interpreter%20%3D%20Interpreter(‘%2Fhome%2Fpi%2FStore%2Fmodel_face.tflite’)%0A%20%20%20%20%20%20%20%20interpreter.allocate_tensors()%0A%20%20%20%20%20%20%20%20_%2C%20height%2C%20width%2C%20_%20%3D%20interpreter.get_input_details()%5B0%5D%5B’shape’%5D%0A%0A%20%20%20%20%20%20%20%20image%3Dcv2.resize(image_src%2C(224%2C224))%0A%0A%20%20%20%20%20%20%20%20results%20%3D%20self.classify_image(interpreter%2C%20image)%0A%20%20%20%20%20%20%20%20label_id%2C%20prob%20%3D%20results%5B0%5D%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20if%20(labels%5Blabel_id%5D%20%3D%3D%20%221%20no_people%22)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20labelExample%20%3D%20tkinter.Label(text%3D%22____________________%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20labelExample.grid(row%3D2%2C%20column%3D0)%0A%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20else%20%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20labelExample%20%3D%20tkinter.Label(text%3D%22____________________%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20labelExample.grid(row%3D2%2C%20column%3D0)%0A%20%20%20%20%20%20%20%20%20%20%20%20labelExample%20%3D%20tkinter.Label(text%3D%22%E6%AD%A1%E8%BF%8E%E5%85%89%E8%87%A8%E6%99%BA%E6%85%A7%E5%95%86%E5%BA%97%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20labelExample.grid(row%3D2%2C%20column%3D0)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%E9%96%8B%E5%95%9F%E6%8B%8D%E7%85%A7%E5%8A%9F%E8%83%BD%E4%B8%A6%E5%AD%98%E5%9C%A8face_collect%E8%B3%87%E6%96%99%E5%A4%BE%E4%B8%AD%0A%20%20%20%20%20%20%20%20%20%20%20%20cv2.imwrite(%22face_collect%2F%22%20%2B%20%22face-%22%20%2B%20time.strftime(%22%25d-%25m-%25Y-%25H-%25M-%25S%22)%20%2B%20%22.jpg%22%2C%20cv2.cvtColor(image_src%2C%20cv2.COLOR_RGB2BGR))%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%E6%92%AD%E6%94%BE%E6%AD%A1%E8%BF%8E%E5%85%89%E8%87%A8%E9%9F%B3%E8%A8%8A%E6%AA%94%0A%20%20%20%20%20%20%20%20%20%20%20%20file%3Dr’%2Fhome%2Fpi%2FStore%2Fwelcome.mp3’%0A%20%20%20%20%20%20%20%20%20%20%20%20while%20(pygame.mixer.music.get_busy()!%3D1)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20track%20%3D%20pygame.mixer.music.load(file)%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pygame.mixer.music.play()%0A%0A%0A%23%E8%A6%96%E7%AA%97%E6%A8%99%E9%A1%8C%E7%82%BA%22%E6%99%BA%E6%85%A7%E5%95%86%E5%BA%97%22%0AApp(tkinter.Tk()%2C%20%22%E6%99%BA%E6%85%A7%E5%95%86%E5%BA%97%22)” message=”” highlight=”” provider=”manual”/]

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

執行Python程式

[pastacode lang=”python” manual=”%24python3%20tk_cv_face.py” message=”” highlight=”” provider=”manual”/]

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

 

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

發佈留言

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