Python3+Opencv+PyMySQL实现人脸识别

2020-07-14 09:54:59 浏览数 (1)

前言

今天带来的是基于Opencv(c 底层编译)的人脸识别,再利用PyMySQL实现对数据的储存。 具体步骤是:

使用到的库,模块有 ①CV2(Opencv):图像识别,摄像头调用 ②os:文件操作 ③numpy:NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库 ④PIL:Python Imaging Library,Python平台事实上图像处理的标准库 ⑤PyMySQL:用户信息存储(可以用 Navicat for MySQL来可视化操作)

当然电脑应该先下载mysql数据库,本次默认的是使用mysql自带的test数据库,具体参数也可自行修改,下载教程百度有,使用可以看我另一篇博文
建议用pip命令时使用清华镜像网站,否则下载可能很慢
代码语言:javascript复制
pip install [名字] -i https://pypi.tuna.tsinghua.edu.cn/simple

还需要下载Opencv提供的人脸模型特征数据 版本自己选择,还有显示的字体(simsun.ttc文件)应该放到py文件的同个目录 下载网址是下载地址Opencv官网 下载完后将 ./data/haarcascades/haarcascade_frontalface_default.xml 复制到跟py文件同个目录

首先

首先应该在py文件的目录里面创建一个名为Picture_resources的子文件夹,当然可以修改代码中的生成文件目录 先是对人脸数据的抓取,然后生成一帧一帧的人脸灰度图片储存起来,过程中还应该将用户输入的信息对应储存到MySQL数据库里面

代码语言:javascript复制
import datetime
import os
import shutil
import time
import cv2 as cv
import numpy as np
import pymysql
'''
@Author:Himit_ZH
@Date: 2020.3.1
'''
#与数据库进行数据连接
def PutDatatoSql(sname,sno):
    flag = 1
    con = pymysql.connect(host='localhost', password='123456', user='root', port=3306,db='facedata')
    # 创建游标对象
    cur = con.cursor()
    # 判断是否存在库
    #判断是否存在表 无则自动创建
    sql1 = r'''
                CREATE TABLE IF NOT EXISTS t_stu (
                id int PRIMARY KEY NOT NULL auto_increment,
                sname VARCHAR(20) NOT NULL,
                sno VARCHAR(14) NOT NULL,
                created_time DATETIME )
                 '''
    cur.execute(sql1)
    # 编写查询数据的sql
    sql2 = 'select * from t_stu where sname=%s and sno=%s'
    try:
        cur.execute(sql2,args=(sname,sno))
        con.commit()
        # 处理结果集
        student = cur.fetchall()
        if student:
            con.close()
            flag = 2
            return flag
    except Exception as e:
        print(e)
        print('查询数据失败')
        flag = 0
        return flag
    # 编写插入数据的sql
    sql3 = 'insert into t_stu(sname,sno,created_time) values(%s,%s,%s)'
    dt = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    try:
        # 执行sql
        cur.execute(sql3, (sname,sno,dt))
        # 提交事务
        con.commit()
        print('插入数据成功')
        return flag
    except Exception as e:
        print(e)
        con.rollback()
        print('插入数据失败')
        flag = 0
        return flag
    finally:
        # 关闭连接
        con.close()



