20243426 2024—2025《Python程序设计》实验四实验报告

课程:《Python程序设计》
班级: 2434
姓名:樊泽睿
学号:20243426
实验教师:王志强
实验日期:2025年6月8日
必修/选修: 公选课
一、实验内容
这是一个人脸识别系统,支持上传包含人脸的图片并保存入库后,在视频中识别该人脸。
我在bilibili上边寻求python期末大作业的灵感和教程,由于我本人水平较次,最终选择跟随一位up主的视频的教程完成了这个项目。
二、实验过程及结果
1.下载Anaconda3。
2.打开命令提示符面板。
3.配置虚拟环境。
①我们要做一个人脸识别系统,故此我们把我们想要的虚拟环境命名为为face。
——conda create -n face python == 3.7.10 ,配置一个python版本为3.7.10的虚拟环境
②激活face环境
——conda activate face42
这时候我发现我的.whl文件没放到环境在的文件夹里,我不知道不放在一起能不能成,但是我这个时候是报错了,在其他文件夹里把.whl挪过来就好了
③安装本程序所需要的文件和库。
——pip install -r requirements.txt ,用pip安装
这个时候,我的PyQt5库始终无法完成安装,报错告诉我,缺失了pyproject.toml这个声明文件。
然而,配置这个声明文件花了我一些时间,好像因为我的猪鼻操作导致声明里写的东西有些许紊乱。于是我在pycharm里按照网上的教学把它修改了一遍。
我本以为此时再次安装PyQt5库就毫无阻碍了,谁知仍然不可行。依然报错:
ERROR: Could not build wheels for PyQt5-sip, which is required to install pyproject.toml-based projects
我崩溃了。
随后又在网上求爷爷告奶奶,终于有高人指路,曰: Microsoft Visual C++ 14.0 or greater is required.
我抱着试一试的心态去安装了Microsoft Visual Studio C++生成工具,新版的。
随后再安装PyQt5库,毫无阻碍!我成功了。(老师请不要嘲笑我,我的计算机水平确实是很次的)









经历了重重困难之后,终于配置成功了!
④万事俱备后,输入:
——python UI.py以打开程序

·源代码:

from PyQt5.QtWidgets import *
import threading
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QDockWidget, QListWidget
from PyQt5.QtGui import *
import face_recognition
import cv2
import os

窗口主类

class MainWindow(QTabWidget):
# 基本配置不动,然后只动第三个界面
def init(self):
# 初始化设置
super().init()
self.setWindowTitle('人脸识别系统')
self.resize(1100, 650)
self.setWindowIcon(QIcon("UI_images/logo.png"))
# 要上传的图片路径
self.up_img_name = ""
# 要检测的图片名称
self.input_fname = ""
# 要检测的视频名称
self.source = ''
self.video_capture = cv2.VideoCapture(0)
# 初始化中止事件
self.stopEvent = threading.Event()
self.stopEvent.clear()
# 初始化人脸向量
self.known_names, self.known_encodings = self.initFaces()
# 加载lbp检测器
# 加载人脸识别模型
# 初始化界面
self.initUI()
self.set_down()

# 初始化数据库的人脸
def initFaces(self):
    # 存储知道人名列表
    known_names = []
    # 存储知道的特征值
    known_encodings = []
    # 遍历存储人脸图片的文件夹
    db_folder = "images/db_faces"
    face_imgs = os.listdir(db_folder)
    # 遍历图片,将人脸图片转化为向量
    for face_img in face_imgs:
        face_img_path = os.path.join(db_folder, face_img)
        face_name = face_img.split(".")[0]
        load_image = face_recognition.load_image_file(face_img_path)  # 加载图片
        image_face_encoding = face_recognition.face_encodings(load_image)[0]  # 获得128维特征值
        known_names.append(face_name)  # 添加到人名的列表
        known_encodings.append(image_face_encoding)  # 添加到向量的列表
    return known_names, known_encodings

