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

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

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

★★★☆☆

 

本文為前篇『使用Google Teachable Machine 來實現Raspberry Pi 4 的影像分類推論』的延伸,所以本文主要是分享如何更換商品之模型檔,讀者可以沿用前篇所訓練好的模型相關檔案作為商品,直接匯入本文所設計好的GUI介面,同時攝像頭會將照到的人臉和商品拍照,作為資料庫,可進一步優化下次在網頁訓練時的模型。

 

本專題將商品結帳系統設計成GUI介面,讓使用者方便操作,商品結帳系統分辨人臉及商品兩種不同的類別,功能使用如下:

  • 「分辨商品視窗」顯示到商品後,當使用者按下「增加購買商品」鍵,「結帳台」上會出現該商品名稱,反之,若「結帳台」沒有商品,或「分辨商品視窗」顯示非商品(即不是在Teachable Machine所訓練的物件),便會播放”結帳台上沒有商品”的聲音。
  • 當使用者反悔不想購買商品時,可按下「刪除購買商品」鍵,「結帳台」上會出現『———–』的刪除符號,當商品全數刪除後,若又按下「商品結帳」鍵時,會播放”沒有可結帳商品,請先選購商品後再結帳”的聲音。
  • 「結帳台」上有商品,使用者按下「商品結帳」鍵時,會播放”結帳完成謝謝光臨”的聲音。

 

本文將分成幾個部分來介紹:

  1. 介紹 Tkinter 模組。
  2. 將商品結帳系統之相關檔案上傳至RPi4。
  3. 匯入訓練商品之模型檔。
  4. 安裝商品結帳系統播放音訊檔和圖像PIL之套件。
  5. 接上硬體設備之須知
  6. 開啟商品結帳系統程式。
  7. 程式說明。

 

一、介紹Tkinter模組

使用Tkinter ,是因Python 裡已經內建的標準模組,且具有以下兩項優點:

  • 可以跨平台,如: Linux/MAC OS/Windows 可以執行Python的作業系統,而Windows則是安裝Python時會一併安裝Tkinter。即可先在Windows 作業系統中設計介面再到其他作業系統執行,如本文使用的RPi4 。
  • 程式碼簡潔易懂,對於剛接觸人機互動介面的初學者很容易學習,且也很快可以設計出人機介面。

 

因為RPi4的Python中已內建Tkinter ,我們可進一步查詢Tkinter版本,和呼叫內建的測試視窗。

 

(1)Tkinter版本查詢,查詢指令步驟如下:

先開啟RPi4 終端機,並輸入

Python 3 

輸入 import tkinter ,再輸入

tkinter.Tcl().eval('info patchlevel')

即可知道本文的Tkinter版本為8.6.9版。

 

(2)呼叫出內建的測試視窗,執行測試函數_test()即可顯示測試視窗

輸入

import tkinter

再輸入

tkinter._test()

若按下「Click me!」,會顯示中括號,按下「QUIT」則退出

二、將商品結帳系統之相關檔案上傳至RPi4

本文準備了Store資料夾,相關的檔案讀者可以從本文提供的連結下載後上傳至RPi4

三、匯入訓練商品之模型檔

如果讀者想重新訓練商品的模型,請參考前篇文章使用Google Teachable Machine  來實現Raspberry Pi 4 的影像分類推論所訓練的模型檔案,將商品的模型檔及標籤檔改名成labels_goods.txt和model_goods.tflite 。

將商品的模型檔及標籤檔透過遠端連線軟體傳送至RPi4中本文已經創建好的Store資料夾中

四、安裝商品結帳系統播放音訊檔和圖像之套件

本文的人機互動介面除可以分辨人臉及商品功能外,也可以播放音訊檔來得知結果,播放音訊檔的套件是使用pygame,故須先安裝此套件:

$pip3 install pygame

本文pygame套件的版本,可利用以下指令查詢:

$pip3 list

可以得知pygame套件的版本為1.9.6版

還需安裝圖像PIL套件

$  sudo apt-get install python3-pil.imagetk

如果讀者想更換音訊檔,本文是使用文字轉語音的人工語音合成網站,輸入文字後可以依照喜好如:男生、女生、語速、音高,進行調整後下載。當然,讀者有找到不錯的人工語音合成網站也可嘗試使用。

網址連結: https://www.toolfk.com/tool-online-text2video?type=base

 

要注意的一點,本文所下載的音訊語速和音高皆是調到最低值,主要的原因是pygame套件會加速原本音訊檔的語速,這裡請讀者需耐心測試。

本文整理了商品結帳系統所需的音訊檔名稱,及音訊內容

