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值对应。

原文地址:https://www.cnblogs.com/blsx/p/11272744.html

时间: 2024-10-05 23:31:32

opencv实现人脸识别(五) 运用tkinter进行GUI绘制 整合人脸识别模块的相关文章

汽车Vin码识别——可以嵌入到手机里的新OCR识别技术

汽车Vin码识别(车架号识别),顾名思义,就是识别汽车的Vin码(车架号),汽车Vin码识别(车架号识别)利用的是OCR识别技术,支持视频流获取图像,自动触发识别,另外汽车Vin码识别(车架号识别)sdk支持安卓和ios主流操作平台.便于集成.以汽车维修店为例,以前车相对少,都是人为的抄录下汽车Vin码(车架号)来做记录,不仅慢,还容易出错,比如弄错一位数字,有可能导致解析的发动机型号不正确,那么就会直接影响到维修的正确性和维修进度.因此,北京易泊时代科技有限公司结合汽车Vin码(车架号)主机厂

Win7 x64 Eclipse无法识别手机 / adb interface有黄色感叹号,无法识别

今天公司停电,因此把安卓项目带回宿舍做.宿舍的笔记本,装的是Win7 x64,手机连上电脑后,windows可以识别,但Eclipse的DDMS中却无法识别,什么都没有: 然后打开设备管理器查看,发现windows能正常识别设备(HUAWEI C8817E),而ADB Interface(有的机器可能显示Android ADB什么的)却显示有黄色感叹号,是驱动程序没有正常安装: 那怎样正确安装ADB Interface的驱动程序呢?折腾了一个晚上加一个上午,还是没办法,什么system32目录.

C++开发人脸性别识别教程(6)——通过SVM实现性别识别

上一篇教程中我们介绍了怎样使用OpenCv封装的FaceRecognizer类实现简单的人脸性别识别,这里我们为大家提供第二种主要的性别识别手段--支持向量机(SVM). 支持向量机在解决二分类问题方面有着强大的威力(当然也能够解决多分类问题).性别识别是典型的二分类模式识别问题,因此非常适合用SVM进行处理,同一时候OpenCv又对SVM进行了非常好的封装,调用非常方便,因此我们在这个性别识别程序中考虑增加SVM方法. 在这里我们採用了HOG+SVM的模式来进行,即先提取图像的HOG特征.然后

(转)C++开发人脸性别识别教程(6)——通过SVM实现性别识别

原文地址:http://blog.csdn.net/u013088062/article/details/50480518 上一篇教程中我们介绍了如何使用OpenCv封装的FaceRecognizer类实现简单的人脸性别识别,这里我们为大家提供另外一种基本的性别识别手段——支持向量机(SVM). 支持向量机在解决二分类问题方面有着强大的威力(当然也可以解决多分类问题),性别识别是典型的二分类模式识别问题,因此很适合用SVM进行处理,同时OpenCv又对SVM进行了很好的封装,调用非常方便,因此我

OpenCV探索之路(十五):角点检测

角点检测是计算机视觉系统中用来获取图像特征的一种方法.我们都常说,这幅图像很有特点,但是一问他到底有哪些特点,或者这幅图有哪些特征可以让你一下子就识别出该物体,你可能就说不出来了.其实说图像的特征,你可以尝试说一下这幅图有几个矩形啊几个圆形啊,有几条直线啊,当然啦,你也可以说一下有几个角点. 什么是角点? 角点通常被定义为两条边的交点.比如,三角形有三个角,矩形有四个角,这些就是角点,也是他们叫做矩形.三角形的特征,我们看到一些几何图形具有三个角,那么我们便可以脱口而出说这是一个三角形. 上面所

Python下opencv使用笔记(五)(图像的平滑与滤波)

对于图形的平滑与滤波,但从滤波角度来讲,一般主要的目的都是为了实现对图像噪声的消除,增强图像的效果. 首先介绍二维卷积运算,图像的滤波可以看成是滤波模板与原始图像对应部分的的卷积运算.关于卷积运算,找到几篇相关的博客: 图像处理:基础(模板.卷积运算) 图像处理-模板.卷积的整理 对于2D图像可以进行低通或者高通滤波操作,低通滤波(LPF)有利于去噪,模糊图像,高通滤波(HPF)有利于找到图像边界. (一)统一的2D滤波器cv2.filter2D Opencv提供的一个通用的2D滤波函数为cv2

人脸识别---基于深度学习和稀疏表达的人脸识别算法

介绍 基于深度学习和稀疏表达的人脸识别算法 1 利用VGGFace提取人脸特征 2 PCA对人脸特征进行降维 3 稀疏表达的人脸匹配 Code 1 介绍 本文将介绍一种基于深度学习和稀疏表达的人脸识别算法.首先,利用深度学习框架(VGGFace)提取人脸特征:其次,利用PCA对提取的特征进行降维:最后,利用稀疏表达分类实现特征匹配.我采用CMC曲线评价在AR数据库上的识别性能.最后我还提供了整个过程的code. 2 基于深度学习和稀疏表达的人脸识别算法 2.1 利用VGGFace提取人脸特征 下

【OpenCV】opencv3.0中的SVM训练 mnist 手写字体识别

前言: SVM(支持向量机)一种训练分类器的学习方法 mnist 是一个手写字体图像数据库,训练样本有60000个,测试样本有10000个 LibSVM 一个常用的SVM框架 OpenCV3.0 中的ml包含了很多的ML框架接口,就试试了. 详细的OpenCV文档:http://docs.opencv.org/3.0-beta/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html mnist数据下载:http://yann.l

人脸识别活体检测技术讨论:基于背景人脸相对运动的活体判断方法

活体检测是人脸识别安全性的重要保障,是人脸识别厂商将产品普及应用于各行业的重要竞争力.目前活体判断的方法很多,但很难基于一种方法就能达到理想的效果,往往需要多种算法的交叉判断,本文主要介绍一种简单有效的活体判断方法.在很多情况下,高清照片和真人在某些光线条件下在相机中的成像很接近,这就导致仅仅基于面部的特征很难准确判断活体.在大部分场景下,人手拿着照片,手机或者pad不会完全静止不动,而在有运动时,真人和照片的区别很大:真实人脸运动与背景无相关性,照片,手机或者Pad在运动时,人脸运动与周围背景