Python与人工智能入门实践——简易人脸识别系统

Python与人工智能入门实践——简易人脸识别系统

写在前面:

笔者在寒假期间进行了一些简短的实训,主要内容包括简单的爬虫和简单的人脸识别算法,由于时间有限,对于python也是第一次详细学习,功能较为简单,提供给入学者参考,帮助大家进入py的世界,若有不正确或不明确的地方欢迎指正。

                                                                                                                                                                             

一、相关包和工具的安装

人脸识别所需要的包有:

dlib

opencv

numpy

pandas

pillow

为了方便安装可以从网上下载.whl文件,安装过程省去了联网下载的过程

另外建立可视化界面这里用到的是tkinter包

tkinter包中主要提供了一些按钮、文字等功能,类似于java中的swing

 另外用到了两个文件是

这两个是已经训练好了人脸识别模型和人脸68个关键点位置提取

二、编写代码

主要功能是用户注册时拍照抓取人脸,在登录时重新拍照验证是否与已注册用户相匹配

1.建立项目

主要包括一下文件

2.data包中的主要功能是保存用户数据和判断是否是同一个人,利用两个图像的欧氏距离,若小于0.35则找到匹配对象

data_util.py
import pickle
import numpy

# 保存用户数据到文件中
def save(user_info):

    # 以追加二进制的方式打开一个文件
    file = open("userInfo.dat", 'ab')

    # 使用pickle将user_info对象写入到文件中
    pickle.dump(user_info, file)


    file.close()
def read():
    #以二进制的方式读取一个文件
    file = open("userInfo.dat", 'rb')
    #将文件中的二进制还原回对象
    ls=[]

    while True:
        try:
            user_info=pickle.load(file)
            ls.append(user_info)
        except:
            break

    file.close()
    return ls
# 从文件中查询用户数据
def query(face):
    ls=read()
    for user_info in ls:
        user_face=user_info.face
        #计算user_face与face之间的欧氏距离
        #将128维向量转成矩阵
        old_face=numpy.array(user_face)
        new_face=numpy.array(face)

        dist=numpy.sqrt(numpy.sum(numpy.square(old_face-new_face)))
        if dist<0.35:
            return user_info

user_info.py

#描述用户信息
class UserInfo():
    def __init__(self,name,face):
        self.name=name
        self.face=face

3.utils包主要是调用摄像头进行拍照与图片处理

camera.py

import cv2
import copy
def get_photo():
    try:
        #VideoCapture打开一个视频,filename   0:打开默认摄像头
        cap=cv2.VideoCapture(0)
        #读取一个图片出来
        font=cv2.FONT_HERSHEY_COMPLEX
        while True:
            flag,photo=cap.read()
            #深度拷贝

            photo2=copy.deepcopy(photo)
            #在窗口绘制文本
            cv2.putText(photo, "Press space to take photos", (20,40), font, 1, (0,255,0))#img, text(中文不能正常显示), org, fontFace(定义的FONT_HERSHEY_COMPLEX), fontScale(行距), color,
            #显示图片
            cv2.imshow('Camera',photo)
            key=cv2.waitKey(1)

            if key==ord(' '):#转成asc码进行检测才可以
                cap.release()
                cv2.destroyAllWindows()
                return photo2
    except:
        return None
if __name__ == '__main__':
    get_photo()

img_handle.py

import dlib
import cv2
import os
#图像处理类
class ImgHandle():
    def __init__(self):
        #获取正面人脸检测器
        self.detector=dlib.get_frontal_face_detector()

        # 人脸68特征点
        predictor_path = 'shape_predictor_68_face_landmarks.dat'
        self.sp = dlib.shape_predictor(predictor_path)

        # 128D 向量生成器
        face_rec_model_path = 'dlib_face_recognition_resnet_model_v1.dat'
        self.facerec = dlib.face_recognition_model_v1(face_rec_model_path)
    def get_handle(self,img):
        #检测图片中的人脸区域,返回值为图片中检测到人脸的区域,可能有多个
        dets=self.detector(img,1)
        #如果处理过程过慢,可以将1->0,或者用cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)将图片改成灰度图以提高效率

        #判断人脸数量
        if len(dets)==1:
            for det in dets:
                # print(det.top())
                # print(det.bottom())
                # print(det.left())
                # print(det.right())#图片坐标

                cv2.rectangle(img,(det.left(),det.top()),(det.right(),det.bottom()),(255,0,0))#在图片上绘制矩形区域
                #调用检测器提取68个特征点
                shape=self.sp(img,det)
                count = 1
                for pt in shape.parts():
                    cv2.circle(img,(pt.x,pt.y,),2,(0,0,255))
                    cv2.putText(img,str(count),(pt.x,pt.y),cv2.FONT_HERSHEY_PLAIN,0.5,(0,255,0))
                    count+=1
                face_descriptor=self.facerec.compute_face_descriptor(img,shape)
                #print(face_descriptor)
                return face_descriptor
        else:
            return None