if __name__ == '__main__':
    while True:
        face_id = input('请输入学号:')
        face_name = input('请输入姓名:')
        result = PutDatatoSql(face_name, face_id)
        if  result == 1:
            break
        elif result == 2:
            # 可能存在数据库有记录 但是图像资源被删掉了,这种情况重新录入
            if not os.path.exists('./Picture_resources/Stu_'   str(face_id)): #文件夹是否存在
                break
            elif not os.listdir("./Picture_resources/Stu_"   str(face_id)): #文件夹里面是否有文件
                break
            else:
                print('该用户已存在!')
        else:
            print('数据库未能成功连接')
    print('请看向摄像头,3秒后开始采集300张人脸图片(可按ESC强制退出)...')
    count = 0 #统计照片数量
    path = "./Picture_resources/Stu_"   str(face_id) #人脸图片数据的储存路径
    #读取视频
    cap=cv.VideoCapture(0)
    time.sleep(3) #停顿三秒后打开摄像头
    while True:
        flag,frame=cap.read()
        #print('flag:',flag,'frame.shape:',frame.shape)
        if not flag:
            break
        # 将图片灰度
        gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
        # 加载特征数据
        face_detector = cv.CascadeClassifier('haarcascade_frontalface_default.xml')
        faces = face_detector.detectMultiScale(gray, 1.2, 5)
        if not os.path.exists(path):  # 如果没有对应文件夹,自动生成
            os.makedirs(path)
        if len(faces) > 1: #一帧出现两张照片丢弃,原因:有人乱入,也有可能人脸识别出现差错
            continue
        # 框选人脸,for循环保证一个能检测的实时动态视频流
        for x, y, w, h in faces:
            cv.rectangle(frame, (x, y), (x   w, y   h), color=(0, 255, 0), thickness=2)
            count  = 1
            cv.imwrite(path '/' str(count)   '.png', gray[y:y   h, x:x   w])
            # 显示图片
            cv.imshow('Camera', frame)
        print('已采集成功人脸照片数量为:' str(count))
        if 27 == cv.waitKey(1) or count>=300: #按ESC可退出 默认采集500张照片
            break
    #关闭资源
    print('采集照片成功,3秒后退出程序...')
    time.sleep(3)
    cv.destroyAllWindows()
    cap.release()

其次

一样要在py文件同目录下创建一个名叫Trainer的文件夹,当然你可以修改代码里面的生成文件路径 然后利用opencv的LBPHFaceRecognizer对人脸创建模型,制作我们自己的人脸识别器,存储为名叫trainer.yml的数据文件,等待人脸识别来调用 补充:LBP是一种特征提取方式,能提取出图像的局部的纹理特征,最开始的LBP算子是在3X3窗口中,取中心像素的像素值为阀值,与其周围八个像素点的像素值比较,若像素点的像素值大于阀值,则此像素点被标记为1,否则标记为0。这样就能得到一个八位二进制的码,转换为十进制即LBP码,于是得到了这个窗口的LBP值,用这个值来反映这个窗口内的纹理信息。

代码语言:javascript复制
import os
import time

import cv2
import pymysql
from PIL import Image
import numpy as np
'''
@Author:Himit_ZH
@Date: 2020.3.1
'''
def getIdfromSql(sno):
    con = pymysql.connect(host='localhost', database='test', user='root', password='123456', port=3306)
    # 创建游标对象
    cur = con.cursor()
    # 编写查询的sql
    sql = 'select id from t_stu where sno=' sno
    # 执行sql
    try:
        cur.execute(sql)
        # 处理结果集
        student = cur.fetchone()
        id = student[0]
        return id
    except Exception as e:
        print(e)
        print('查询所有数据失败')
    finally:
        con.close()

