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进入,失败会提示:请确认是否是合法用户或者调整拍照角度重新拍照。
最后希望这篇博客能够帮助各位完成人脸识别的基础入门( ̄▽ ̄)/