# 初始化界面
def initUI(self):
    # 设置字体
    font_v = QFont('楷体', 14)
    generally_font = QFont('楷体', 15)
    # 图片检测
    img_widget = QWidget()
    img_layout = QVBoxLayout()
    img_f_title = QLabel("上传人脸图像")  # 设置标题
    img_f_title.setAlignment(Qt.AlignCenter)  # 设置标题位置为居中
    img_f_title.setFont(QFont('楷体', 18))  # 设置标题字体大小
    # todo 要上传的人脸图像
    self.img_f_img = QLabel()  # 设置第一个界面上要显示的图片
    self.img_f_img.setPixmap(QPixmap("UI_images/renlian1.jpg"))  # 初始化要显示的图片
    self.img_f_img.setAlignment(Qt.AlignCenter)  # 设置图片居中
    self.face_name = QLineEdit()  # 设置当前图片对应的人名
    img_up_btn = QPushButton("上传图片")  # 设置上传图片的按钮
    img_det_btn = QPushButton("开始上传")  # 设置开始上传的按钮
    img_up_btn.clicked.connect(self.up_img)  # 联系到相关函数
    img_det_btn.clicked.connect(self.up_db_img)  # 连接到相关函数
    # 设置组件的样式
    img_up_btn.setFont(generally_font)
    img_det_btn.setFont(generally_font)
    img_up_btn.setStyleSheet("QPushButton{color:white}"
                             "QPushButton:hover{background-color: rgb(2,110,180);}"
                             "QPushButton{background-color:rgb(48,124,208)}"
                             "QPushButton{border:2px}"
                             "QPushButton{border-radius:5px}"
                             "QPushButton{padding:5px 5px}"
                             "QPushButton{margin:5px 5px}")
    img_det_btn.setStyleSheet("QPushButton{color:white}"
                              "QPushButton:hover{background-color: rgb(2,110,180);}"
                              "QPushButton{background-color:rgb(48,124,208)}"
                              "QPushButton{border:2px}"
                              "QPushButton{border-radius:5px}"
                              "QPushButton{padding:5px 5px}"
                              "QPushButton{margin:5px 5px}")
    # 将组件添加到布局上,然后设置主要的widget为当前的布局
    img_layout.addWidget(img_f_title)
    img_layout.addWidget(self.img_f_img)
    img_layout.addWidget(self.face_name)
    img_layout.addWidget(img_up_btn)
    img_layout.addWidget(img_det_btn)
    img_widget.setLayout(img_layout)

    '''
    *** 4. 视频识别界面 ***
    '''
    video_widget = QWidget()
    video_layout = QVBoxLayout()
    # 设置视频识别区的标题
    self.video_title2 = QLabel("视频识别区")
    self.video_title2.setFont(font_v)
    self.video_title2.setAlignment(Qt.AlignCenter)
    self.video_title2.setFont(font_v)
    # 设置显示的界面
    self.DisplayLabel = QLabel()
    self.DisplayLabel.setPixmap(QPixmap(""))
    self.btn_open_rsmtp = QPushButton("检测摄像头")
    self.btn_open_rsmtp.setFont(font_v)
    # 设置打开摄像头的按钮和样式
    self.btn_open_rsmtp.setStyleSheet("QPushButton{color:white}"
                                      "QPushButton:hover{background-color: rgb(2,110,180);}"
                                      "QPushButton{background-color:rgb(48,124,208)}"
                                      "QPushButton{border:2px}"
                                      "QPushButton{border-radius:5px}"
                                      "QPushButton{padding:5px 5px}"
                                      "QPushButton{margin:5px 5px}")
    # 设置选择文件的的按钮和样式
    self.btn_open = QPushButton("开始识别(选择文件)")
    self.btn_open.setFont(font_v)
    self.btn_open.setStyleSheet("QPushButton{color:white}"
                                "QPushButton:hover{background-color: rgb(2,110,180);}"
                                "QPushButton{background-color:rgb(48,124,208)}"
                                "QPushButton{border:2px}"
                                "QPushButton{border-radius:5px}"
                                "QPushButton{padding:5px 5px}"
                                "QPushButton{margin:5px 5px}")
    # 设置结束演示的按钮和样式
    self.btn_close = QPushButton("结束检测")
    self.btn_close.setFont(font_v)
    self.btn_close.setStyleSheet("QPushButton{color:white}"
                                 "QPushButton:hover{background-color: rgb(2,110,180);}"
                                 "QPushButton{background-color:rgb(48,124,208)}"
                                 "QPushButton{border:2px}"
                                 "QPushButton{border-radius:5px}"
                                 "QPushButton{padding:5px 5px}"
                                 "QPushButton{margin:5px 5px}")
    # 将组件添加到布局上
    self.btn_open_rsmtp.clicked.connect(self.open_local)
    self.btn_open.clicked.connect(self.open)
    self.btn_close.clicked.connect(self.close)
    video_layout.setAlignment(Qt.AlignCenter)
    video_layout.addWidget(self.video_title2)
    video_layout.addWidget(self.DisplayLabel)
    self.DisplayLabel.setAlignment(Qt.AlignCenter)
    video_layout.addWidget(self.btn_open_rsmtp)
    video_layout.addWidget(self.btn_open)
    video_layout.addWidget(self.btn_close)
    video_widget.setLayout(video_layout)
    '''
    *** 5. 关于界面 ***
    '''
    about_widget = QWidget()
    about_layout = QVBoxLayout()
    about_title = QLabel('欢迎使用人脸检测系统\n\n 提供付费指导:有需要的好兄弟加下面的QQ即可')  # todo 修改欢迎词语
    about_title.setFont(QFont('楷体', 18))
    about_title.setAlignment(Qt.AlignCenter)
    about_img = QLabel()
    about_img.setPixmap(QPixmap('UI_images/qq.png'))
    about_img.setAlignment(Qt.AlignCenter)

    # label4.setText("<a href='https://oi.wiki/wiki/学习率的调整'>如何调整学习率</a>")
    label_super = QLabel()  # todo 更换作者信息
    label_super.setText("<a href='https://blog.csdn.net/ECHOSON'>或者你可以在这里找到我-->肆十二</a>")
    label_super.setFont(QFont('楷体', 16))
    label_super.setOpenExternalLinks(True)
    # label_super.setOpenExternalLinks(True)
    label_super.setAlignment(Qt.AlignRight)
    about_layout.addWidget(about_title)
    about_layout.addStretch()
    about_layout.addWidget(about_img)
    about_layout.addStretch()
    about_layout.addWidget(label_super)
    about_widget.setLayout(about_layout)
    # 分别添加子页面
    self.addTab(img_widget, "上传人脸")
    self.addTab(video_widget, '视频检测')
    self.addTab(about_widget, '关于')
    self.setTabIcon(0, QIcon('UI_images/图片.png'))
    # self.setTabIcon(1, QIcon('UI_images/图片.png'))
    self.setTabIcon(1, QIcon('UI_images/直播.png'))
    self.setTabIcon(2, QIcon('UI_images/logo_about.png'))