def getImageAndLabels(path):
    facesSamples=[]
    imageFiles = []
    ids = []
    for root, dirs, files in os.walk(path):
        # root 表示当前正在访问的文件夹路径
        # dirs 表示该文件夹下的子目录名list
        # files 表示该文件夹下的文件list
        # 遍历文件
        for file in files:
            imageFiles.append(os.path.join(root, file))
    #检测人脸
    face_detector = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
    #遍历列表中的图片
    count = 0
    for imagefile in imageFiles: #获得各文件夹名字
        imagefile=imagefile.replace('\','/')
        id = int(getIdfromSql(imagefile.split('/')[2].split('_')[1]))

        PIL_img=Image.open(imagefile).convert('L') #打开图片并且转为灰度图片
        #将图像转换为数组
        img_numpy=np.array(PIL_img,'uint8')
        faces = face_detector.detectMultiScale(img_numpy)
        for x,y,w,h in faces:
            facesSamples.append(img_numpy[y:y h,x:x w])
            ids.append(id)
    return facesSamples,ids

if __name__ == '__main__':
    #图片路径
    path='./Picture_resources'
    #获取图像数组和id标签数组
    print('开始采集数据...')
    faces,ids=getImageAndLabels(path)
    print('采集数据结束,开始训练数据...')
    #获取训练对象
    recognizer=cv2.face.LBPHFaceRecognizer_create()
    recognizer.train(faces,np.array(ids))
    #保存文件
    recognizer.write('./Trainer/trainer.yml')
    print('训练数据成功!3秒后程序自动关闭...')
    time.sleep(3)

最后

再次打开摄像头对人脸进行识别,此次识别先取出数据库对应id的数据,然后显示在识别框旁边。

代码语言:javascript复制
import cv2
import pymysql
import numpy
from PIL import Image, ImageFont, ImageDraw
'''
@Author:Himit_ZH
@Date: 2020.3.1
'''
#此函数是为了实现中文显示,opencv自带显示文字的函数的并不能显示中文
def cv2ImgAddText(img, text, left, top, textColor=(0, 255, 0), textSize=20):
    if (isinstance(img, numpy.ndarray)):  #判断是否OpenCV图片类型
        img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(img)
    fontText = ImageFont.truetype(
        "./simsun.ttc", textSize, encoding="utf-8")
    draw.text((left, top), text, textColor, font=fontText)
    return cv2.cvtColor(numpy.asarray(img), cv2.COLOR_RGB2BGR)
def getDataFromSql():
    names = {}
    # 创建连接
    con = pymysql.connect(host='localhost', database='facedata', user='root', password='123456', port=3306)
    # 创建游标对象
    cur = con.cursor()
    # 编写查询的sql
    sql = 'select * from t_stu'
    # 执行sql
    try:
        cur.execute(sql)
        # 处理结果集
        students = cur.fetchall()
        for student in students:
            id = student[0]
            sname = student[1]
            sno = student[2]
            names[int(id)] = sname
    except Exception as e:
        print(e)
        print('查询所有数据失败')
    finally:
        # 关闭连接
        con.close()
        return names

if __name__ == '__main__':
    recogizer=cv2.face.LBPHFaceRecognizer_create()
    #加载训练数据集文件
    recogizer.read('./Trainer/trainer.yml')
    #加载人脸特征数据
    face_detector = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
    names = getDataFromSql() #获取数据库对应数据
    cam = cv2.VideoCapture(0) #开启默认摄像头,其他外接摄像头参数可换为1,2....
    minW = 0.1*cam.get(3)
    minH = 0.1*cam.get(4)
    font = cv2.FONT_HERSHEY_SIMPLEX #字体格式
    while True:
        ret, img = cam.read() #读取每一帧图片
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #设置成灰度图片
        faces = face_detector.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 = recogizer.predict(gray[y:y h, x:x w])
            if confidence < 60:
                idname = names[idnum]
                confidence = "{0}%".format(round(100 - confidence))
            else:
                idname = 'unknown'
                confidence = "{0}%".format(round(100 - confidence))
            #将名字和相似度显示在图片上
            img = cv2ImgAddText(img, idname, x, y-30, (0, 255, 0),30) #显示中文
            #cv2.putText(img, str(idname), (x 5, y-5), font, 1, (0, 0, 255), 2)
            cv2.putText(img, str(confidence), (x 5, y h-5), font, 1, (0, 0, 255), 2)
        cv2.imshow('Camera', img)
        k = cv2.waitKey(1)
        if k == 27:#按ESC关闭摄像头,退出人脸识别
            break
    cam.release()
    cv2.destroyAllWindows()

最后的最后

附上已经生成exe文件的压缩包,不过还应该自己下载mysql数据库,将登陆密码设为123456,只要下载后,按顺序打开程序,①录入图片,②训练数据,③进行人脸识别,就能成功。

已经生成exe文件可以没有python环境 但是还是要有mysql数据库!!! 完整压缩包下载

0 人点赞