Python | Face Recognition System—User Operation

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:

Admin side logic:

Note: The above code is for reference only. To run it, refer to the GitHub source code:  Su-Face-Recognition

Guess you like

Origin blog.csdn.net/sun80760/article/details/130493221