Blog Summary: Python | Face Recognition System — Blog Index
GitHub address: Su-Face-Recognition
Note: Please refer to before reading this blog
Tool Installation, Environment Configuration: Python | Face Recognition System—Introduction
UI Interface Design: Python | Face Recognition System — UI Interface Design
UI event processing: Python | Face recognition system - UI event processing
Face Recognition: Python | Face Recognition System—Face Recognition
Liveness Detection: Python | Face Recognition System - Liveness Detection
1. User login
1. Login logic
Information verification (database) -> silent liveness detection -> interactive liveness detection -> face recognition
Once the user fails to log in successfully for more than three times (liveness detection/face recognition is unsuccessful), the system will lock the current user. Users need to be unlocked by an administrator.
# 登录标志
USER_LOGIN_MSG_FLAG = False # 用户信息核验成功标志
USER_LOGIN_FLAG = False # 用户登录成功标志
... ...
# 用户登录
def user_login(self):
if not self.cap.isOpened():
QMessageBox.information(self, "提示", self.tr("请先打开摄像头"))
else:
global USER_LOGIN_FLAG
if not USER_LOGIN_FLAG:
QApplication.processEvents()
login = LoginWindow(self) # 创建信息核验界面对象
login.exec_()
global USER_LOGIN_MSG_FLAG
global USER_LOGIN_NAME
if USER_LOGIN_MSG_FLAG:
# 登录信息成功,进行活体检测
QMessageBox.about(self, '提示', '登录成功,进行活体检测')
if self.detect_face():
# 活体检测成功,进行人脸识别
global ENCODING_TEMP
face_encoding = FaceEncodingUtil.decoding_FaceStr(ENCODING_TEMP)
if self.recognize_instant_face(face_encoding):
QMessageBox.about(self, '提示', '登陆成功')
self.save_record(USER_LOGIN_NAME, '使用摄像头进行登录') # 使用excel表格进行保存
USER_LOGIN_FLAG = True
else:
QMessageBox.about(self, '提示', '人脸识别失败,请重新登录')
if USER_LOGIN_NAME != "":
UserSqlUtil.add_name_warn(USER_LOGIN_NAME)
USER_LOGIN_MSG_FLAG = False
else:
QMessageBox.about(self, '提示', '活体检测失败,请重新登录')
if USER_LOGIN_NAME != "":
UserSqlUtil.add_name_warn(USER_LOGIN_NAME)
USER_LOGIN_MSG_FLAG = False
login.destroy()
else:
QMessageBox.about(self, '提示', '用户已经登录')
2. Information verification
The user clicks the [User Login] button, and the information verification interface pops up on the main interface, requiring the user to enter the account number and password. After clicking OK, the system accesses the database to determine whether the input information is correct.
Information verification interface related code:
# 用户登录界面
class LoginWindow(QDialog, LoginMsgUi):
def __init__(self, parent=None):
super(LoginWindow, self).__init__(parent)
self.setupUi(self)
self.minimize_button.clicked.connect(self.showMinimized)
self.close_button.clicked.connect(self.cancel_login)
self.confirm_button.clicked.connect(self.search_user)
self.cancel_button.clicked.connect(self.cancel_login)
# 点击确认,搜索用户
def search_user(self):
input_name = self.name_lineEdit.text()
input_password = self.password_lineEdit.text()
if input_name == "":
QMessageBox.about(self, '提示', '姓名不能为空')
elif input_password == "":
QMessageBox.about(self, '提示', '密码不能为空')
else:
row = UserSqlUtil.search_by_name("\"" + input_name + "\"")
if row:
result = row[0]
password = result[1]
if input_password != password:
QMessageBox.about(self, '提示', '密码输入错误')
else:
global USER_LOGIN_MSG_FLAG
count = UserSqlUtil.search_count_warn("\"" + input_name + "\"")
if count >= 3:
QMessageBox.about(self, '警告', '该账号目前已被锁定')
USER_LOGIN_MSG_FLAG = False
else:
global ENCODING_TEMP
global USER_LOGIN_NAME
USER_LOGIN_MSG_FLAG = True
ENCODING_TEMP = result[5]
USER_LOGIN_NAME = input_name
self.close_window()
else:
QMessageBox.about(self, '提示', '该用户不存在')
# 点击取消按钮
def cancel_login(self):
global USER_LOGIN_MSG_FLAG
USER_LOGIN_MSG_FLAG = False
self.close_window()
# 关闭窗口
def close_window(self):
self.name_lineEdit.setPlaceholderText("请输入姓名")
self.password_lineEdit.setPlaceholderText("请输入密码")
self.close()
Database table structure (user table)
database code
# -*- coding: utf-8 -*-
import pymysql
def init_conn():
conn = pymysql.connect(
host="127.0.0.1", # 数据库的IP地址
user="root", # 数据库用户名称
password="root", # 数据库用户密码
db="contest", # 数据库名称
port=3306, # 数据库端口名称
charset="utf8" # 数据库的编码方式
)
return conn
def execute_with_bool(sql_str, args=()):
conn = init_conn()
cursor = conn.cursor()
try:
cursor.execute(sql_str, args)
conn.commit()
return True
except Exception as e:
conn.rollback()
print(e)
return False
finally:
cursor.close()
def execute_with_list(sql_str):
conn = init_conn()
cursor = conn.cursor()
results = []
try:
cursor.execute(sql_str)
results = cursor.fetchall()
except Exception as e:
conn.rollback()
print(e)
finally:
cursor.close()
return results
def insert_data(name, password, age, sex, more, face_encoding):
return execute_with_bool(
"insert into user(name,password,age,sex,more,face_encoding) values(%s,%s,%s,%s,%s,%s)",
(name, password, age, sex, more, face_encoding))
def update_by_name(name, password, age, sex, more, face_encoding):
return execute_with_bool(
"update user set name=%s,password=%s,age=%s,sex=%s,more=%s,face_encoding=%s where name = %s",
(name, password, age, sex, more, face_encoding, name))
def update_by_name_without_encoding(name, age, sex, more):
return execute_with_bool("update user set name=%s,age=%s,sex=%s,more=%s where name = %s",
(name, age, sex, more, name))
def search_all_msg():
return execute_with_list("select * from user")
def search_by_name(name):
return execute_with_list("select * from user where name = " + name)
def search_count_name(name):
return execute_with_list("select count(*) from user where name = " + name)[0][0]
def delete_by_name(name):
return execute_with_bool("delete from user where name = %s", name)
def search_count_warn(name):
return execute_with_list("select count(*) from warn where name = " + name)[0][0]
def add_name_warn(name):
return execute_with_bool("insert into warn(name) values(%s)", name)
3. Liveness detection
The liveness detection function detect_face() is detailed in the blog:
4. Face recognition
The face recognition function recognize_instant_face() is detailed in the blog:
Face recognition uses the function recognize_instant_face(), which requires the parameter face_encoding, which is a matrix of 1*128. When we retrieve the face code of the currently logged-in user from the database, it is a string type and needs to be converted. Write the tool class FaceEncodingUtil, and call the written method decoding_FaceStr to convert. Its tool class methods are as follows:
# -*- coding: utf-8 -*-
import numpy
def decoding_FaceStr(encoding_str):
# 将字符串转为numpy ndarray类型,即矩阵
# 转换成一个list
decoding_list = encoding_str.strip(' ').split(',')
# 将list中str转换为float
decoding_float = list(map(float, decoding_list))
face_encoding = numpy.array(decoding_float)
return face_encoding
2. User registration
1. Judge
The camera captures the current face -> fill in the information -> encode the facial information -> save the information (database)
The camera captures the current face photo, uses the current time point as the photo name and saves it in the photo directory, and deletes the photo after the facial information encoding is successful.
signal_register = pyqtSignal() # 用户注册 界面信号
... ...
# 用户注册
def user_register(self):
isCapOpened_flag = self.cap.isOpened()
if not isCapOpened_flag:
QMessageBox.information(self, "提示", self.tr("请先打开摄像头!"))
else:
ret, frame = self.cap.read()
frame_location = face_recognition.face_locations(frame)
if len(frame_location) == 0:
QMessageBox.information(self, "提示", self.tr("没有检测到人脸,请重新拍摄!"))
else:
QMessageBox.information(self, "提示", self.tr("拍照成功!"))
global PHOTO_FOLDER_PATH
global SHOT_TEMP_NAME
SHOT_TEMP_NAME = datetime.now().strftime("%Y%m%d%H%M%S")
self.show_image.save(PHOTO_FOLDER_PATH + SHOT_TEMP_NAME + ".jpg")
self.send_signal_register()
# 发射信号 打开注册用户界面
def send_signal_register(self):
self.signal_register.emit()
2. Logic writing
The user clicks the [User Registration] button, and the main interface pops up the information filling interface, requiring the user to enter account number, password and other information. After clicking OK, the system judges whether the format of the input information is correct.
# 用户注册界面
class RegisterWindow(QMainWindow, RegisterMsgUi):
def __init__(self, parent=None):
super(RegisterWindow, self).__init__(parent)
self.setupUi(self)
self.minimize_button.clicked.connect(self.showMinimized)
self.close_button.clicked.connect(self.close_window)
self.cancel_button.clicked.connect(self.delete_shot)
self.confirm_button.clicked.connect(self.fill_information)
# 填写信息
def fill_information(self):
flag = 0
name = self.name_lineEdit.text()
password = self.password_lineEdit.text()
age = self.age_lineEdit.text()
sex = self.sex_lineEdit.text()
more_infor = self.more_lineEdit.text()
if self.judge_name_conflict(name):
if name != '':
# 输入密码
if password != '':
# 输入年龄
if age == '':
age = '未知'
elif not str.isdigit(age):
flag = 1
QMessageBox.about(self, '提示', '请输入正确的年龄格式')
# 输入性别
if sex == '':
sex = '未知'
elif sex != '男' and sex != '女':
flag = 1
QMessageBox.about(self, '提示', '请输入正确的性别格式')
sex = '未知'
# 输入更多信息
if more_infor == '':
more_infor = '未知'
global PHOTO_FOLDER_PATH
global SHOT_TEMP_NAME
if flag == 0:
# 计算脸部数据并保存到数据库
QApplication.processEvents()
register_encoding = self.analyse_encoding(SHOT_TEMP_NAME)
if self.save_database(name, password, age, sex, more_infor, register_encoding):
QMessageBox.about(self, '提示', '完成注册')
else:
QMessageBox.about(self, '提示', '注册失败')
self.delete_shot()
elif flag == 1:
QMessageBox.about(self, '提示', '注册失败')
else:
QMessageBox.about(self, '提示', '请输入密码')
else:
QMessageBox.about(self, '提示', '请输入姓名')
else:
QMessageBox.about(self, '提示', '用户' + name + '已经注册过')
# 保存注册信息
@staticmethod
def save_database(name, password, age, sex, more, face_encoding):
return UserSqlUtil.insert_data(name, password, age, sex, more, face_encoding)
# 判断姓名是否冲突
@staticmethod
def judge_name_conflict(name):
count = UserSqlUtil.search_count_name("\"" + name + "\"")
if count != 0:
return False
else:
return True
# 分析截图
@staticmethod
def analyse_encoding(name):
global PHOTO_FOLDER_PATH
photo_path = PHOTO_FOLDER_PATH + name + ".jpg"
register_images = face_recognition.load_image_file(photo_path)
register_encoding = face_recognition.face_encodings(register_images)[0]
return FaceEncodingUtil.encoding_FaceStr(register_encoding)
# 删除截图
def delete_shot(self):
global PHOTO_FOLDER_PATH
global SHOT_TEMP_NAME
delete_shot_path = PHOTO_FOLDER_PATH + SHOT_TEMP_NAME + ".jpg"
os.remove(delete_shot_path)
SHOT_TEMP_NAME = ""
self.close_window()
# 关闭窗口
def close_window(self):
lineText = [self.age_lineEdit, self.sex_lineEdit, self.name_lineEdit, self.more_lineEdit]
line = 0
for lineEdit in lineText:
lineEdit.setPlaceholderText(str(line))
if 3 >= line >= 0:
lineEdit.setPlaceholderText("请输入信息")
line = line + 1
self.close()
When saving in the database, the face encoding data is a matrix of 1*128, which needs to be converted by the method encoding_FaceStr of the tool class FaceEncodingUtil. Its tool class methods are as follows:
# -*- coding: utf-8 -*-
import numpy
def encoding_FaceStr(image_face_encoding):
# 将numpy array类型转化为列表
encoding__array_list = image_face_encoding.tolist()
# 将列表里的元素转化为字符串
encoding_str_list = [str(i) for i in encoding__array_list]
# 拼接列表里的字符串
encoding_str = ','.join(encoding_str_list)
return encoding_str
3. User logout
The user logout operation is relatively simple, just set the global flag USER_LOGIN_FLAG to False.
The judge code is as follows:
# 用户登出
def user_logout(self):
global USER_LOGIN_FLAG
global USER_LOGIN_NAME
if not USER_LOGIN_FLAG:
QMessageBox.about(self, '提示', '请先登录')
else:
USER_LOGIN_FLAG = False
QMessageBox.about(self, '提示', '退出成功')
self.save_record(USER_LOGIN_NAME, '退出登录') # 记录到excel表格中
4. Excel table records
Save user login and logout operation time, equipment, user name and other information into the system excel form. You can log in to the system as an administrator and export the excel form of the system for viewing. The code to export the excel form is written on the administrator side.
The code to save the information to the excel table is as follows:
# 将记录保存到excel中
@staticmethod
def save_record(name, record):
global DATA_FOLDER_PATH
local_path = DATA_FOLDER_PATH + 'history.xls'
old_book = xlrd.open_workbook(local_path)
new_book = copy(old_book)
sheet2 = new_book.get_sheet(0)
sheet0 = old_book.sheet_by_index(0)
n_rows = sheet0.nrows
str_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
sheet2.write(n_rows + 1, 0, str_time)
sheet2.write(n_rows + 1, 1, '摄像头')
sheet2.write(n_rows + 1, 2, name)
sheet2.write(n_rows + 1, 3, record)
new_book.save('new_book.xls')
os.remove(local_path)
os.rename('new_book.xls', local_path)
continue reading:
Camera screen display: face recognition system - camera screen display
Client side logic:
- Face Recognition: Python | Face Recognition System—Face Recognition
- Liveness Detection: Python | Face Recognition System - Liveness Detection
- Background Blurring: Python | Face Recognition System - Background Blurring
- Pose Detection: Python | Face Recognition System - Pose Detection
- Face comparison: Python | Face recognition system - face comparison
Admin side logic:
- Administrator Operations: Python | Face Recognition System——Administrator Operations
Note: The above code is for reference only. To run it, refer to the GitHub source code: Su-Face-Recognition