DesignSpark Electrical Logolinkedin
菜单 搜寻
提问问题

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

 

作者

许钰莨/曾俊霖

難度

普通

 

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

 

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

  • 「分辨商品視窗」顯示到商品後,當使用者按下「增加購買商品」鍵,「結帳台」上會出現該商品名稱,反之,若「結帳台」沒有商品,或「分辨商品視窗」顯示非商品(即不是在Teachable Machine所訓練的物件),便會播放"結帳台上沒有商品"的聲音。

 

  • 當使用者反悔不想購買商品時,可按下「刪除購買商品」鍵,「結帳台」上會出現『-----------』的刪除符號,當商品全數刪除後,若又按下「商品結帳」鍵時,會播放"沒有可結帳商品,請先選購商品後再結帳"的聲音。

 

  • 「結帳台」上有商品,使用者按下「商品結帳」鍵時,會播放"結帳完成謝謝光臨"的聲音。

 

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

一、介紹 Tkinter 模組。

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

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

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

五、接上硬體設備之須知

六、開啟商品結帳系統程式。

七、程式說明。

 

一、介紹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,黃框為預測值

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 App Inventor, Lego robot and IoT (Arduino / Raspberry Pi). Please check CAVEDU's website for more information: http://www.cavedu.com, http://www.appinventor.tw

21 May 2020, 7:06