用python的tkinter库调用笔记本的摄像头,出现的画面一直闪烁白光。
# -*- coding: utf-8 -*-
"""
Created on Mon May 31 23:39:19 2021
@author: ZHOU
"""
# -*- coding: utf-8 -*-
import os # 调用os多操作系统接口库
import threading # 调用threading多线程运行库
import time # 调用系统时间戳库
import tkinter as tk # 调用窗口tk
import tkinter.messagebox
from tkinter import ttk, TOP
import cv2 # 调用OpenCV图像处理库
import matplotlib.pyplot as plt # 调用matplotlib绘图库
from PIL import Image, ImageTk # 调用图像处理库pillow
import api_face # 调用本地函数库 用于登入外部机器学习库并调用人脸识别函数
plt.rcParams['font.sans-serif'] = ['SimHei'] # 载入字体
# print('请输入需录入的人脸图片路径/文件名:')
# pic_name=input()
# 利用matplotlib显示图片函数
def pshow(words, picture):
plt.imshow(picture[:, :, ::-1]) # 将读取的图片以RGB转换为BGR
plt.title(words), plt.xticks([]), plt.yticks([])
plt.show() # 显示图片
class Login(ttk.Frame): # 定义窗口大类
def __init__(self, win):
ttk.Frame.__init__(self, win)
frame0 = ttk.Frame(self)
frame1 = ttk.Frame(self)
win.title("人脸识别")
win.minsize(1240, 620)
self.center_window() # 执行中置窗口函数
self.thread_run = None # 赋值 线程1默认关闭
self.thread_run2 = None # 线程2默认关闭
self.camera = None # 摄像头默认关闭
# 定义tk窗口属性
# self.pilImage = Image.open("img/start.png")
# self.tkImage = ImageTk.PhotoImage(image=self.pilImage)
# self.image_ctl = tk.Label(frame0, image=self.tkImage)
# self.image_ctl.pack()
frame0.pack(side=TOP, fill=tk.Y, expand=1)
frame1.pack(side=TOP, fill=tk.Y, expand=1)
self.facer = ttk.Label(frame1, text='', font=('Times', '20')) # 字体
self.facer.pack()
# def filefound(): # 定义获取图片路径的函数
# filepath= askopenfilename() # 获取文件路径
# pic_name=filepath
# self.pic_path2 = pic_name # 赋值给图像2
#
# pshow('所选人像图片',pic_img) # 显示所选图片
self.pic_path3 = './star.png'
# pic_img=cv2.imread(self.pic_path3)
# pic_xz = pic_img.shape # 计算图像大小
# pic_h=pic_xz[0] # 得出图片高度
# pic_w=pic_xz[1] # 得出图片宽度
# turn_w=pic_w*500/pic_h # 限制最大高度为580 以防窗口过小不完全显示 等比例转换宽度
# turn_w=int(turn_w)
# print('人像图像大小(高 宽):',pic_h,pic_w)
# print ('路径:',filepath)
# # 在tk窗口中显示所选图片
self.pilImage = Image.open(self.pic_path3)
self.photo = self.pilImage.resize((500, 500)) # 限制最大高度为580 等比缩放显示
self.tkImage = ImageTk.PhotoImage(image=self.photo)
self.image_ctl = tk.Label(frame0, image=self.tkImage)
self.image_ctl.pack()
# #e.delete(0, END) # 将输入框里面的内容清空
# #e.insert(0, filepath)
#
# #button2=Button(frame1,text="button2",command=filefound).grid(row=0,column=3)
# # 按钮1 调用filefound函数 获取选择图片的路径 并赋值给self.pic_path2 输出图像
# self.face_button1 = ttk.Button(frame1, text="1. 选择人像图片", width=15, command=filefound)
# self.face_button1.pack(side=TOP)
# 按钮2 调用摄像头函数
self.url_face_button = ttk.Button(frame1, text="使用相机识别", width=15, command=self.cv_face)
self.url_face_button.pack(side=TOP)
# self.file_pic_button = ttk.Button(frame1, text="本地文件识别", width=15, command=self.file_pic)
# self.file_pic_button.pack(side=TOP)
self.pack(fill=tk.BOTH, expand=tk.YES, padx="10", pady="10")
# 使弹出的窗体处于屏幕的中间位置
def center_window(self):
screenwidth = log.winfo_screenwidth() # 获取屏幕分辨率宽
screenheight = log.winfo_screenheight() # 获取屏幕分辨率高
log.update() # 更新窗口
width = log.winfo_width() # 重新赋值
height = log.winfo_height()
size = '+%d+%d' % ((screenwidth - width) / 2, (screenheight - height) / 2)
# 重新赋值大小 大小为屏幕大小/2
log.geometry(size) # 以新大小定义窗口
# def file1(self):
# self.pic_path = askopenfilename(title="选择识别图片", filetypes=[("jpg图片", "*.jpg"), ("png图片", "*.png")])
def cv_face(self): # 调用摄像头函数
if self.thread_run:
if self.camera.isOpened(): # 如果已经打开则关闭
self.camera.release()
print("关闭摄像头")
self.camera = None
self.thread_run = False
return
if self.camera is None: # 如果没有摄像头则尝试打开
self.camera = cv2.VideoCapture(0) # 利用OpenCV调用外摄像头
if not self.camera.isOpened(): # 如果没有打开 则调用内摄像头
self.camera = None
print("没有外置摄像头")
self.camera = cv2.VideoCapture(1) # 用OpenCV调用内摄像头
if not self.camera.isOpened(): # 如果没有打开 则打开失败
print("没有内置摄像头")
tkinter.messagebox.showinfo('警告', '摄像头打开失败!')
self.camera = None
return
else:
print("打开内置摄像头")
else:
print("打开外置摄像头")
self.thread = threading.Thread(target=self.video_thread) # 多线程函数执行摄像头运行函数
self.thread.daemon = True
self.thread.start()
self.thread_run = True
def video_thread(self): # 开始摄像头运行
self.thread_run = True # 多线程1开启
self.thread2 = threading.Thread(target=self.video_pic)
self.thread2.daemon = True
self.thread2.start()
self.thread_run2 = True
while self.thread_run: # 循环一直调用摄像头
_, img_bgr = self.camera.read() # 以bgr格式读取摄像头内的截图
gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) # 灰度转换
# 在CV官方机器学习库内加载人脸识别分类器
# Python\Python38-32\Lib\site-packages\cv2\data 这个目录下也有
classifier = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
color = (0, 255, 0) # 绿色线
# 识别器进行识别
faceRects = classifier.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))
# 识别器返回一个列表, 里面是每个识别出的人脸的区域, 左上和右下定点的坐标
# print(faceRects) #[[113 42 60 60]] 前两个值是左上定点的xy坐标,第三个是width 宽度对应y的变化, 另一个就是x的
# 判断识别结果集合长度
if len(faceRects):
for faceRect in faceRects:
x, y, w, h = faceRect
# 用矩形框选出人脸 最后一个参数2是框线宽度
cv2.rectangle(img_bgr, (x, y), (x + h, y + w), color, 2)
img = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # 颜色转换
im = Image.fromarray(img)
w, h = im.size
pil_image_resized = self.resize(w, h, im) # 调整大小函数
self.imgtk = ImageTk.PhotoImage(image=pil_image_resized)
self.image_ctl.configure(image=self.imgtk)
abc = None
abc = img # 解决摄像头图像闪烁的问题..
print("结束运行")
def video_pic(self): # 视频截图保存及框选函数
dir_name = './img' # 给出图片目录路径
fileimg_list = [] # 建立列表型图片列表
fileimg_list = os.listdir(dir_name) # 获取目录下文件名
file_num = len(fileimg_list) # 获取列表长度
fileimg_num = 0 # 定义文件序号初始值为0
self.thread_run2 = True # 开启多线程
predict_time = time.time() # 获得系统时间
while self.thread_run2: # 循环读取
if time.time() - predict_time > 0.1: # 每0.1s读取一次摄像头截图
print("正在识别中")
_, img_bgr = self.camera.read() # 重新读取摄像头图像
cv2.imwrite("tmp/test.jpg", img_bgr) # 利用cv写入到tmp/test.jpg路径下
# test_pic=cv2.imread('tmp/test.jpg') # 重新读取截图
# pshow('识别截图',test_pic) # 显示截图
#
# # 图像路径 我用的相对路径
# face_mark = 'tmp/test.jpg'
# # 读取截图
# faceImg = cv2.imread(face_mark)
# # 转换灰色
# gray = cv2.cvtColor(faceImg,cv2.COLOR_RGB2GRAY) # 由于tmp/test.jpg路径下的已经转换成RGB保存 所以不用再进行BGR转换
#
# # 在CV官方机器学习库内加载人脸识别分类器
# # Python\Python38-32\Lib\site-packages\cv2\data 这个目录下也有
# classifier = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
# color = (0,255,0) # 绿色线
#
# # 识别器进行识别
# faceRects = classifier.detectMultiScale(gray,scaleFactor=1.2,minNeighbors=3,minSize=(32, 32))
# # 识别器返回一个列表, 里面是每个识别出的人脸的区域, 左上和右下定点的坐标
# # print(faceRects) #[[113 42 60 60]] 前两个值是左上定点的xy坐标,第三个是width 宽度对应y的变化, 另一个就是x的
#
# # 判断识别结果集合长度
# if len(faceRects):
# for faceRect in faceRects:
# x,y,w,h = faceRect
# # 用矩形框选出人脸 最后一个参数2是框线宽度
# cv2.rectangle(faceImg,(x, y), (x + h, y + w), color, 2)
#
#
# pshow('识别结果',faceImg) # 输出框选结果图像并显示
# cv2.imwrite("tmp/test2.jpg", faceImg)
self.pic_path = "tmp/test.jpg" # 重新读取摄像头截图
# self.pilImage = Image.open(self.pic_path)
# self.tkImage = ImageTk.PhotoImage(image=self.pilImage)
# self.image_ctl = tk.Label(frame0, image=self.tkImage)
# self.image_ctl.pack()
try:
if fileimg_num < file_num: # 当文件序号小于总列表长度时
# print(fileimg_num)
# img=cv2.imread(dir_name + '/' + fileimg_list[fileimg_num])
# pshow(fileimg_list[fileimg_num],img)
self.pic_path2 = dir_name + '/' + fileimg_list[fileimg_num] # 给出pic_path2的值为目录名+/+文件名
# print(self.pic_path2)
# self.pic_path2='./img/111.jpg'
facestr, result = api_face.facef(self.pic_path, self.pic_path2) # 调用api_face库的人脸识别函数
self.facer.configure(text=str(facestr))
# self.pic() # 对摄像头图像进行尺度变换
if result > 80: # 识别结果大于80
humantitle = ' 姓名: ' + fileimg_list[fileimg_num].partition('.')[0] + " 人脸匹配成功! \n"
print(humantitle) # 输出人像文件文件名
tkinter.messagebox.showinfo('提示', humantitle) # tk窗口提示
try:
f = open("识别记录.txt", "r")
fi = open("识别记录.txt", "a")
txt = time.ctime()
fi.write(txt + humantitle)
f.close()
fi.close() # 将识别成功的记录保存在txt文件下
except:
f = open("识别记录.txt", "w")
txt = time.ctime()
f.write(txt + humantitle)
f.close()
# close_window()
# os.system("python3 ./main.py")
# if result < 20:
# tkinter.messagebox.showinfo('提示', '未检测到人脸!')
break
else: # 小于80失败
fileimg_num = fileimg_num + 1 # 文件序号+1
else: # 超出文件列表长度
tkinter.messagebox.showinfo('提示', '查无此人!人脸匹配失败!')
break
except:
pass
predict_time = time.time() # 读取时间
print("识别结束")
# 看门狗程序(调试用)
# 防止程序关闭时进入死循环跑飞
# print('请输入任意值以继续,否则请关闭窗口以终止程序:')
# a=input()
# print(a)
pass
# def file_pic(self): #识别函数
# dir_name='./img'
# fileimg_list = []
# fileimg_list=os.listdir(dir_name)
# file_num=len(fileimg_list)
# fileimg_num=0
# while True:
# if fileimg_num<file_num:
# print(fileimg_num)
# img=cv2.imread(dir_name + '/' + fileimg_list[fileimg_num])
# pshow(fileimg_list[fileimg_num],img)
# self.pic_path2 = dir_name + '/' + fileimg_list[fileimg_num]
# print(self.pic_path2)
## self.pic_path2='./img/111.jpg'
# facestr, result = api_face.facef(self.pic_path, self.pic_path2) # 调用api_face库的人脸识别函数
# self.facer.configure(text=str(facestr))
# #self.pic() # 对摄像头图像进行尺度变换
# if result > 80: #识别结果大于80
# tkinter.messagebox.showinfo('提示', '人脸匹配成功!') # tk窗口提示
# print('人像图片路径:'+self.pic_path2) # 输出人像文件路径
# try:
# f=open("识别记录.txt","r")
# fi=open("识别记录.txt","a")
# txt=time.ctime()
# fi.write(txt+' 人像图片路径: '+self.pic_path2+" 人脸匹配成功! \n")
# f.close()
# fi.close() # 将识别成功的记录保存在txt文件下
# except:
# f=open("识别记录.txt","w")
# txt=time.ctime()
# f.write(txt+' 人像图片路径: '+self.pic_path2+" 人脸匹配成功! \n")
# f.close()
#
#
#
# # close_window()
# # os.system("python3 ./main.py")
# #if result < 20:
# # tkinter.messagebox.showinfo('提示', '未检测到人脸!')
# break
# else: # 小于80失败
# fileimg_num=fileimg_num+1
#
#
# else:
# tkinter.messagebox.showinfo('提示', '人脸匹配失败!')
# break
# def pic(self): # 对摄像头图像进行尺度变换
# self.pilImage3 = Image.open(self.pic_path) # 用pillow读取摄像头图像
# w, h = self.pilImage3.size # 计算大小赋值给宽 高
# pil_image_resized = self.resize(w, h, self.pilImage3) # 调整大小函数
# self.tkImage3 = ImageTk.PhotoImage(image=pil_image_resized)
# self.image_ctl.configure(image=self.tkImage3) # 输出结果
def resize(self, w, h, pil_image): # 调整大小函数
w_box = 1000 # 定义最大宽度
h_box = 500 # 最大高度
f1 = 1.0 * w_box / w # 最大值/真实值
f2 = 1.0 * h_box / h
factor = min([f1, f2]) # 取最小值
width = int(w * factor) # 用最小值*对应值 调整到最大定义值 等比调整另一个值
height = int(h * factor)
return pil_image.resize((width, height), Image.LANCZOS) # 输出调整
def close_window():
print("已关闭人脸识别")
if Login.thread_run:
Login.thread_run = False
Login.thread.join(2.0)
log.destroy()
if __name__ == '__main__':
log = tk.Tk()
login = Login(log)
# close,退出输出destroy
log.protocol('清除窗口', close_window)
# 进入消息循环
log.mainloop()


