opencv实现人脸识别(五) 运用tkinter进行GUI绘制 整合人脸识别模块
因为之前学习过tkinter库,所以在学习了人脸识别模块的编写后,
打算绘制一个简单的GUI来应用人脸识别功能。
主界面如下所示:
签到打开在点开后直接进行人脸识别,如果成功则自动关闭视频窗口。
录入新的人脸界面:
输入姓名后打开摄像头,开始拍摄镜头前的人的照片,然后生成训练文件。
并且可以查询历史签到记录。
这里需要另外添加的模块是关于数据库的,这里选用了sqlite,所需的功能也很简单,
只需要两个表,一个用来存放用户姓名,一个用来存放签到记录。
这里是数据库模块的代码
db.py:
import sqlite3
from datetime import * class record:
def __init__(self):
# 创建或打开一个数据库
# check_same_thread 属性用来规避多线程操作数据库的问题
self.conn = sqlite3.connect("recordinfo.db", check_same_thread=False)
# 创建游标
self.cursor = self.conn.cursor()
# 建表
self.conn.execute('create table if not exists record_table('
'id integer primary key autoincrement,'
'name varchar(30) ,'
'record_time timestamp)') self.conn.execute('create table if not exists name_table('
'id integer primary key autoincrement,'
'name varchar(30))') # 插入数据
def insert_record(self, name):
self.conn.execute('insert into record_table values (null, ?, ?)', (name, datetime.now()))
self.conn.commit() def insert_name(self, name):
self.conn.execute('insert into name_table values (null, ?)', [name])
self.conn.commit() # 搜索用户名
def query_name(self):
self.cursor.execute("select name from name_table")
results = self.cursor.fetchall()
name_list = []
for i in results:
i = list(i)
name_list += i return name_list def query_record(self):
self.cursor.execute('select * from record_table')
results = self.cursor.fetchall() return results def close(self):
self.cursor.close()
self.conn.close()
然后是把之前的拍照模块,训练模块整合在一起,绑定到录入新的人脸的 确定按钮上
add_face.py:
import os
import cv2
from PIL import Image, ImageTk
import numpy as np
import db def makeDir():
if not os.path.exists("face_trainer"):
os.mkdir("face_trainer")
if not os.path.exists("FaceData"):
os.mkdir("FaceData") def getFace(name):
cap = cv2.VideoCapture(0)
face_detector = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
count = 0
while True:
sucess, img = cap.read() # 从摄像头读取图片
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_detector.detectMultiScale(gray, 1.3, 5)
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0))
count += 1
cv2.imwrite("FaceData/User." + name.get() + '.' + str(count) + '.jpg', gray[y: y + h, x: x + w])
cv2.imshow('image', img)
# 保持画面的持续。
k = cv2.waitKey(1)
if k == 27: # 通过esc键退出摄像
break
elif count >= 20: # 得到1000个样本后退出摄像
break
cap.release()
cv2.destroyAllWindows() def getImagesAndLabels(path,detector, usernames):
imagePaths = [os.path.join(path, f) for f in os.listdir(path)]
faceSamples = []
ids = []
for imagePath in imagePaths:
PIL_img = Image.open(imagePath).convert('L')
img_numpy = np.array(PIL_img, 'uint8')
username = os.path.split(imagePath)[-1].split(".")[1]
id = 1
for x in usernames:
if username == x:
break
else:
id += 1 faces = detector.detectMultiScale(img_numpy)
for (x, y, w, h) in faces:
faceSamples.append(img_numpy[y:y + h, x: x + w])
ids.append(id)
return faceSamples, ids def trainFace(names):
# 人脸数据路径
path = 'FaceData'
recognizer = cv2.face.LBPHFaceRecognizer_create()
detector = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
faces, ids = getImagesAndLabels(path, detector, names)
recognizer.train(faces, np.array(ids))
recognizer.write(r'face_trainer\trainer.yml') def add_face(name, names):
makeDir()
getFace(name)
trainFace(names)
user = db.record()
user.insert_name(name.get())
然后把识别人脸的模块绑定给主界面的签到按钮
detect.py:
import cv2
import time
import db
def check( names):
cam = cv2.VideoCapture(0)
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read('face_trainer/trainer.yml')
cascadePath = "haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(cascadePath)
font = cv2.FONT_HERSHEY_SIMPLEX
minW = 0.1 * cam.get(3)
minH = 0.1 * cam.get(4)
while True:
ret, img = cam.read()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(
gray,
scaleFactor=1.2,
minNeighbors=5,
minSize=(int(minW), int(minH))
)
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
idnum, confidence = recognizer.predict(gray[y:y + h, x:x + w]) if confidence < 100:
username = names[idnum-1]
confidence = "{0}%".format(round(100 - confidence)) cv2.putText(img, str(username), (x + 5, y - 5), font, 1, (0, 0, 255), 1)
cv2.putText(img, str(confidence), (x + 5, y + h - 5), font, 1, (0, 0, 0), 1)
cv2.imshow('camera', img)
time.sleep(2) db.record().insert_record(username) # 签到信息插入数据库
cam.release()
cv2.destroyAllWindows()
return
else:
idnum = "unknown"
confidence = "{0}%".format(round(100 - confidence))
cv2.putText(img, str(idnum), (x + 5, y - 5), font, 1, (0, 0, 255), 1)
cv2.putText(img, str(confidence), (x + 5, y + h - 5), font, 1, (0, 0, 0), 1) cv2.imshow('camera', img)
k = cv2.waitKey(10)
if k == 27:
break
cam.release()
cv2.destroyAllWindows()
把上述模块整合到GUI界面中
from tkinter import *
from tkinter import ttk
import add_face
import db
import detect class APP:
def __init__(self): self.root = Tk()
self.root.title('FACE')
self.root.geometry('%dx%d' % (400, 300)) # 数据库实例创建
self.mydb = db.record() self.createFirstPage() # 新录入的人的姓名
self.name = StringVar() mainloop() def createFirstPage(self):
self.page1 = Frame(self.root)
self.page1.grid()
Label(self.page1, height=4, text='人脸识别系统', font=('粗体', 20)).grid(columnspan=2)
#self.usernames 是 用户名字组成的列表
self.usernames = []
self.usernames = self.mydb.query_name() self.button11 = Button(self.page1, width=18, height=2, text="签到打卡", bg='red', font=("宋", 12),
relief='raise', command = lambda :detect.check( self.usernames))
self.button11.grid(row=1, column=0, padx=25, pady=10)
self.button12 = Button(self.page1, width=18, height=2, text="录入新的人脸", bg='green', font=("宋", 12),
relief='raise', command = self.createSecondPage)
self.button12.grid(row=1, column=1, padx=25, pady=10)
self.button13 = Button(self.page1, width=18, height=2, text="查询签到信息", bg='white', font=("宋", 12),
relief='raise',command = self.checkDataView)
self.button13.grid( row=2, column=0,padx=25, pady=10)
self.button14 = Button(self.page1, width=18, height=2, text="退出系统", bg='gray', font=("宋", 12),
relief='raise',command = self.quitMain)
self.button14.grid(row=2, column=1,padx=25, pady=10) def createSecondPage(self):
# self.camera = cv2.VideoCapture(0)
self.page1.grid_forget()
self.page2 = Frame(self.root)
self.page2.pack()
Label(self.page2, text='欢迎使用人脸识别系统', font=('粗体', 20)).pack() # 输入姓名的文本框
font1 = ('宋',18)
# self.name = StringVar()
self.text = Entry(self.page2, textvariable=self.name, width=20, font=font1).pack(side=LEFT)
self.name.set('请输入姓名') # 确认名字的按钮
self.button21 = Button(self.page2, text='确认', bg='white', font=("宋", 12),
relief='raise', command=lambda :add_face.add_face( self.name, self.usernames))
self.button21.pack(side=LEFT, padx=5, pady=10) # 返回按钮
self.button22 = Button(self.page2, text="返回", bg='white', font=("宋", 12),
relief='raise',command = self.backFirst)
self.button22.pack(side=LEFT, padx=10, pady=10) def checkDataView(self):
self.page3 = Frame(self.root)
self.page1.grid_forget()
self.root.geometry('700x360')
self.page3.pack()
Label(self.page3, text='今日签到信息', bg='white', fg='red', font=('宋体', 25)).pack(side=TOP, fill='x')
# 签到信息查看视图
self.checkDate = ttk.Treeview(self.page3, show='headings', column=('sid', 'name', 'check_time'))
self.checkDate.column('sid', width=100, anchor="center")
self.checkDate.column('name', width=200, anchor="center")
self.checkDate.column('check_time', width=300, anchor="center") self.checkDate.heading('sid', text='签到序号')
self.checkDate.heading('name', text='名字')
self.checkDate.heading('check_time', text='签到时间') # 插入数据
self.records = self.mydb.query_record()
for i in self.records:
self.checkDate.insert('', 'end', values=i) # y滚动条
yscrollbar = Scrollbar(self.page3, orient=VERTICAL, command=self.checkDate.yview)
self.checkDate.configure(yscrollcommand=yscrollbar.set)
yscrollbar.pack(side=RIGHT, fill=Y) self.checkDate.pack(expand=1, fill=BOTH) # 返回按钮
Button(self.page3, width=20, height=2, text="返回", bg='gray', font=("宋", 12),
relief='raise',command =self.backMain).pack(padx = 20, pady = 20) def backFirst(self):
self.page2.pack_forget()
self.root.geometry('400x300')
self.page1.grid() def backMain(self):
self.root.geometry('400x300')
self.page3.pack_forget()
self.page1.grid() def quitMain(self):
sys.exit(0) if __name__ == '__main__':
demo = APP()
整合的代码逻辑还可以更清楚一点,以后有时间改进,
然后列出了部分学习过程中遇到的新知识和问题,以作参考
QA:
1.Python: cv2.waitKey([delay]) → retval
waitKey()函数的功能是不断刷新图像,频率时间为delay,单位为ms。
返回值为当前键盘按键值。
用cv2.waitKey(n) == 27 break
即为 按下esc键时退出当前画面
2.os.path.join()函数:
连接两个或更多的路径名组件
os.listdir() 方法
用于返回指定的文件夹包含的文件或文件夹的名字的列表。这个列表以字母顺序
3.Image.open(imagePath).convert('L')
L代表转换到灰度图
4.os.path.split 把路径分割成 dirname 和 basename,返回一个元组
5.ret,frame = cap.read()
cap.read()按帧读取视频,ret,frame是获cap.read()方法的两个返回值。其中ret是布尔值,如果读取帧是正确的则返回True,如果文件读取到结尾,它的返回值就为False。frame就是每一帧的图像,是个三维矩阵。
6.把StringVar类型变量传入函数中时,用StringVar.get()方法可以得到字符串类型,并进行字符串连接等操作
7.tkinter库中的组件,在使用command参数时,在函数名前加 lambda: 就可以在函数名后加函数的参数,否则会报错。 例如 Button(root, command= lambda: FUNC(a,b)).pack()
8.遇到一个问题,在进行一次新的人脸录入后,就无法进行签到,必须退出系统重新进入,才能进行签到,报的错误是 error: (-215:Assertion failed) !_src.empty() in function 'cvtColor'
经过检查, 我在写GUI的类时,一开始写的是 self.camera = cv2.VideoCapture(0), 然后将self.camera 调入绑定的动作函数,而在多个动作函数中,都有cv2.VideoCapture(0).release的操作。问题就在于只获取了一次摄像头资源,但释放了多次。
解决方案:把GUI类中的self.camera删去,在执行的动作函数中,分别定义局部变量来获取摄像头资源,然后函数结束时释放资源。
9. 报错:sqlite3.ProgrammingError: Incorrect number of bindings supplied. The current statement uses 1, and there are 3 supplied
在写数据库里的插入函数时,遇到这个问题,例如conn.execute(‘inssert into xxtable values(?)’, a) 假设我插入a时输的是‘abc’,就会提示我 there are 3 supplied。也就是这时候传入的是字符串的长度。
而从下图可以看出,把a 设为元组,那么传的就是长度为一的元组中的元素
因此改为: conn.execute(‘inssert into xxtable values(?)’, (a, ))
10.数据库中设置了两个表,一个记录用户姓名,一个记录用户签到记录,删除表中原有记录,但是由于两个表的主键id都设置为autoincrement, 在记录删除后,id并没有置零。其实在数据库中,有一个表sqlite_sequence,这个表中会记录自增量的值。
因此在删除记录后,需要将自增量也清零
UPDATE sqlite_sequence SET seq = 0 WHERE name = ‘TableName’
11.对于treeview控件的使用,特别是其中的insert函数, 这里要插入的每条记录都是由三个元素组成的,因此需要一个元组组成的列表。而正好数据库中游标的fetchall(),正好符合要求。
12.数据库中 建表时候, 要用到当前时间,可以用 create xxtable (cur_time timestamp ) ,插入数据时则用 datetime.now来获取当前本地时间
13. 报错:IndexError:List index out of range
username = names[idnum-1]
我猜测是idnum的数值越界了。 我打印出函数中idnum的值,发现有35,而本来idnum的值对应的是第几个录入人脸数据的人,当是只录入了两个人,明显有错误。idnum的值由cv2.face.LBPHFaceRecognize_create().predict 函数返回,这个函数与训练文件的生成有关,于是在getImageAndLabel函数中把idnum值重新对应。
开始时对应关系错误,idnum与 表示第几张图片的count值对应, 而应该改为与表示地几个人的id值对应。
opencv实现人脸识别(五) 运用tkinter进行GUI绘制 整合人脸识别模块的更多相关文章
- OpenCV开发笔记(五十五):红胖子8分钟带你深入了解Haar、LBP特征以及级联分类器识别过程(图文并茂+浅显易懂+程序源码)
若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...
- Java基于opencv实现图像数字识别(五)—投影法分割字符
Java基于opencv实现图像数字识别(五)-投影法分割字符 水平投影法 1.水平投影法就是先用一个数组统计出图像每行黑色像素点的个数(二值化的图像): 2.选出一个最优的阀值,根据比这个阀值大或小 ...
- OpenCV添加中文(五)
OpenCV添加文字的方法putText(...),添加英文是没有问题的,但如果你要添加中文就会出现"???"的乱码,需要特殊处理一下. 下文提供封装好的(代码)方法,供OpenC ...
- C++开发人脸性别识别教程(10)——加入图片的人脸检測程序
现在我们的MFC框架已经初具规模,能够读取并显示目录下的图片.在这篇博文中我们将向当中加入人脸检測的程序. 一.人脸检測算法 这里我们使用OpenCv封装的Adaboost方法来进行人脸检測,參见:C ...
- OpenCV开发笔记(五十六):红胖子8分钟带你深入了解多种图形拟合逼近轮廓(图文并茂+浅显易懂+程序源码)
若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...
- 项目实战:Qt+OpenCV大家来找茬(Qt抓图,穿透应用,识别左右图区别,框选区别,微调位置)
前言 本项目的出现理由只是笔者的一个念头,于是利用专业Qt和Opencv相关的知识开发一个辅助工具,本文章仅用于Qt和Opencv结合的学习. Demo演示效果 运行包下载地 ...
- opencv学习笔记(五)镜像对称
opencv学习笔记(五)镜像对称 设图像的宽度为width,长度为height.(x,y)为变换后的坐标,(x0,y0)为原图像的坐标. 水平镜像变换: 代码实现: #include <ios ...
- 【D3.V3.js系列教程】--(十五)SVG基本图形绘制
[D3.V3.js系列教程]--(十五)SVG基本图形绘制 1.path <!DOCTYPE html> <html> <head> <meta charse ...
- Win7 x64 Eclipse无法识别手机 / adb interface有黄色感叹号,无法识别
今天公司停电,因此把安卓项目带回宿舍做.宿舍的笔记本,装的是Win7 x64,手机连上电脑后,windows可以识别,但Eclipse的DDMS中却无法识别,什么都没有: 然后打开设备管理器查看,发现 ...
随机推荐
- 主机,路由器,应用程序,sockets api的关系
- Java实例化对象过程中的内存分配
Java实例化对象过程中的内存分配: https://blog.csdn.net/qq_36934826/article/details/82685791 问题引入这里先定义一个很不标准的“书”类,这 ...
- onselectstart与onselect—禁止选择或禁止复制
这两个事件看起来很相似,事实上却非常的不同. onselectstart 使用js禁止用户选中网页上的内容,IE及Chrome下的方法一样.使用onselectstart,例如 IE: <bod ...
- elasticsearch _all
在轻量搜索中,我们介绍了 _all 字段:一个把其它字段值 当作一个大字符串来索引的特殊字段. query_string 查询子句(搜索 ?q=john )在没有指定字段时默认使用 _all 字段._ ...
- Docker安装redis3.2
1.拉取redis3.2镜像 2.使用docker images查看拉去下来的镜像 3.运行容器,命令如下 docker run -p : -v $PWD/data:/data -d redis:3. ...
- Java排序之归并排序
Java排序之归并排序 1. 简介 归并排序的算法是将多个有序数据表合并成一个有序数据表.如果参与合并的只有两个有序表,则成为二路合并.对于一个原始的待排序数列,往往可以通过分割的方法来归结为多路合并 ...
- SQL-W3School-高级:SQL UNIQUE 约束
ylbtech-SQL-W3School-高级:SQL UNIQUE 约束 1.返回顶部 1. SQL UNIQUE 约束 UNIQUE 约束唯一标识数据库表中的每条记录. UNIQUE 和 PRIM ...
- android studio gradle国内代理设置
android studio在开始都各项目之前都会遇到 gradle 的同步,而在同步过程中很多依赖下载特别慢甚至出现无法现在的情况,有的时候等的时间特别长,甚至要一天,关键是等了大半天之后突然报错, ...
- oracle数据库使用PL/sql导入excel数据
1.打开PL/SQL工具,菜单进入 工具>ODBC导入器(菜单列表倒数第二): 2.连接数据库与选择导入的excel文件表: 3.选择导入的表以及excel与数据库表字段之间的对应关系:
- 阶段5 3.微服务项目【学成在线】_day05 消息中间件RabbitMQ_15.RabbitMQ研究-与springboot整合-声明交换机和队列
复制topic的代码 把常量都设置成public方便其他的类引用 ExchangeBuilder里面有4个方法分别对应四个交换机. 声明Email和短信的队列 队列绑定交换机 所以需要把Bean注入到 ...