4.view包主要利用tkinter设计简单的界面

login_frame.py

import tkinter
from tkinter import messagebox
import numpy
import cv2
from PIL import ImageTk, Image
from utils.camera import get_photo
from utils.img_handle import ImgHandle
from data.data_util import query
class LoginFrame(tkinter.Frame):

    def __init__(self, master=None, root=None):
        super().__init__(master=master, bg='yellow')

        self.root = root

        btn_back = tkinter.Button(master=self, text='返回', command=self.btn_back_click)
        btn_back.place(x=20, y=30, width=80, height=40)

        btn_pic = tkinter.Button(master=self, text='拍照', command=self.btn_pic_click)
        btn_pic.place(x=20, y=90, width=80, height=40)

        btn_save = tkinter.Button(master=self, text='验证', command=self.btn_save_click)
        btn_save.place(x=20, y=150, width=80, height=40)
        self.lab = tkinter.Label(self)
        self.lab.place(x=120, y=20)
        self.face = None


    def btn_back_click(self):
        self.root.show_frm('main')
    def btn_pic_click(self):
        photo = get_photo()

        if isinstance(photo, numpy.ndarray):
            img_handle = ImgHandle()
            self.face = img_handle.get_handle(photo)

            if self.face == None:
                messagebox.showinfo('提示', '人脸检测失败,请确保拍照时有且仅有一个人')
            else:
                # 转换颜色通道
                rgb_photo = cv2.cvtColor(photo, cv2.COLOR_BGR2RGB)
                # 显示图片
                # 图片格式转换,从opencv的矩阵格式转换成Tk中的位图格式
                img = Image.fromarray(rgb_photo)
                img_tk = ImageTk.PhotoImage(image=img)
                # 将图片设置给label
                self.lab.imgtk = img_tk
                self.lab.config(image=img_tk)
        else:
            messagebox.showinfo('提示', '图片抓取失败!请检查您的摄像头是否可用。')

    def btn_save_click(self):
        if self.face==None:
            messagebox.showinfo('提示', '请先拍照')
        else:
            user_info=query(self.face)
            if user_info!=None:
                messagebox.showinfo('登录成功', '欢迎'+user_info.name+'进入系统')
            else:
                messagebox.showinfo('登录失败', '请确认是否是合法用户或者调整拍照角度重新拍照')

main_frame.py

import tkinter
import tkinter.font
class MainFrame(tkinter.Frame):

    def __init__(self, master=None, root=None):
        super().__init__(master=master)

        self.root = root
        font = tkinter.font.Font(size=20,family="楷体")
        lab=tkinter.Label(master=self,text='欢迎使用ST人脸识别系统',font=font,foreground="red")
        lab.pack(pady=50)
        #font_login = tkinter.font.Font(size=30)
        btn_reg = tkinter.Button(master=self, text='注册', command=self.btn_reg_click,font=font,bg="red")
        btn_reg.pack(pady=30,ipady=30,ipadx=150)

        btn_login = tkinter.Button(master=self, text='登录', command=self.btn_login_click,font=font,bg="blue")
        btn_login.pack(pady=30,ipady=30,ipadx=150)


    def btn_reg_click(self):
        self.root.show_frm('reg')

    def btn_login_click(self):
        self.root.show_frm('login')

reg_frame.py