# 第一个界面的函数
def up_img(self):
    # 打开文件选择框
    openfile_name = QFileDialog.getOpenFileName(self, '选择文件', '', 'Image files(*.jpg , *.png)')
    # 获取上传的文件名称
    img_name = openfile_name[0]
    if img_name == '':
        pass
    else:
        # 上传之后显示并做归一化处理
        src_img = cv2.imread(img_name)
        src_img_height = src_img.shape[0]
        src_img_width = src_img.shape[1]
        target_img_height = 400
        ratio = target_img_height / src_img_height
        target_img_width = int(src_img_width * ratio)
        # 将图片统一处理到高为400的图片,方便在界面上显示
        target_img = cv2.resize(src_img, (target_img_width, target_img_height))
        cv2.imwrite("UI_images/tmp/toup.jpg", target_img)
        self.img_f_img.setPixmap(QPixmap("UI_images/tmp/toup.jpg"))
        self.up_img_name = "UI_images/tmp/toup.jpg"

def up_db_img(self):
    # 首先判断该图像是否有一个人脸,多个人脸或者没有人脸都不行
    face_name = self.face_name.text()
    if face_name == "":
        QMessageBox.information(self, "不能为空", "请填写人脸姓名")
    else:
        load_image = face_recognition.load_image_file(self.up_img_name)  # 加载图片
        image_face_encoding = face_recognition.face_encodings(load_image)  # 获得128维特征值
        encoding_length = len(image_face_encoding)  # 获取人脸得数量
        if encoding_length == 0:  # 如果没有人脸,提示用户重新上传
            QMessageBox.information(self, "请重新上传", "当前图片没有发现人脸")
        elif encoding_length > 1:  # 如果人脸有多个,也提示用户重新上传
            QMessageBox.information(self, "请重新上传", "当前图片发现多张人脸")
        else:
            face_encoding = image_face_encoding[0]  # 获取解析得到得人脸数量
            img = cv2.imread(self.up_img_name)  # 将上传得图片保存在db目录下
            img_path = face_name + '.jpg'
            cv2.imwrite("images/db_faces/" + img_path, img)
            # 上传之后重新对字典进行处理
            self.known_names.append(face_name)
            self.known_encodings.append(face_encoding)
            QMessageBox.information(self, "上传成功", "数据已上传!")