音訊檔名稱 音訊內容(不可隨意更改檔名)
thanks.mp3 結帳完成謝謝光臨
no_goods.mp3 沒有可結帳商品
請先選購商品後再結帳
no_goods_class.mp3 結帳台上沒有商品

 

以上為商品結帳系統所有音訊檔案的說明,音訊內容的語句可以自行設計,但是音訊檔檔名請照原本的名稱,因為tk_cv_goods.py檔案需要和以上音訊檔檔名一致,故不可隨意更改,否則執行時會顯示找不到檔案的錯誤。

 

五、接上硬體設備之須知

開啟程式前,請先確認攝像頭是否插入RPi4的USB3.0孔(藍色USB插孔)

並將喇叭插入3.5mm 音源孔來播出聲音

開啟商品結帳系統。

首先,移動到Store資料夾中

$ cd Store/ 

匯入需要執行影像的檔案

$ ln -s /usr/local/python/cv2/python-3.7/cv2.cpython-37m-arm-linux-gnueabihf.so cv2.so

六、執行商品結帳系統程式

$python3 tk_cv_goods.py

執行畫面如下:

七、程式說明

匯入相關函式庫,本文所使用的框架為Tensorflow Lite ,優點在於若部屬在像RPi4的邊緣裝置,可以使模型優化,執行的效率非常快速,而且也可部屬於Android手機。

import tkinter
import cv2
import PIL.Image, PIL.ImageTk 

import pygame

from tflite_runtime.interpreter import Interpreter

開啟視訊鏡頭

self.vid_0 = MyVideoCapture(self.video_source_0)

Tkinter的GUI視窗的像素大小設為500*400

 self.window.geometry('500x400')
 self.window.resizable(False, False)

設置畫布尺寸,為240*180。

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

設置畫布尺寸視窗中的於第0行第1列,在網格中使用”sticky”參數來指定對齊方式,可指定n、s、e、w,分別為上、下、左、右對齊,這剛好可用指北針的方位圖來表示位置。

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

設置按鈕尺寸,分別為”增加購買商品”於第1行第1列、”刪除購買商品”於第2行第1列、”商品結帳”於第3行第1列

 tkinter.Button(window, text="增加購買商品", command=self.add_goods).grid(row=1, column=1)
 tkinter.Button(window, text="刪除購買商品", command=self.delete_goods).grid(row=2, column=1)
 tkinter.Button(window, text="商品結帳", command=self.check_out).grid(row=3, column=1)

攝影機還有另一項功能,就是做完影像推論後,會將商品的圖像擷取到goods_collect資料夾中作為資料庫,往後再重新訓練時能提高商品的精確度。

    def add_goods(self):
        ret_0, frame_0 = self.vid_0.get_frame()
        if (ret_0):
            cv2.imwrite("goods_collect/" + "goods-" + time.strftime("%d-%m-%Y-%H-%M-%S") + ".jpg", cv2.cvtColor(frame_0, cv2.COLOR_RGB2BGR))
            self.is_goods = Goods_class(frame_0,self.item)    
        self.item=self.item+1

當按下”刪除購買商品”時,會將商品刪除,標籤元件顯示於第self.item+1行,第二列。

def delete_goods(self):
        self.item = self.item -1
        if (self.item <= 0):
            self.item=0
        labelExample = tkinter.Label(text="____________________")
        labelExample.grid(row=self.item+1, column=2)

接下來說明如何將影像顯示在GUI視窗上,攝像頭原本的影像大小為320*240

  self.vid = cv2.VideoCapture(video_source)
  self.vid.set(cv2.CAP_PROP_FRAME_WIDTH,320)
  self.vid.set(cv2.CAP_PROP_FRAME_HEIGHT,240)

但前面已設置畫布尺寸為240*180,必須符合畫布尺寸,所以將影像尺寸縮放成240*180

frame_0_small=cv2.resize(frame_0,(240,180))

最後在Goods_class的函式裡,開始進行圖像分類,最後推論出來的結果,可以顯示商品標籤及信心指數(即預測值)

#做圖像分類
class Goods_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 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,item):
        labels = self.load_labels('/home/pi/Store/labels_goods.txt')
        interpreter = Interpreter('/home/pi/Store/model_goods.tflite')
        interpreter.allocate_tensors() 
        _, height, width, _ = interpreter.get_input_details()[0]['shape']
        
        #驗證畫面尺寸為224X224,改變尺寸會驗證錯誤
        image=cv2.resize(image_src,(224,224))

        results = self.classify_image(interpreter, image)
        label_id, prob = results[0]
        
        #顯示商品標籤id及信心指數
        print(label_id, prob)

筆者在最後加上”print(label_id, prob)”,可以使讀者了解圖片是如何被分類出來,其中紅框表示商品標籤id,黃框為預測值

 

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

發佈留言

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