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

在Raspberry Pi 4设计GUI图形介面,汇入机器学习模型实现商品结账应用

作者

许钰莨/曾俊霖

難度

普通

 

本文为前篇『使用Google Teachable Machine 来实现Raspberry Pi 4 的影像分类推论』的延伸,所以本文主要是分享如何更换商品之模型文件,读者可以沿用前篇所训练好的模型相关档案作为商品,直接汇入本文所设计好的GUI接口,同时摄像头会将照到的人脸和商品拍照,作为数据库,可进一步优化下次在网页训练时的模型。

本专题将商品结账系统设计成GUI接口,让用户方便操作,商品结账系统分辨人脸及商品两种不同的类别,功能使用如下:

  • 「分辨商品窗口」显示到商品后,当使用者按下「增加购买商品」键,「结账台」上会出现该商品名称,反之,若「结账台」没有商品,或「分辨商品窗口」显示非商品(即不是在Teachable Machine所训练的对象),便会播放"结账台上没有商品"的声音。

 

  • 当使用者反悔不想购买商品时,可按下「删除购买商品」键,「结账台」上会出现『-----------』的删除符号,当商品全数删除后,若又按下「商品结账」键时,会播放"没有可结账商品,请先选购商品后再结账"的声音。

 

  • 「结账台」上有商品,使用者按下「商品结账」键时,会播放"结账完成谢谢光临"的声音。

0124_18c8a030123a9956cca492289b8ce8a9c5638eab.jpg

 

本文将分成几个部分来介绍:

一、介绍 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

0221_baa69733b8ce3684ead91b4f16516f3b8870f090.jpg

 

输入

import tkinter 

 

再输入

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

0319_8973ce64a6e322ae7c3bce8451fdf1963159002c.jpg

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

 

(2)呼叫出内建的测试窗口,执行测试函数_test()即可显示测试窗口

输入

import tkinter

 

再输入

tkinter._test()

0420_e01c23928432660d83f781ca802a91f9e60790e0.jpg

 

若按下「Click me!」,会显示中括号,按下「QUIT」则退出

0514_0665ed7d4bc48726c36db19f4dba8ea1a77255c8.jpg

 

二、将商品结账系统之相关档案上传至RPi4

        本文准备了Store文件夹,相关的档案读者可以从本文提供的连结下载后上传至RPi4

0613_95c48e40fa617748c3094fc4963407364eb20989.jpg

 

三、汇入训练商品之模型文件。

         如果读者想重新训练商品的模型,请参考前篇文章使用Google Teachable Machine 来实现Raspberry Pi 4 的影像分类推论所训练的模型档案,将商品的模型文件及卷标文件改名成labels_goods.txt和model_goods.tflite 。

0711_e63c9eeda8f062206f8b78db65bfb7e68a9e7bfe.jpg

 

将商品的模型文件及卷标文件透过远程联机软件传送至RPi4中本文已经创建好的Store文件夹中

0811_7c7232c5ea8d84b936a625d8bfceb236f050f79a.jpg

 

四、安装商品结账系统播放音频文件和图像PIL之套件

本文的人机互动接口除可以分辨人脸及商品功能外,也可以播放音讯文件来得知结果,播放音讯文件的套件是使用pygame,故须先安装此套件:

$pip3 install pygame

0913_994c81b10d6e71e35e0b09d3c81e555a427ef372.jpg

 

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

$pip3 list

1036_1aa84b14d541142309e796ddaa836392c3012bd0.jpg

 

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

1146_b50b225645401aeb9a0974f46bd4d6aa73e67eea.jpg

 

还需安装图像PIL套件

$  sudo apt-get install python3-pil.imagetk

11-21_fbc635bc76cd8e03fcaec61b7c0a6080d30ec0a6.jpg

 

如果读者想更换音讯文件,本文是使用文字转语音的人工语音合成网站,输入文字后可以依照喜好如:男生、女生、语速、音高,进行调整后下载。当然,读者有找到不错的人工语音合成网站也可尝试使用。

网址连结: https://www.toolfk.com/tool-online-text2video?type=base

 

要注意的一点,本文所下载的音讯语速和音高皆是调到最低值,主要的原因是pygame套件会加速原本音讯文件的语速,这里请读者需耐心测试。

1238_05bab02b4ee0f055deef9cf983c696604d987f17.jpg

 

本文整理了商品结账系统所需的音频文件名称,及音频内容

 

音讯文件名称

(不可随意更改档名)

音讯内容

thanks.mp3

结账完成谢谢光临

no_goods.mp3

没有可结账商品

请    请先选购商品后再结账

no_goods_class.mp3

结账台上没有商品

 

 以上为商品结账系统所有音频档案的说明,音频内容的语句可以自行设计,但是音讯文件文件名请照原本的名称,因为tk_cv_goods.py档案需要和以上音讯文件文件名一致,故不可随意更改,否则执行时会显示找不到档案的错误。

 

五、接上硬设备之须知

开启程序前,请先确认摄像头是否插入RPi4的USB3.0孔(蓝色USB插孔)

1336_0dc8d777232fc1b761b735e6a85138a941547612.jpg

 

并将喇叭插入3.5mm 音源孔来播出声音

1432_423e323e49c368fbebf1b6be8bef656ba33b6324.jpg

 

开启商品结账系统。

首先,移动到Store文件夹中

$ cd Store/ 

1531_0c22111de6949301bfe1d833d5a088d5b3781271.jpg

 

汇入需要执行影像的档案

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

1632_8715dff722f4bd17cfbf2f855e154d73c38eb2ef.jpg

 

六、执行商品结账系统程序

$python3 tk_cv_goods.py

1731_e7e3c44ab87185cea8098868c3a1b04a84e2e31f.jpg

 

执行画面如下:

1829_022a13d0eb82d255f809e3dc92f3ec3c1711e62a.jpg

执行程序影片

 

七、程序说明

汇入相关函式库,本文所使用的框架为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)

1928_85948d0ccb38388f35b0b0e5564c2f29d3e199ce.jpg

 

设置画布尺寸,为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")

20_2_b55c74ae4c35aa9eca2168b7a06738af2a8c6928.jpg

 

设置按钮尺寸,分别为"增加购买商品"于第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)

2126_7344c4c388be0a753e5bb4bbfff27cc47602b0e9.jpg

 

摄影机还有另一项功能,就是做完影像推论后,会将商品的图像撷取到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)

2225_e30da0dfdfe40d12c37d952e8d6bede7adddd765.jpg

 

接下来说明如何将影像显示在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,黄框为预测值

2322_a877099192c1cf9e8065349195324016054986e0.jpg

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
DesignSpark Electrical Logolinkedin