'''
### 3. 视频识别相关功能 ### 
'''

# 关闭事件 询问用户是否退出
def closeEvent(self, event):
    reply = QMessageBox.question(self,
                                 '退出',
                                 "是否要退出程序?",
                                 QMessageBox.Yes | QMessageBox.No,
                                 QMessageBox.No)
    if reply == QMessageBox.Yes:
        self.close()
        event.accept()
    else:
        event.ignore()

# 读取录像文件
def open(self):
    # 选择录像文件进行读取
    mp4_fileName, fileType = QFileDialog.getOpenFileName(self, 'Choose file', '', '*.mp4')
    if mp4_fileName:
        # 启动录像文件读取得线程并在画面上实时显示
        self.source = mp4_fileName
        self.video_capture = cv2.VideoCapture(self.source)
        th = threading.Thread(target=self.display_video)
        th.start()

def open_local(self):
    # 选择录像文件进行读取
    mp4_filename = 0
    self.source = mp4_filename
    # 读取摄像头进行实时得显示
    self.video_capture = cv2.VideoCapture(self.source)
    th = threading.Thread(target=self.display_video)
    th.start()

# 退出进程
def close(self):
    # 点击关闭按钮后重新初始化界面
    self.stopEvent.set()
    self.set_down()

# todo 执行人脸识别主进程
def display_video(self):
    # 首先把打开按钮关闭
    self.btn_open.setEnabled(False)
    self.btn_close.setEnabled(True)
    process_this_frame = True
    while True:
        ret, frame = self.video_capture.read()  # 读取摄像头
        # opencv的图像是BGR格式的,而我们需要是的RGB格式的,因此需要进行一个转换。
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)  # 将图像转化为rgb颜色通道
        # gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        # 检查人脸 按照1.1倍放到 周围最小像素为5
        # face_zone = self.face_detect.detectMultiScale(gray_frame, scaleFactor=2, minNeighbors=2)  # maxSize = (55,55)
        if process_this_frame:
            face_locations = face_recognition.face_locations(rgb_frame)  # 获得所有人脸位置
            face_encodings = face_recognition.face_encodings(rgb_frame, face_locations)  # 获得人脸特征值
            face_names = []  # 存储出现在画面中人脸的名字
            for face_encoding in face_encodings:  # 和数据库人脸进行对比
                # 如果当前人脸和数据库的人脸的相似度超过0.5,则认为人脸匹配
                matches = face_recognition.compare_faces(self.known_encodings, face_encoding, tolerance=0.5)
                if True in matches:
                    first_match_index = matches.index(True)
                    # 返回相似度最高的作为当前人脸的名称
                    name = self.known_names[first_match_index]
                else:
                    name = "unknown"
                face_names.append(name)
        process_this_frame = not process_this_frame
        # 将捕捉到的人脸显示出来
        for (top, right, bottom, left), name in zip(face_locations, face_names):
            cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)  # 画人脸矩形框
            # 加上人名标签
            cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED)
            font = cv2.FONT_HERSHEY_DUPLEX
            cv2.putText(frame, name, (left + 6, bottom - 6), font, 1.0, (255, 255, 255), 1)
        # 保存图片并进行实时的显示
        frame = frame
        frame_height = frame.shape[0]
        frame_width = frame.shape[1]
        frame_scale = 500 / frame_height
        frame_resize = cv2.resize(frame, (int(frame_width * frame_scale), int(frame_height * frame_scale)))
        cv2.imwrite("images/tmp.jpg", frame_resize)
        self.DisplayLabel.setPixmap(QPixmap("images/tmp.jpg"))
        if cv2.waitKey(25) & self.stopEvent.is_set() == True:
            self.stopEvent.clear()
            self.DisplayLabel.clear()
            self.btn_close.setEnabled(False)
            self.btn_open.setEnabled(True)
            self.set_down()
            break
    self.btn_open.setEnabled(True)
    self.btn_close.setEnabled(False)
    self.set_down()

