整体代码
# -*- coding:utf-8 -*-
#!/usr/bin/env python
# @Time : 2020/02/21 9:48
# @Author : Cxk
# @File : face_recongnition.py··········
from tkinter.messagebox import *
from tkinter import *
import cv2, os, math, operator
from PIL import Image
from functools import reduce
# python -m pip install --upgrade pip -i https://pypi.doubanio.com/simple
# pip install opencv-python==4.5.3.56 -i https://pypi.doubanio.com/simple
# pip install pillow==8.4.0 -i https://pypi.doubanio.com/simple
# 先输入一个随机账号点击人脸注册,再用这个账号进行人脸登录
def makeFace(facename, msg):
print(msg) #显示提示信息
# cv2.namedWindow("face_recognition")
# cv2.waitKey(0)
cap = cv2.VideoCapture(0) #打开摄像头
while(cap.isOpened()): #如果摄像头处于打开状态,则...
try:
ret, img = cap.read() #读取图像
if ret == True:#读取成功
cv2.imshow("face_recognition", img) #显示图像
k = cv2.waitKey(100) #每0.1秒读一次键盘
if k == ord("z") or k == ord("Z"): #如果输入z
cv2.imwrite(facename,img) #把读取的img保存至facename文件
image = cv2.imread(facename) #读取刚刚保存的facename文件至image变量,作为下面人脸识别函数的参数
faces = faceCascade.detectMultiScale(image, scaleFactor=1.1, minNeighbors=5, minSize=(30,30), flags = cv2.CASCADE_SCALE_IMAGE)
#print(faces)
(x, y, w, h) = (faces[0][0], faces[0][1], faces[0][2], faces[0][3]) #取出第一张人脸区域
# print(x,y,w,h)
image1 = Image.open(facename).crop((x, y, x+w, y+h)) #抓取人脸区域的图像并存至image1变量
image1 = image1.resize((200, 200), Image.ANTIALIAS) #把取得的人脸区域的分辨率变为200x200
image1.save(facename) #把经过处理的人脸文件保存至facename文件
break
except Exception as e:
print(e)
continue
cap.release() #关闭摄像头
cv2.destroyAllWindows() #关闭窗口
return
def cxk(number):
diff=0.0
recogname = "%s_recogface.jpg"%number #预存的人脸文件
loginname = "%s_loginface.jpg"%number #登录者的人脸文件
os.system("cls") #清屏
if(os.path.exists(recogname)):#如果预存的人脸文件已存在
msg = "摄像头打开后按 z 键进行拍照对比!"
makeFace(loginname, msg) #创建登录者人脸文件
pic1 = Image.open(recogname) #打开预存的人脸文件
pic2 = Image.open(loginname) #打开登录者人脸文件
h1 = pic1.histogram() #取预存片文件的直方图信息
h2 = pic2.histogram() #取登录者图片的直方图信息
diff = math.sqrt(reduce(operator.add, list(map(lambda a,b: (a-b)**2, h1, h2)))/len(h1)) #计算两个图形差异度
else: #如果预存的人脸文件不存在
diff=200.0
return diff
class LoginPage(object):
def __init__(self, master=None):
self.root = master
winWidth = 650
winHeight = 400
screenWidth = self.root.winfo_screenwidth()
screenHeight = self.root.winfo_screenheight()
x = int((screenWidth - winWidth) / 2)
y = int((screenHeight - winHeight) / 2)
# 设置窗口初始位置在屏幕居中
self.root.geometry("%sx%s+%s+%s" % (winWidth, winHeight, x, y))
# 设置窗口图标
# root.iconbitmap("./image/icon.ico")
# 设置窗口宽高固定
self.root.resizable(0, 0)
self.student_number = StringVar()
self.student_pw = StringVar()
self.createPage()
def createPage(self):
'''
登录页面
1:创建图片组件
2:根目录基础上添加Frame容器
3:Frame容器上添加注册控件
'''
# 注释掉的背景图片,可自己打开并替换其他图片
# bm=PhotoImage(file=r'cxk.gif')
# self.lab3=Label(self.root,image=bm)
# self.lab3.bm=bm
# self.lab3.pack()
self.page = Frame(self.root)
self.page.pack()
Label(self.page).grid(row=0, stick=W)
Label(self.page, text = ' 学号:').grid(row=1, column=0,stick=W, pady=10)
Entry(self.page, textvariable=self.student_number).grid(row=1, column=1, stick=E)
Label(self.page, text = ' 密码:').grid(row=2, column=0,stick=W, pady=10)
Entry(self.page, textvariable=self.student_pw, show='*').grid(row=2, column=1, stick=E)
Button(self.page, text='学生人脸保存注册', command=self.signup).grid(row=3, column=0)
Button(self.page, text='学生帐号密码登录',command="#").grid(row=3, column=1)
Button(self.page, text='学生人脸识别登录', command=self.student_loginCheck).grid(row=3,column=2)
def signup(self):
Student_number=self.student_number.get()
recogname = "%s_recogface.jpg"%Student_number
if Student_number=="":
showinfo(title="错误",message='请输入学生账号!')
elif(os.path.exists(recogname)):
showinfo(title="错误",message='该学生已保存人脸信息,可直接登录!')
else:
msg = "摄像头打开后按 z 键进行拍照!\n"
makeFace(recogname, msg) #建立预存人脸文件
showinfo(title='确认',message='人脸信息保存成功!')
def student_loginCheck(self):
try:
Student_number=self.student_number.get()
if Student_number=="":
showinfo(title="错误",message='请输入学生账号!')
else:
a=cxk(Student_number)
if(a <= 100): #若差度在100内,可通过验证
print("通过验证,欢迎使用本系统! diff=%4.2f" % a)
self.page.destroy()
# 注释掉的背景图片,可自己打开并替换其他图片
# self.lab3.pack_forget()
StudentPage(self.root)
elif (a==200.0):
showinfo(title='错误',message='数据库无该学生人脸信息,请先进行人脸注册!')
else:
print("没有通过验证! diff=%4.2f" % a)
except Exception as e:
print(e)
showinfo(title='错误',message='输入错误,请重新输入!')
class StudentPage(object):
def __init__(self, master=None):
self.root = master #定义内部变量root
self.root.geometry('%dx%d' % (650, 400)) #设置窗口大小
self.root.resizable(0,0) #防止用户调整尺寸
self.createPage()
def createPage(self):
self.menuPage = MenuFrame(self.root) # 创建不同Frame
class MenuFrame(Frame): # 继承Frame类
def __init__(self, master=None):
Frame.__init__(self, master)
self.root = master #定义内部变量root
self.createPage()
def createPage(self):
strs="人脸识别登录成功!"
Label(self.root,text=strs,font=("Arial", 30)).pack()
root = Tk() #建立一个根窗口,所有窗口的基础
root.title('学生管理系统')
# 找到opencv的安装路径,cmd输入where python可查看python安装包路径Python37\\Lib\\site-packages\\cv2\\data\\
casc_path = "C:\\Users\\******\\AppData\\Local\\Programs\\Python\\Python37\\Lib\\site-packages\\cv2\\data\\haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(casc_path) #创建识别对象
LoginPage(root)#进入调用登录
root.mainloop()
人脸识别代码
源自邓文渊老师的《毫无障碍学python》--->本书PDF,提取码: t5kk
本人已将老师一部分源码进行更改,整体思路未动,需要老师整书源码的同学私聊我。
import cv2, os, math, operator
from PIL import Image
from functools import reduce
def makeFace(facename, msg):
print(msg) #显示提示信息
cap = cv2.VideoCapture(0) #打开摄像头
while(cap.isOpened()): #如果摄像头处于打开状态,则...
try:
ret, img = cap.read() #读取图像
if ret == True:#读取成功
cv2.imshow("face_recognition", img) #显示图像
k = cv2.waitKey(100) #每0.1秒读一次键盘
if k == ord("z") or k == ord("Z"): #如果输入z
cv2.imwrite(facename,img) #把读取的img保存至facename文件
image = cv2.imread(facename) #读取刚刚保存的facename文件至image变量,作为下面人脸识别函数的参数
faces = faceCascade.detectMultiScale(image, scaleFactor=1.1, minNeighbors=5, minSize=(30,30), flags = cv2.CASCADE_SCALE_IMAGE)
(x, y, w, h) = (faces[0][0], faces[0][1], faces[0][2], faces[0][3]) #取出第一张人脸区域
image1 = Image.open(facename).crop((x, y, x+w, y+h)) #抓取人脸区域的图像并存至image1变量
image1 = image1.resize((200, 200), Image.ANTIALIAS) #把取得的人脸区域的分辨率变为200x200
image1.save(facename) #把经过处理的人脸文件保存至facename文件
break
except:
print("错误")
continue
cap.release() #关闭摄像头
cv2.destroyAllWindows() #关闭窗口
def cxk(number):
diff=0.0
recogname = "%s_recogface.jpg"%number #预存的人脸文件
loginname = "%s_loginface.jpg"%number #登录者的人脸文件
os.system("cls") #清屏
if(os.path.exists(recogname)):#如果预存的人脸文件已存在
msg = "摄像头打开后按 z 键进行拍照对比!"
makeFace(loginname, msg) #创建登录者人脸文件
pic1 = Image.open(recogname) #打开预存的人脸文件
pic2 = Image.open(loginname) #打开登录者人脸文件
h1 = pic1.histogram() #取预存片文件的直方图信息
h2 = pic2.histogram() #取登录者图片的直方图信息
diff = math.sqrt(reduce(operator.add, list(map(lambda a,b: (a-b)**2, h1, h2)))/len(h1)) #直方图的相似性度量
else: #如果预存的人脸文件不存在
diff=200.0
return diff
识别对象
casc_path = "C:\\Users\\Cxk\\Anaconda3\Lib\site-packages\cv2\data\\haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(casc_path) #创建识别对象
系统登录代码
窗口代码是本人前段时间发的史上最全面的python学生管理系统教程(一)
登录窗口代码
from tkinter.messagebox import *
from tkinter import *
class LoginPage(object):
def __init__(self, master=None):
self.root = master
winWidth = 650
winHeight = 400
screenWidth = self.root.winfo_screenwidth()
screenHeight = self.root.winfo_screenheight()
x = int((screenWidth - winWidth) / 2)
y = int((screenHeight - winHeight) / 2)
# 设置窗口初始位置在屏幕居中
self.root.geometry("%sx%s+%s+%s" % (winWidth, winHeight, x, y))
# 设置窗口图标
# root.iconbitmap("./image/icon.ico")
# 设置窗口宽高固定
self.root.resizable(0, 0)
self.student_number = StringVar()
self.student_pw = StringVar()
self.createPage()
def createPage(self):
'''
登录页面
1:创建图片组件
2:根目录基础上添加Frame容器
3:Frame容器上添加注册控件
'''
bm=PhotoImage(file=r'cxk.gif')
self.lab3=Label(self.root,image=bm)
self.lab3.bm=bm
self.lab3.pack()
self.page = Frame(self.root)
self.page.pack()
Label(self.page).grid(row=0, stick=W)
Label(self.page, text = ' 学号:').grid(row=1, column=0,stick=W, pady=10)
Entry(self.page, textvariable=self.student_number).grid(row=1, column=1, stick=E)
Label(self.page, text = ' 密码:').grid(row=2, column=0,stick=W, pady=10)
Entry(self.page, textvariable=self.student_pw, show='*').grid(row=2, column=1, stick=E)
Button(self.page, text='学生人脸保存注册', command=self.signup).grid(row=3, column=0)
Button(self.page, text='学生帐号密码登录',command="#").grid(row=3, column=1)
Button(self.page, text='学生人脸识别登录', command=self.student_loginCheck).grid(row=3,column=2)
def signup(self):
Student_number=self.student_number.get()
recogname = "%s_recogface.jpg"%Student_number
if Student_number=="":
showinfo(title="错误",message='请输入学生账号!')
elif(os.path.exists(recogname)):
showinfo(title="错误",message='该学生已保存人脸信息,可直接登录!')
else:
msg = "摄像头打开后按 z 键进行拍照!\n"
makeFace(recogname, msg) #建立预存人脸文件
showinfo(title='确认',message='人脸信息保存成功!')
def student_loginCheck(self):
try:
Student_number=self.student_number.get()
if Student_number=="":
showinfo(title="错误",message='请输入学生账号!')
else:
a=cxk(Student_number)
if(a <= 100): #若差度在100内,可通过验证
print("通过验证,欢迎使用本系统! diff=%4.2f" % a)
self.page.destroy()
self.lab3.pack_forget()
StudentPage(self.root)
elif (a==200.0):
showinfo(title='错误',message='数据库无该学生人脸信息,请先进行人脸注册!')
else:
print("没有通过验证! diff=%4.2f" % a)
except:
showinfo(title='错误',message='输入错误,请重新输入!')
class StudentPage(object):
def __init__(self, master=None):
self.root = master #定义内部变量root
self.root.geometry('%dx%d' % (650, 400)) #设置窗口大小
self.root.resizable(0,0) #防止用户调整尺寸
self.createPage()
def createPage(self):
self.menuPage = MenuFrame(self.root) # 创建不同Frame
class MenuFrame(Frame): # 继承Frame类
def __init__(self, master=None):
Frame.__init__(self, master)
self.root = master #定义内部变量root
self.createPage()
def createPage(self):
strs="人脸识别登录成功!"
Label(self.root,text=strs,font=("Arial", 30)).pack()
根窗口(main)
root = Tk() #建立一个根窗口,所有窗口的基础
root.title('学生管理系统')
casc_path = "C:\\Users\\Cxk\\Anaconda3\Lib\site-packages\cv2\data\\haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(casc_path) #创建识别对象
LoginPage(root)#进入调用登录
root.mainloop()
具体流程
- 运行登录页面,输入学号进行登录,如果没有输入学号则无法进行登录
- 输入学号尝试登录,系统查找是否有该学号预存人脸信息,如无提示进行人脸保存
- 用户人脸信息获取函数makeFace(facename, msg),参数一:图片保存地址,参数二:提示信息。保存头像信息如下
- 登录运行,检查是否有该学号预存人脸信息,如有开启摄像头进行登录人脸信息获取,将获取到的人脸信息与预存人脸信息进行对比。
- 关键部分:获取人脸
faces = faceCascade.detectMultiScale(image, scaleFactor=1.1, minNeighbors=5, minSize=(30,30), flags = cv2.CASCADE_SCALE_IMAGE) (x, y, w, h) = (faces[0][0], faces[0][1], faces[0][2], faces[0][3]) #取出第一张人脸区域 image1 = Image.open(facename).crop((x, y, x+w, y+h)) #抓取人脸区域的图像并存至image1变量 image1 = image1.resize((200, 200), Image.ANTIALIAS) #把取得的人脸区域的分辨率变为200x200
人脸信息比对
pic1 = Image.open(recogname) #打开预存的人脸文件 pic2 = Image.open(loginname) #打开登录者人脸文件 h1 = pic1.histogram() #取预存片文件的直方图信息 h2 = pic2.histogram() #取登录者图片的直方图信息 diff = math.sqrt(reduce(operator.add, list(map(lambda a,b: (a-b)**2, h1, h2)))/len(h1)) #直方图的相似性度量
直方图的相似性度量
运行效果
人脸信息预存
人脸识别
总结
- 花了大半天时间研究每一行代码,到直方图对比时头大了,不过还是受益匪浅。
- 本来想用图片来代替人脸的,发现每次比对都不成功,人脸能准确获取,但是同一张图片的直方图对比相差总是超过150。
- 没具体测试过准确度,但是感觉受外部影响比较打,比如是否戴眼镜,头发是否遮住额头,脸部是否偏移,总体来说超过70%成功率,当然这是在同一时刻测的。
- 可以用多线程来启动人脸获取,不然系统界面有时会有卡顿。
- 进一步想将人脸信息加密保存至数据库,等有空再试行一下。具体思路想了一下,先将图片转为字符,将字符加密转入数据库,数据库提出解密进行比对,比对成功删除登录人脸信息。不过这样一来好像效率要低到爆炸。。溜了溜了