import tkinter
import numpy
from tkinter import messagebox
from utils.camera import get_photo
from PIL import Image,ImageTk
from tkinter.simpledialog import askstring
import cv2
from utils.img_handle import ImgHandle
import data.user_info
from data.user_info import UserInfo
import data.data_util
class RegFrame(tkinter.Frame):

    def __init__(self, master=None, root=None):
        super().__init__(master=master)
        self.root = root
        btn_back = tkinter.Button(master=self, text='返回', command=self.btn_back_click)
        btn_back.place(x=20, y=30, width=80, height=40)

        btn_pic = tkinter.Button(master=self, text='拍照', command=self.btn_pic_click)
        btn_pic.place(x=20, y=90, width=80, height=40)

        btn_save = tkinter.Button(master=self, text='保存', command=self.btn_save_click)
        btn_save.place(x=20, y=150, width=80, height=40)
        #label控件显示照片
        self.lab=tkinter.Label(self)
        self.lab.place(x=120, y=20)
        self.face=None
    def btn_back_click(self):
        self.root.show_frm('main')
    def btn_pic_click(self):

        photo = get_photo()

        if isinstance(photo,numpy.ndarray):
            img_handle=ImgHandle()
            self.face=img_handle.get_handle(photo)

            if self.face==None:
                messagebox.showinfo('提示', '人脸检测失败,请确保拍照时有且仅有一个人')
            else:
                #转换颜色通道
                rgb_photo=cv2.cvtColor(photo,cv2.COLOR_BGR2RGB)
                #显示图片
                #图片格式转换,从opencv的矩阵格式转换成Tk中的位图格式
                img=Image.fromarray(rgb_photo)
                img_tk=ImageTk.PhotoImage(image=img)
                #将图片设置给label
                self.lab.imgtk=img_tk
                self.lab.config(image=img_tk)
        else:
            messagebox.showinfo('提示', '图片抓取失败!请检查您的摄像头是否可用。')



    def btn_save_click(self):
        if self.face!=None:
            #获取姓名
            name=askstring('输入','请输入名字')
            if name==None:
                messagebox.showinfo('提示', '请输入姓名')
            else:
                user_info=UserInfo(name,self.face)
                data.data_util.save(user_info)
            pass
        else:
            messagebox.showinfo('提示', '请先拍照再保存')

main_win.py

# 主界面
import tkinter
from view.main_frame import MainFrame
from view.reg_frame import RegFrame
from view.login_frame import LoginFrame

# 从Tk类派生出类MainWin ,
class MainWin(tkinter.Tk):

    # 重写构造方法
    def __init__(self):
        super().__init__()

        # 设置标题
        self.title('人脸识别登录系统')

        # 窗口大小和位置
        self.center()

        # Frame
        container = tkinter.Frame(self, bg='#FF0000')
        container.pack(fill=tkinter.BOTH, expand=True)
        container.rowconfigure(0, weight=1)
        container.columnconfigure(0, weight=1)

        # 将主Frame 添加到container中
        self.main_frm = MainFrame(container, self)
        self.main_frm.grid(row=0, column=0, sticky=tkinter.NSEW)

        # 将注册的Frame添加到container中
        self.reg_frm = RegFrame(container, self)
        self.reg_frm.grid(row=0, column=0, sticky=tkinter.NSEW)

        # 登录Frame
        self.login_frm = LoginFrame(container, self)
        self.login_frm.grid(row=0, column=0, sticky=tkinter.NSEW)

        self.show_frm('main')

    # 显示不同的Frame
    def show_frm(self, name):

        if name == 'main':
            self.main_frm.tkraise()
        elif name == 'reg':
            self.reg_frm.tkraise()
        else:
            self.login_frm.tkraise()

    def center(self):
        self.width = 800
        self.height = 600

        screen_width = self.winfo_screenwidth()
        screen_height = self.winfo_screenheight()

        x = (screen_width - self.width) // 2
        y = (screen_height - self.height) // 2

        self.geometry('%dx%d+%d+%d'%(self.width, self.height, x, y))



if __name__ == '__main__':
    win = MainWin()
    win.mainloop()

三、操作过程

首先注册,空格键拍照,保存并输入名字,为了有助于学习,拍照出来的照片处理结果用蓝色矩形框选中了识别出来的人脸区域,68个特征点用绿色的数字标注出来。

返回主界面登录界面重新拍照,拍完后验证,若成功则显示欢迎xxx进入,失败会提示:请确认是否是合法用户或者调整拍照角度重新拍照。

最后希望这篇博客能够帮助各位完成人脸识别的基础入门( ̄▽ ̄)/

猜你喜欢

转载自blog.csdn.net/gcn_Raymond/article/details/86613056