# 初始化视频检测界面
def set_down(self):
    self.video_capture.release()
    cv2.destroyAllWindows()
    self.DisplayLabel.setPixmap(QPixmap("UI_images/ae862.jpg"))

https://www.lfd.uci.edu/~gohlke/pythonlibs/

https://pypi.org/project/dlib/#files

if name == "main":
# 加载页面
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())

显而易见,这段代码对于我而言无疑是超纲的,很多东西课程也未涉及,我就更不可能掌握了。因此,这段代码我基本一比一模仿了博主在CSND中分享的代码,并且使用了AI工具来辅助我的理解。

4.程序使用
·打开命令提示符,输入:conda activate face42
·输入:-python UI.py

我们的UI界面就出来了。
·从相册里选一张帅照,上传,并未这张照片命名备注,应当注意,照片文件名称和程序中对其的命名不能出现中文,否则会无法上传/出现乱码。

·之后点进“视频检测”。这个程序可以检测电脑前置摄像头录制的画面,也可以检测视频。这里我选择摄像头画面。

如图,它认出了我是fzr,程序可用。
保险起见,我还用我的舍友邬凯博同学做了实验,证明这个程序虽然精度不高,但是基本可以做到人脸间的区分与识别。

三、实验过程中遇到的问题和解决过程
1.PyQt5库始终无法完成安装
解决:配置pyproject.toml声明文件并且下载Microsoft Visual C++ 2022程序,最终得以顺利安装。

其他:这个实验所应用到的代码对于我而言还是相对困难的,因此我借助AI工具来帮助我理解。
尽管我在代码上几乎没有什么建树,但是本次我克服困难,独立为该程序配置一个虚拟环境这一事对我个人而言还是可喜可贺的,现在我知道这些事情该怎么操作了。

课程总结:
在本堂课中,我学习到了Python的基础知识、基本语法,能够编一点简单的python小程序,截止于此,好像和上学期的C语言课程的收获没有什么不同。
但是王老师还教会了我一点更高级的东西:比如Socket技术,这个东西原理类似打电话过程,服务器端先初始化Socket、绑定端口、监听并等待客户端连接;客户端则初始化Socket后连接服务器,双方建立连接后可发送和接收数据。在我学python以前,这是我想都不敢想的事情,我手底下竟然能产生实现这种功能的代码,太厉害了!尽管借助的是已有的库,我也不得不感叹一句python真的是一门功能很强大,实用性很强的语言。还有网络爬虫技术。这个我没学会,有点复杂,但是我接触过类似的东西,网络上有很多下载器,应当用的就是爬虫技术。现在3DM站给大家限流,下游戏模组常常很慢,而借助爬虫,速度则有了质的飞跃。
说点心里话,我是西北来的学生,从来没有接触过编程,上学期C语言学艺不精,最后挂挂乐了。这学期选择python,也是希冀触类旁通,能弥补我一点缺失的编程能力,而当踏入这门学科的厅堂之后,我才惊异于它的宏伟。不计的前人不计的努力,累作金山,供我们后来者调用、支取、化作己用。真是神奇的工具!
王老师是很好的老师,首先我听说了王老师极佳的口碑,稍后成了王老师的学生,更加印证了这一点。平常楼道里、电梯里相遇,向老师打招呼,总是笑着回应,间或寒暄两句,更使人觉亲切温暖。在师生关系不尽如中学时代那般紧密的大学中,王老师的亲切好像又能把我带回无虑的中学校园。另一点我也很佩服王老师,就是他能坚持英语打卡许多许多天,本人在学习上常有怠惰,乏少毅力,每每望人师而自愧弗如,便决心向老师看齐,坚持一下,哪怕只是比昨天变得更好一点。
也不知老师会不会看到这里,情已达至,文尽于此,王老师仍能看到我的朋友圈,而我亦能看到王老师的朋友圈,我从此是老师生活中不起眼的一角,但老师会是我大学生活中浓墨重彩的一划。祝老师万事顺意,生活安康!
此致,敬礼
20243426樊泽睿

posted @ 2025-06-10 22:54  20243426樊泽睿  阅读(47)  评论(0)    收藏  举报