一、前期准备
1、这次是结合前面的做个阶段性总结,在硬件端加个简单的tkinter界面,管理员登录并可以控制人脸识别门禁的开关,以及实时显示视频流,显示人脸识别结果,记忆日志记录。具体如何在树莓派上做的一些准备,可参考前几期:
【物联网树莓派毕设02】树莓派4B安装Pyhton3及opencv
【物联网树莓派毕设03】树莓派4B调用百度API快速实现人脸识别
【物联网树莓派毕设04】Arduino实现电梯及人脸识别门禁
2、安装必要的库,如已安装可跳过
python -m pip install --upgrade pip //先更新pip
pip install opencv-python //摄像头
pip install pillow //图像控件
pip install baidu-aip //百度api
pip install pyserial //串口
3、效果展示
二、具体实现
1、实现tkinter界面跳转,有2个方法
(1)设置1个根窗口,2个frame,利用destory方法,实现登录frame跳转至首页frame,缺点是跳转之后不可回到登录frame,即只能实现一次跳转,如需继续跳转,需要新建frame,如法炮制
top = tk.Tk()#新建tk根窗口
top.title("人脸识别门禁系统")
top.geometry("300x300+350+180")#设置窗口位置
top.resizable(width=False, height=False) #设置窗口不可改变
login = login(top)#进入login frame
try:
top.wait_window(window=login)#直到login窗口销毁,login界面跳转index界面
index(top)#进入index frame
except Exception as e:
print("erro:",e)#异常捕获处理
pass
top.mainloop() #窗口消息循环
(2)设置2个根窗口,利用lambda表达式即可,可以实现两个窗口来回切换
def login():
root1=tk.Tk()
root1.geometry("300x300+450+200")#设置窗口位置
bu1=tk.Button(root1,text="登录",command=lambda:[root1.destroy(),index()])
bu1.pack()
root1.mainloop()
def index():
root2=tk.Tk()
root2.geometry("300x300+450+200")#设置窗口位置
bu1=tk.Button(root2,text="登陆成功,回到登录",command=lambda:[root2.destroy(),login()])
bu1.pack()
root2.mainloop()
login()
2、放置各组件在容器内的位置的3个方法
(1)pack():默认先使用的放到上面,然后依次向下排,它会给我们的组件一个自认为合适的位置 和大小,这是默认方式。【进入学习pack】
(2)grid():类比多维数组,会根据组件自动计算。【进入学习grid】
(3)place():设置控件在窗体或窗口内的绝对地址或相对地址。【进入学习place】
3、tk的Frame、Toplevel、Button、Label、Entry、Canvas创建
(1)Frame:作为一种容器,可以把其他组件放在frame上。
import tkinter as tk #tk界面
master = tk.Tk()#新建tk根窗口
index_frame =tk.Frame(master)
index_frame.grid() #设置参数,可以调整大小,位置等
master.mainloop() #窗口消息循环
(2)Toplevel:作为顶级窗口,在原有窗口上弹出的窗口,一般可用于信息确认,密码验证等等
import tkinter as tk #tk界面
check_frame = tk.Toplevel() #新建顶级窗口
#布局
check_frame.title("验证管理员权限")
check_frame.geometry("300x200+580+300")
(3)Button:按钮控件,不过tk的按钮太丑了,可以引入ttk,好看一点。button的第一个参数是放在的容器,command是绑定按钮处理函数
from tkinter import ttk #tk子部件,更加美观
import tkinter as tk #tk界面
master = tk.Tk()#新建tk根窗口
login_frame = tk.Frame(master)#新建frame
login_frame.grid() #展示frame
def cert():
#弹出顶级窗口
check_frame = tk.Toplevel() #新建顶级窗口
check_frame.title("登录验证")
check_frame.geometry("300x200+580+300")
print("登录处理")
button1 = ttk.Button(login_frame, text = '立即登录',width = 28,command = cert)
button1.pack()
master.mainloop()
(4) Label及Entry:标签和输入框,标签可以放文字,也可以放图片,输入框获取输入。
给出登录界面代码
import tkinter as tk
import tkinter.messagebox
import pickle
from PIL import Image, ImageTk
window = tk.Tk()
window.title('login')
window.geometry('300x200')
# 登陆界面
tk.Label(window, text='账户:').place(x=50,y=60) #标签
tk.Label(window, text='密码:').place(x=50, y=100)
var_usr_name = tk.StringVar() #定义字符串输入变量
enter_usr_name = tk.Entry(window, textvariable=var_usr_name).place(x=100, y=60) #输入框
var_usr_pwd = tk.StringVar()
enter_usr_pwd = tk.Entry(window, textvariable=var_usr_pwd, show='*').place(x=100, y=100)
#登陆
def usr_login():
#输入框内容
usr_name = var_usr_name.get()
usr_pwd = var_usr_pwd.get()
print("用户名:",usr_name)
print("密码:",usr_pwd)
#退出
def usr_quit():
window.destroy()
#登录 退出按钮
bt_login = tk.Button(window,text='登录',command=usr_login).place(x=100,y=130)
bt_logquit = tk.Button(window,text='退出',command=usr_quit).place(x=160,y=130)
window.mainloop()
(5)Canvas:画布,一般用来放图片或者视频,视频就是一帧帧刷新进行显示。【进入学习Canvas】
import tkinter as tk #tk界面
master = tk.Tk()#新建tk根窗口
index_frame =tk.Frame(master)
index_frame.grid() #设置参数,可以调整大小,位置等
#新建画布
canvas = tk.Canvas(index_frame,height = 463, width = 895)
canvas.pack(side = 'top')
master.mainloop() #窗口消息循环
4、tk界面插入背景图片
import tkinter as tk #tk界面
master = tk.Tk()#新建tk根窗口
index_frame =tk.Frame(master)
index_frame.grid() #设置参数,可以调整大小,位置等
#新建画布
canvas = tk.Canvas(index_frame,height = 463, width = 895)
canvas.pack(side = 'top')
#创建画布背景图
global photo #设置全局变量,解决图片不显示的问题
photo = tk.PhotoImage(file = 'bg_img.gif') #gif图片的路径,不支持jpg,png
canvas.create_image(449, 233, image=photo)#前面为偏移参数
master.mainloop() #窗口消息循环
若遇到问题,可参考Python tkinter之PhotoImage图片显示问题
5、动态更新人脸识别结果
用label显示人脸识别结果,才用循环创建label覆盖,即可实现动态显示,直接点击右上角关闭会报错,需要处理
import tkinter as tk
import time
index=tk.Tk()
index.wm_title("测试")
index.geometry('500x300+450+200')
var = tk.StringVar() #设置string类型的变量
var.set("0") #设置初始值
tk.Label(index, text= "动态显示" , font = ("微软雅黑",24)).grid(row=1,column=0)
while(True):
var.set(chr(ord(var.get())+ 1))#设置显示的内容,如人脸识别返回的结果
tk.Label(index, text= var.get() , font = ("微软雅黑",24)).grid(row=1,column=1) #每次显示的位置要一致,覆盖效果
time.sleep(1)
index.update() #不断更新
index.mainloop()
6、在画布Canvas实时显示视频流
跟上面动态显示结果类似,也是采用循环1帧帧更新摄像头获取的图像,实现动态显示视频,直接点击右上角关闭会报错,需要处理
import tkinter as tk
from PIL import Image, ImageTk#图像控件
import cv2
cap = cv2.VideoCapture(0)#创建摄像头对象
top = tk.Tk()
top.title('人脸识别门禁系统')
top.geometry('580x600+450+100')
#绘制视频画布
canvas = tk.Canvas(top,bg = 'white',width = 580,height = 600 )
canvas.pack()
#图像预处理
def tkImage():
ref,frame=cap.read()
frame = cv2.flip(frame, 1) #摄像头翻转
cvimage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
pilImage=Image.fromarray(cvimage)
pilImage = pilImage.resize((image_width, image_height),Image.ANTIALIAS)
tkImage = ImageTk.PhotoImage(image=pilImage)
return tkImage
#动态显示
while True:
pic = tkImage()
canvas.create_image(0,0,anchor = 'nw',image = pic)
top.update()
cap.release()
top.mainloop()
7、日志
建立一个txt文件,记录下管理员的操作,以及人脸识别结果。
import time
import datetime
now_time = datetime.datetime.now()
str_time = now_time.strftime("%Y-%m-%d %X")# 格式化时间字符串
log = open("log.txt", "a",encoding='utf-8')#每次打开会接着往下写,若文件不存在则会自己创建
log = open("log.txt", "w",encoding='utf-8')#每次打开会清空日志内容,若文件不存在则会自己创建
log.write(str_time+" "+"管理员重置人脸识别门禁系统日志!\n")
log.close()# 关闭打开的文件
三、说明
1、整合
结合前几期的内容,以及这期的主要内容,进行代码整合,即可实现tkinter界面的人脸识别门禁管理系统,在此基础上,可以继续添加管理员的注册管理,人脸信息登记,对视频流进行图像处理,加一些动态特效啥的,还可以对接数据库等等
2、tk界面动态显示的时候,直接右上角关闭界面会报错的问题
因为程序在循环里还在执行,直接关闭会摧毁窗口,但图像还在获取,会报Erro: Too early to create image的错误 。
解决方法:利用protocol("WM_DELETE_WINDOW",quit)#触发点击窗口关闭处理事件。捕获点击右上角关闭事件,然后在quit函数里进行处理,通知界面不要再获取图像了,常见方法就是在获取图像函数里设置信号量,之后该释放的资源释放,该摧毁的摧毁,该关闭的关闭
3、一些小点
(1)密码输入框: tk.Entry(login_frame,show='*'),通过参数show='*',隐藏输入的内容
(2)组件设置后,要设置place或者grid或者pack方法才会显示
(3)为了弥补tk组件的美观,ttk可以设置style,类似css,具体参考官方文档
(4)在画布上添加组件:canvas.create_window(210, 50, window=tk.Entry(login_frame, borderwidth=3)),实现在画布的背景图片上添加组件。