├── .idea ├── .gitignore ├── Face-Recognition-Class-Attendance-System.iml ├── aws.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── README.md ├── __pycache__ └── execute.cpython-38.pyc ├── emo ├── face_test.py ├── intel │ └── emotions-recognition-retail-0003 │ │ ├── FP16-INT8 │ │ ├── emotions-recognition-retail-0003.bin │ │ └── emotions-recognition-retail-0003.xml │ │ ├── FP16 │ │ ├── emotions-recognition-retail-0003.bin │ │ └── emotions-recognition-retail-0003.xml │ │ └── FP32 │ │ ├── emotions-recognition-retail-0003.bin │ │ └── emotions-recognition-retail-0003.xml └── weights │ ├── opencv_face_detector.pbtxt │ └── opencv_face_detector_uint8.pb ├── environment.yaml ├── execute.py ├── images ├── deep_face_recognition_process.png ├── detail_window.png ├── face_alignment.png ├── face_detection.png ├── facenet_architecture.png ├── info_window.png ├── main_window.png ├── mysql_table1.png ├── mysql_table2.png ├── mysql_table3.png └── mysql_table4.png ├── logo_imgs ├── bkg1.png ├── bkg2.png ├── brain.png ├── fcb_logo.jpg ├── fcb_logo.png ├── info.png ├── info_icon.jpg └── warn_icon.png ├── model_face_detection ├── deploy.prototxt ├── haarcascade_frontalface_default.xml ├── model_architecture_visualization.py ├── res10_300x300_ssd_iter_140000.caffemodel ├── resnet-ssd-deploy.png └── resnet-ssd-deploy2.png ├── model_facenet ├── openface_nn4.small2.v1.png └── openface_nn4.small2.v1.t7 ├── mysql_table └── facerecognition.sql ├── requirements.txt ├── saved_weights ├── embeddings.pickle ├── le.pickle └── recognizer.pickle ├── ui ├── RandomCheckUI.py ├── RandomCheckUI.ui ├── __pycache__ │ ├── RandomCheckUI.cpython-38.pyc │ ├── detail.cpython-38.pyc │ ├── infoUI.cpython-38.pyc │ ├── main.cpython-36.pyc │ ├── main.cpython-38.pyc │ └── mainwindow.cpython-38.pyc ├── detail.py ├── infoUI.py ├── infoUI.ui ├── mainwindow.py └── mainwindow.ui └── utils2 ├── Detaillog.py ├── GeneratorModel.py ├── GlobalVar.py ├── InfoDialog.py ├── RandomCheck.py ├── __pycache__ ├── Detaillog.cpython-38.pyc ├── GeneratorModel.cpython-38.pyc ├── GlobalVar.cpython-38.pyc ├── InfoDialog.cpython-38.pyc ├── InfoUI.cpython-38.pyc ├── MainUI.cpython-38.pyc └── RandomCheck.cpython-38.pyc └── microsoft.ttf /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/Face-Recognition-Class-Attendance-System.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /.idea/aws.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Student-behavior-system 2 | 3 | 学生行为分析系统 4 | 5 | --- 6 | 7 | > Contributor : Memory555 8 | > 9 | > Reference : https://github.com/datamonday/Face-Recognition-Class-Attendance-System 10 | > 11 | > Github Repo :https://github.com/Memory555/Student-behavior-system 12 | > 13 | > Project Post : 2023.06.14 14 | 15 | 16 | # 1. 项目简介及声明 17 | 18 | **🐱‍👓本项目为大三智慧学习与教育课程设计,非完全原创代码,参考借鉴Github见上方Reference链接**。 19 | 20 | 🎠本项目使用Python3.8开发。主要包括四部分: 21 | - 前端界面设计:使用Qt Designer(QT5)设计主界面,PyQt5编写界面控件的槽函数。 22 | - 人脸检测算法:使用OpenCV提供的ResNet-SSD预训练模型 23 | - 人脸识别算法:使用OpenFace开源的基于Inception的FaceNet预训练模型。 24 | - 表情识别算法:使用OpenVINO模型库中的emotions-recognition-retail-0003人脸表情模型。 25 | - 信息管理:使用PyMySQL实现学生信息,考勤信息的集中统一化管理。 26 | 27 | --- 28 | 29 | # 2. 环境配置🎡 30 | 31 | ## 2.1 Anaconda 创建虚拟环境 32 | 33 | 打开官方地址并下载对应版本: 34 | 35 | https://www.anaconda.com/products/individual#Downloads 36 | 37 | 如果追求轻量化,使用miniconda也可以,只不过没有navigator。 38 | 39 | 安装完成后,打开 Anaconda Prompt,并按以下步骤输入: 40 | 41 | 1. 创建虚拟环境 `fr` ,设置Python版本为 3.8.0 42 | 43 | ```shell 44 | conda create -n fr python==3.8 45 | ``` 46 | 47 | 2. 激活虚拟环境 48 | 49 | ```shell 50 | conda activate fr 51 | ``` 52 | 53 | --- 54 | 55 | ## 2.2 安装所需要的包 56 | 57 | 在新开的命令行,激活虚拟环境后,按如下步骤操作。 58 | 59 | 方式一:requirements.txt 安装 60 | 61 | 该文件是我在本地新建的测试通过的环境中所使用的的包,可以直接安装,避免了一个一个包安装的繁琐步骤。打开命令行并切换到该文件所在的根目录,输入如下命令: 62 | 63 | ```shell 64 | pip install -r requirements.txt 65 | ``` 66 | 注:本地环境生成requirements.txt的命令:`pip freeze > requirements.txt` 67 | 68 | --- 69 | 70 | 方式二:environment.yaml 安装 71 | 72 | 这是我在本地的conda虚拟环境,实现功能类似 requirements.txt,如果安装了anaconda的话,可以直接安装。需要注意的是该方式是直接创建一个虚拟环境,如果想在已有的环境中进行配置,则只需要按方式一即可。 73 | 74 | ```shell 75 | conda env create -f environment.yaml 76 | ``` 77 | 78 | 注:导出本地的conda环境命令:`conda env export > d:/environment.yaml` 79 | 80 | --- 81 | 82 | ## 2.3 安装 dlib 83 | - 官网:http://dlib.net/ 84 | - github:https://github.com/davisking/dlib 85 | 86 | 注意:需要先安装Visual Studio C++的Community版本或者专业版和企业版都可以,否则报错!只安装 installer 无效,仍会报错!如果之前没有安装VS内核,只安装了 installer,打开控制面板,右击修改安装,重启之后生效。 87 | 88 | 确认已经安装了VS,打开命令行,激活虚拟环境,直接命令安装即可: 89 | 90 | ```shell 91 | pip install dlib 92 | ``` 93 | 94 | 安装比较耗时,大概几分钟. 95 | 96 | 97 | --- 98 | 99 | # 3. 系统前端设计🤖 100 | 101 | 使用 Qt Designer 设计前端界面。 102 | 103 | ## 3.1 主界面 104 | 105 | ![在这里插入图片描述](./images/main_window.png) 106 | 107 | --- 108 | 109 | ## 3.2 信息采集界面 110 | 111 | ![在这里插入图片描述](./images/info_window.png) 112 | 113 | --- 114 | 115 | ## 3.3 行为分析详情界面 116 | 117 | ![在这里插入图片描述](./images/detail_window.png) 118 | 119 | --- 120 | 121 | # 4. 系统功能介绍🛵 122 | 123 | ## 4.1 信息采集功能 124 | 125 | 通过信息采集界面实现,针对学生的人脸图片建库。 126 | 127 | 可以手动添加,可以自动采集人脸图像。注意:从自动转为手动方式时,画面会不显示,但不影响采集! 128 | 129 | ## 4.2 人脸识别简介 130 | 131 | 深度学习人脸识别流程可以总结如下(图自论文:I. Masi, Y. Wu, T. Hassner and P. Natarajan, "**Deep Face Recognition: A Survey**," 2018 31st SIBGRAPI Conference on Graphics, Patterns and Images (SIBGRAPI), 2018, pp. 471-478, doi: 10.1109/SIBGRAPI.2018.00067.): 132 | 133 | ![image-20210526000357643](./images/deep_face_recognition_process.png) 134 | 135 | 总的来说应用人脸识别分为四步:(1)人脸检测,对输入的图片应用人脸检测算法(本项目中使用OpenCV提供的训练好的ResNet-SSD模型)找出人脸的坐标,过程如下图所示: 136 | 137 | ![image-20210525223208589](./images/face_detection.png) 138 | 139 | (2)应用人脸校正算法,对人脸进行校正,将图像进行保持图片相对平行的基本图像变换,例如旋转和缩放 (仿射变换, affine transformations),使得眼睛和嘴巴尽可能靠近中心,即目的是将非正视角的人脸校正到使两眼处于同一水平位置。如下图所示: 140 | 141 | ![image-20210525223023860](./images/face_alignment.png) 142 | 143 | (3)使用人脸识别算法(本文中使用Google在2015年提出的FaceNet,论文 **FaceNet: A Unified Embedding for Face Recognition and Clustering**)对纯人脸图像进行特征提取,FaceNet是将人脸编码为128维的向量,又称为嵌入(embedding),这个属于在自然语言处理领域非常常用。下图是FaceNet的论文中描述的网络架构: 144 | 145 | ![image-20210525223540033](./images/facenet_architecture.png) 146 | 147 | (4)如果是训练的话,上一步已经结束了,但是要应用人脸识别算法,还需要在下游任务上实现分类。故本项目中使用支持向量机(Support Vector Machine,SVM)实现对各人脸128维向量的分类任务。 **注意:SVM本质上为二分类器,请保证人脸数据库中至少包含两个人!否则可能导致闪退!** 148 | 注:SVM使用scikit-learn提供的[SVC API](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html)实现。 149 | 150 | - 一个使用SVM进行人脸识别的官方例程:[Faces recognition example using eigenfaces and SVMs](https://scikit-learn.org/stable/auto_examples/applications/plot_face_recognition.html#sphx-glr-auto-examples-applications-plot-face-recognition-py) 151 | 152 | --- 153 | 154 | ## 4.3 表情识别简介 155 | 156 | 表情识别使用OpenVINO模型库中的emotions-recognition-retail-0003人脸表情模型实现。该模型基于深度学习技术,使用卷积神经网络(CNN)架构进行训练,可以识别出七种不同的表情,包括生气、厌恶、恐惧、开心、平静、伤心和惊讶。 157 | 该模型使用的数据集是FER2013(Faces Emotion Recognition),包含35,887张48x48像素的灰度图像,其中28,709张用于训练,3,589张用于验证,3,589张用于测试。该数据集中的每个图像都被标记为七种不同的情感之一。 158 | emotions-recognition-retail-0003模型的输入是一张人脸图像,输出是一个大小为7的向量,每个元素代表一个表情。模型的精度在FER2013测试集上达到了66.49%的准确率,可以在不同的嵌入式设备上进行加速和部署。 159 | 该模型适用于多个领域的应用,如虚拟现实、智能医疗、安防监控、智能客服等,可以帮助人们更好地理解和识别人类情感。 160 | 161 | 此外,OpenVINO是Inter开发的一套深度学习工具库,用于快速部署应用和解决方案。 162 | 当我们应用Pytorch、Tensorflow和PaddlePaddle训练好模型之后,就面临了模型部署上线问题。OpenVINO是解决这一问题的优秀工具。 163 | 更多内容可查看官方文档介绍:[OpenVINO](https://docs.openvino.ai/cn/2022.1/documentation.html) 164 | 165 | ## 4.4 查询考勤信息 166 | 167 | 通过主界面的未到按键,可以查看当前未到学生情况,会在主界面左侧文本框中显示包含迟到和旷课的学生,点击导出按键可见结果导出。 168 | 169 | ## 4.5 查询未采集学生信息 170 | 171 | 通过主界面的未采集人员按键,实现获知尚未录入人脸信息的学生。 172 | 173 | ## 4.6 补签及请假登记 174 | 175 | 通过主界面的补签和请假按键,输入学号,实现补签和请假登记,并写入数据库。 176 | 177 | --- 178 | 179 | # 5. 数据库存取信息🥗 180 | 181 | ## 5.1 数据库可视化工具 Navicat 182 | 183 | 使用该软件是为了方便管理维护信息,也可使用其他数据管理软件。 184 | 185 | --- 186 | 187 | ## 5.3 MySQL表格及字段说明 188 | 189 | 其主界面如下: 190 | 191 | 学生信息表: 192 | ![在这里插入图片描述](./images/mysql_table1.png) 193 | 194 | 学生班级表: 195 | ![在这里插入图片描述](./images/mysql_table2.png) 196 | 197 | 考勤表: 198 | ![在这里插入图片描述](./images/mysql_table3.png) 199 | 200 | 情绪分析详情表: 201 | ![在这里插入图片描述](./images/mysql_table4.png) 202 | 203 | 204 | 205 | 206 | --- 207 | 208 | ## 5.4 PyMySQL 使用 209 | 210 | > Github:https://github.com/PyMySQL 211 | 212 | 项目中只使用了简单的写入、查询等几个常用命令,即使没有数据库基础上手也比较容易。例如: 213 | 214 | ```python 215 | # 连接数据库 216 | db = pymysql.connect(host="localhost", user="root", password="mysql105", database="facerecognition") 217 | 218 | # 查询语句,实现通过ID关键字检索个人信息的功能 219 | sql = "SELECT * FROM STUDENTS WHERE ID = {}".format(self.input_ID) 220 | ``` 221 | 222 | --- 223 | 224 | # 6. 源码介绍及使用🍨 225 | 226 | --- 227 | ## 6.2 关键文件说明 228 | 229 | - `model_face_detection/res10_300x300_ssd_iter_140000.caffemodel`:OpenCV提供的训练好的人脸检测ResNet-SSD模型权重。 230 | - `model_facenet/openface_nn4.small2.v1.t7`:OpenFace提供的基于Inception的训练好的FaceNet模型权重。 231 | - `emo/intel/emotions-recognition-retail-0003/FP32:` 232 | - `mysql_table/facerecognition.sql`:mysql表格。 233 | 234 | --- 235 | 236 | ## 6.3 需要修改源码部分 237 | 238 | 1. 安装 **msqlservice** 然后修改 `execute.py`文件中的数据库连接代码。比如 `db = pymysql.connect("localhost", "root", "mysql105", "facerecognition")`。这首先需要在 navicat中创建数据库。 239 | 2. 如果不是通过本系统的信息采集功能采集的人脸照片,请将采集的人脸照片放到 `face_dataset/XX` 路径下,其中`XX`是学号(唯一索引),如果是通过系统采集的,则会自动存放在该路径下,不需要修改。 240 | 3. **注意:考虑到班级成员信息存储在数据库中,因此本地的信息采集界面不支持人脸的增删,只支持修改。所以当在本地采集了很多人脸并训练完模型之后,一定要确保采集的人脸ID已经存在于数据库之中,否则可能导致闪退!** 241 | 242 | --- 243 | 244 | ## 6.4 使用步骤 245 | 246 | 1. navicat创建数据库,打开数据库录入学生信息和班级信息; 247 | 2. 修改源码中的数据库连接部分,连接到创建的数据库; 248 | 3. 修改文件`GlobalVar.py` 中的路径; 249 | 4. 采集人脸照片,点击界面中的信息采集,在子窗口操作即可,注意保证本地文件夹的名称和数据库中的ID对应,每个ID的人脸在100--300张较好; 250 | 5. 回到主界面,点击界面中的模型训练,以训练人脸识别模型; 251 | 6. 开始考勤和行为分析:打开相机 --> 开始分析; 252 | 7. 查看每个学生表情情况:分析详情; 253 | 254 | --- 255 | -------------------------------------------------------------------------------- /__pycache__/execute.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/__pycache__/execute.cpython-38.pyc -------------------------------------------------------------------------------- /emo/face_test.py: -------------------------------------------------------------------------------- 1 | import cv2 as cv 2 | import numpy as np 3 | from openvino.inference_engine import IECore 4 | 5 | # 人脸检测模型 6 | weight_pb = "E:/soft/smart/Face-Recognition-Class-Attendance-System-master/emo/weights/opencv_face_detector_uint8.pb" 7 | config_text = "E:/soft/smart/Face-Recognition-Class-Attendance-System-master/emo/weights/opencv_face_detector.pbtxt" 8 | 9 | # 加载表情识别模型并设置输入与输出 10 | model_xml = "E:/soft/smart/Face-Recognition-Class-Attendance-System-master/emo/intel/emotions-recognition-retail-0003/FP32/emotions-recognition-retail-0003.xml" 11 | model_bin = "E:/soft/smart/Face-Recognition-Class-Attendance-System-master/emo/intel/emotions-recognition-retail-0003/FP32/emotions-recognition-retail-0003.bin" 12 | 13 | labels = ['neutral', 'happy', 'sad', 'surprise', 'anger'] 14 | 15 | ie = IECore() 16 | emotion_net = ie.read_network(model=model_xml, weights=model_bin) 17 | 18 | input_blob = next(iter(emotion_net.input_info)) 19 | 20 | exec_net = ie.load_network(network=emotion_net, device_name="CPU", num_requests=2) 21 | 22 | # 读取人脸检测模型 23 | net = cv.dnn.readNetFromTensorflow(weight_pb, config=config_text) 24 | 25 | # 人脸检测&表情识别 26 | def emotion_detect(frame): 27 | h, w, c = frame.shape 28 | blobImage = cv.dnn.blobFromImage(frame, 1.0, (300, 300), (104.0, 177.0, 123.0), False, False) 29 | net.setInput(blobImage) 30 | cvOut = net.forward() 31 | 32 | # 绘制检测矩形 33 | for detection in cvOut[0, 0, :, :]: 34 | score = float(detection[2]) 35 | if score > 0.5: 36 | left = detection[3] * w 37 | top = detection[4] * h 38 | right = detection[5] * w 39 | bottom = detection[6] * h 40 | 41 | # roi 和 关键点检测 42 | y1 = np.int32(top) if np.int32(top) > 0 else 0 43 | y2 = np.int32(bottom) if np.int32(bottom) < h else h - 1 44 | x1 = np.int32(left) if np.int32(left) > 0 else 0 45 | x2 = np.int32(right) if np.int32(right) < w else w - 1 46 | roi = frame[y1:y2, x1:x2, :] 47 | image = cv.resize(roi, (64, 64)) 48 | image = image.transpose((2, 0, 1)) # Change data layout from HWC to CHW 49 | res = exec_net.infer(inputs={input_blob: [image]}) 50 | prob_emotion = res['prob_emotion'] 51 | probs = np.reshape(prob_emotion, 5) 52 | txt = labels[np.argmax(probs)] 53 | cv.putText(frame, txt, (np.int32(left), np.int32(top)), cv.FONT_HERSHEY_SIMPLEX, 1.0, (255, 0, 0), 2) 54 | cv.rectangle(frame, (np.int32(left), np.int32(top)), 55 | (np.int32(right), np.int32(bottom)), (0, 0, 255), 2, 8, 0) 56 | print(txt) 57 | 58 | 59 | if __name__ == "__main__": 60 | 61 | # 调用摄像头 62 | capture = cv.VideoCapture(0) 63 | # 读取视频 64 | # path = "./dataset/video/smile.mp4" 65 | # capture = cv.VideoCapture(path) 66 | 67 | while True: 68 | ret, frame = capture.read() 69 | if ret is not True: 70 | break 71 | emotion_detect(frame) 72 | cv.imshow("emotion-detect-demo", frame) 73 | # 按Q 退出 waitKey控制播放速度 74 | if cv.waitKey(1) & 0xFF == ord('q'): 75 | break 76 | -------------------------------------------------------------------------------- /emo/intel/emotions-recognition-retail-0003/FP16-INT8/emotions-recognition-retail-0003.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/emo/intel/emotions-recognition-retail-0003/FP16-INT8/emotions-recognition-retail-0003.bin -------------------------------------------------------------------------------- /emo/intel/emotions-recognition-retail-0003/FP16/emotions-recognition-retail-0003.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/emo/intel/emotions-recognition-retail-0003/FP16/emotions-recognition-retail-0003.bin -------------------------------------------------------------------------------- /emo/intel/emotions-recognition-retail-0003/FP32/emotions-recognition-retail-0003.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/emo/intel/emotions-recognition-retail-0003/FP32/emotions-recognition-retail-0003.bin -------------------------------------------------------------------------------- /emo/weights/opencv_face_detector_uint8.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/emo/weights/opencv_face_detector_uint8.pb -------------------------------------------------------------------------------- /environment.yaml: -------------------------------------------------------------------------------- 1 | name: fr 2 | channels: 3 | - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2 4 | - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main 5 | - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ 6 | - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ 7 | - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/ 8 | - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge 9 | - defaults 10 | dependencies: 11 | - argon2-cffi=20.1.0=py38h2bbff1b_1 12 | - async_generator=1.10=pyhd3eb1b0_0 13 | - attrs=21.2.0=pyhd3eb1b0_0 14 | - backcall=0.2.0=pyhd3eb1b0_0 15 | - blas=1.0=mkl 16 | - bleach=3.3.0=pyhd3eb1b0_0 17 | - ca-certificates=2021.4.13=haa95532_1 18 | - certifi=2020.12.5=py38haa95532_0 19 | - cffi=1.14.5=py38hcd4344a_0 20 | - colorama=0.4.4=pyhd3eb1b0_0 21 | - cycler=0.10.0=py38_0 22 | - decorator=5.0.9=pyhd3eb1b0_0 23 | - defusedxml=0.7.1=pyhd3eb1b0_0 24 | - entrypoints=0.3=py38_0 25 | - freetype=2.10.4=hd328e21_0 26 | - icc_rt=2019.0.0=h0cc432a_1 27 | - icu=58.2=ha925a31_3 28 | - importlib-metadata=3.10.0=py38haa95532_0 29 | - importlib_metadata=3.10.0=hd3eb1b0_0 30 | - intel-openmp=2021.2.0=haa95532_616 31 | - ipykernel=5.3.4=py38h5ca1d4c_0 32 | - ipython=7.23.1=py38h43734a8_0 33 | - ipython_genutils=0.2.0=pyhd3eb1b0_1 34 | - ipywidgets=7.6.3=pyhd3eb1b0_1 35 | - jedi=0.18.0=py38haa95532_1 36 | - jinja2=3.0.0=pyhd3eb1b0_0 37 | - joblib=1.0.1=pyhd3eb1b0_0 38 | - jpeg=9b=hb83a4c4_2 39 | - jsonschema=3.2.0=py_2 40 | - jupyter=1.0.0=py38_7 41 | - jupyter_client=6.1.12=pyhd3eb1b0_0 42 | - jupyter_console=6.4.0=pyhd3eb1b0_0 43 | - jupyter_core=4.7.1=py38haa95532_0 44 | - jupyterlab_pygments=0.1.2=py_0 45 | - jupyterlab_widgets=1.0.0=pyhd3eb1b0_1 46 | - kiwisolver=1.3.1=py38hd77b12b_0 47 | - libpng=1.6.37=h2a8f88b_0 48 | - libsodium=1.0.18=h62dcd97_0 49 | - libtiff=4.2.0=hd0e1b90_0 50 | - lz4-c=1.9.3=h2bbff1b_0 51 | - m2w64-gcc-libgfortran=5.3.0=6 52 | - m2w64-gcc-libs=5.3.0=7 53 | - m2w64-gcc-libs-core=5.3.0=7 54 | - m2w64-gmp=6.1.0=2 55 | - m2w64-libwinpthread-git=5.0.0.4634.697f757=2 56 | - markupsafe=2.0.1=py38h2bbff1b_0 57 | - matplotlib=3.3.4=py38haa95532_0 58 | - matplotlib-base=3.3.4=py38h49ac443_0 59 | - matplotlib-inline=0.1.2=pyhd8ed1ab_2 60 | - mistune=0.8.4=py38he774522_1000 61 | - mkl=2021.2.0=haa95532_296 62 | - mkl-service=2.3.0=py38h2bbff1b_1 63 | - mkl_fft=1.3.0=py38h277e83a_2 64 | - mkl_random=1.2.1=py38hf11a4ad_2 65 | - msys2-conda-epoch=20160418=1 66 | - nbclient=0.5.3=pyhd3eb1b0_0 67 | - nbconvert=6.0.7=py38_0 68 | - nbformat=5.1.3=pyhd3eb1b0_0 69 | - nest-asyncio=1.5.1=pyhd3eb1b0_0 70 | - notebook=6.4.0=py38haa95532_0 71 | - numpy=1.20.2=py38ha4e8547_0 72 | - numpy-base=1.20.2=py38hc2deb75_0 73 | - olefile=0.46=py_0 74 | - openssl=1.1.1k=h2bbff1b_0 75 | - packaging=20.9=pyhd3eb1b0_0 76 | - pandas=1.2.4=py38hd77b12b_0 77 | - pandoc=2.12=haa95532_0 78 | - pandocfilters=1.4.3=py38haa95532_1 79 | - parso=0.8.2=pyhd3eb1b0_0 80 | - pickleshare=0.7.5=pyhd3eb1b0_1003 81 | - pillow=8.2.0=py38h4fa10fc_0 82 | - pip=21.0.1=py38haa95532_0 83 | - prometheus_client=0.10.1=pyhd3eb1b0_0 84 | - prompt-toolkit=3.0.17=pyh06a4308_0 85 | - prompt_toolkit=3.0.17=hd3eb1b0_0 86 | - pycparser=2.20=py_2 87 | - pygments=2.9.0=pyhd3eb1b0_0 88 | - pyparsing=2.4.7=pyhd3eb1b0_0 89 | - pyqt=5.9.2=py38ha925a31_4 90 | - pyrsistent=0.17.3=py38he774522_0 91 | - python=3.8.0=hff0d562_2 92 | - python-dateutil=2.8.1=pyhd3eb1b0_0 93 | - python_abi=3.8=1_cp38 94 | - pytz=2021.1=pyhd3eb1b0_0 95 | - pywin32=227=py38he774522_1 96 | - pywinpty=0.5.7=py38_0 97 | - pyzmq=20.0.0=py38hd77b12b_1 98 | - qt=5.9.7=vc14h73c81de_0 99 | - qtconsole=5.0.3=pyhd3eb1b0_0 100 | - qtpy=1.9.0=py_0 101 | - scikit-learn=0.24.2=py38hf11a4ad_0 102 | - scipy=1.6.2=py38h66253e8_1 103 | - send2trash=1.5.0=pyhd3eb1b0_1 104 | - setuptools=52.0.0=py38haa95532_0 105 | - sip=4.19.13=py38ha925a31_0 106 | - six=1.15.0=py38haa95532_0 107 | - sqlite=3.35.4=h2bbff1b_0 108 | - terminado=0.9.4=py38haa95532_0 109 | - testpath=0.4.4=pyhd3eb1b0_0 110 | - threadpoolctl=2.1.0=pyh5ca1d4c_0 111 | - tk=8.6.10=he774522_0 112 | - tornado=6.1=py38h2bbff1b_0 113 | - traitlets=5.0.5=pyhd3eb1b0_0 114 | - vc=14.2=h21ff451_1 115 | - vs2015_runtime=14.27.29016=h5e58377_2 116 | - wcwidth=0.2.5=py_0 117 | - webencodings=0.5.1=py38_1 118 | - wheel=0.36.2=pyhd3eb1b0_0 119 | - widgetsnbextension=3.5.1=py38_0 120 | - wincertstore=0.2=py38_0 121 | - winpty=0.4.3=4 122 | - xz=5.2.5=h62dcd97_0 123 | - zeromq=4.3.3=ha925a31_3 124 | - zipp=3.4.1=pyhd3eb1b0_0 125 | - zlib=1.2.11=h62dcd97_4 126 | - zstd=1.4.5=h04227a9_0 127 | - pip: 128 | - blurhash==1.1.4 129 | - boost==0.1 130 | - chardet==4.0.0 131 | - cmake==3.20.2 132 | - dlib==19.22.0 133 | - et-xmlfile==1.1.0 134 | - freetype-py==2.2.0 135 | - greenlet==1.1.0 136 | - idna==2.10 137 | - imutils==0.5.4 138 | - mastodon-py==1.5.1 139 | - opencv-contrib-python==4.5.2.52 140 | - opencv-python==4.5.2.52 141 | - openpyxl==3.0.7 142 | - pymysql==1.0.2 143 | - pyqt5==5.15.4 144 | - pyqt5-qt5==5.15.2 145 | - pyqt5-sip==12.9.0 146 | - python-magic==0.4.22 147 | - requests==2.25.1 148 | - sqlalchemy==1.4.15 149 | - urllib3==1.26.4 150 | - xlrd==2.0.1 151 | prefix: D:\anaconda\envs\fr 152 | -------------------------------------------------------------------------------- /execute.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pickle 3 | import sys 4 | import threading 5 | import time 6 | from datetime import datetime 7 | from copy import deepcopy 8 | import cv2 9 | import imutils 10 | import numpy as np 11 | import openpyxl 12 | # 导入数据库操作包 13 | import pymysql 14 | # 导入界面处理包 15 | from PyQt5 import QtCore, QtGui, QtWidgets 16 | from PyQt5.QtCore import QTimer, QDateTime, QCoreApplication, QThread 17 | from PyQt5.QtGui import QImage, QIcon, QPixmap 18 | from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QInputDialog 19 | 20 | # 使用mainwindow类重构 21 | from ui import mainwindow as MainWindowUI 22 | 23 | # 导入人脸识别检测包 24 | from utils2 import GeneratorModel 25 | # 导入信息采集槽函数类 26 | from utils2.InfoDialog import InfoDialog 27 | 28 | # 导入随机点名类 29 | from utils2.RandomCheck import RCDialog 30 | # 添加数据库连接操作 31 | from utils2.GlobalVar import connect_to_sql 32 | # 导入分析详情槽函数类 33 | from utils2.Detaillog import Detaillog 34 | 35 | from utils2.GlobalVar import FR_LOOP_NUM, statical_facedata_nums 36 | 37 | import sys 38 | import os 39 | from collections import Counter 40 | 41 | # 添加当前路径到环境变量 42 | sys.path.append(os.getcwd()) 43 | 44 | from utils2.GlobalVar import num_list 45 | 46 | import numpy as np 47 | from openvino.inference_engine import IECore 48 | 49 | # 全局变量[情绪] 50 | emo_list = [] 51 | # 全局变量[起始时间] 52 | start_time = [] 53 | # 全局变量[姓名] 54 | student_name_list = [] 55 | 56 | 57 | class MainWindow(QtWidgets.QMainWindow): 58 | # 类构造函数 59 | def __init__(self): 60 | # super()构造器方法返回父级的对象。__init__()方法是构造器的一个方法。 61 | super().__init__() 62 | # self.ui = MainUI.Ui_Form() 63 | self.line_text_info3 = [] 64 | self.ui = MainWindowUI.Ui_MainWindow() 65 | self.ui.setupUi(self) 66 | 67 | # ####################### 相对路径 ###################### 68 | # 初始化label显示的(黑色)背景 69 | self.bkg_pixmap = QPixmap('./logo_imgs/bkg1.png') 70 | # 设置主窗口的logo 71 | self.logo = QIcon('./logo_imgs/fcb_logo.png') 72 | # 设置提示框icon 73 | self.info_icon = QIcon('./logo_imgs/info_icon.jpg') 74 | # OpenCV深度学习人脸检测器的路径 75 | self.detector_path = "./model_face_detection" 76 | # 情绪识别模型的路径 77 | self.temper_path = "./emo/intel/emotions-recognition-retail-0003/FP32" 78 | # OpenCV深度学习面部嵌入模型的路径 79 | self.embedding_model = "./model_facenet/openface_nn4.small2.v1.t7" 80 | # 训练模型以识别面部的路径 81 | self.recognizer_path = "./saved_weights/recognizer.pickle" 82 | # 标签编码器的路径 83 | self.le_path = "./saved_weights/le.pickle" 84 | 85 | # ###################### 窗口初始化 ###################### 86 | # 设置窗口名称和图标 87 | self.setWindowTitle('学生行为分析系统 v1.0') 88 | self.setWindowIcon(self.logo) 89 | # 设置单张图片背景 90 | self.ui.label_camera.setPixmap(self.bkg_pixmap) 91 | # label_time显示系统时间 92 | self.timer = QTimer(self) 93 | self.timer.timeout.connect(self.show_time_text) 94 | # 启动时间任务 95 | self.timer.start() 96 | 97 | # ###################### 摄像头初始化 ###################### 98 | # 初始化摄像头,默认调用第一个摄像头 99 | # 如果要调用摄像头1,则设置为1,适用于:笔记本外接USB摄像头 100 | self.url = 0 101 | self.cap = cv2.VideoCapture() 102 | 103 | # ###################### 按键的槽函数 ###################### 104 | # 设置摄像头按键连接函数 105 | self.ui.bt_open_camera.clicked.connect(self.open_camera) 106 | # 设置开始分析按键的回调函数 107 | self.ui.bt_start_check.clicked.connect(self.auto_control) 108 | # 设置“退出系统”按键事件, 按下之后退出主界面 109 | self.ui.bt_exit.clicked.connect(self.quit_window) 110 | # 设置信息采集按键连接 111 | self.ui.bt_gathering.clicked.connect(self.open_info_dialog) 112 | 113 | # 设置区分打开摄像头还是人脸识别的标识符 114 | self.switch_bt = 0 115 | 116 | # ###################### 数据库相关操作 ###################### 117 | # 初始化需要记录的人名 118 | self.record_name = [] 119 | # 设置更新人脸数据库的按键连接函数 120 | self.ui.bt_generator.clicked.connect(self.train_model) 121 | # 设置查询班级人数按键的连接函数 122 | # self.ui.bt_check.clicked.connect(self.check_nums) 123 | # 设置请假按键的连接函数 124 | self.ui.bt_leave.clicked.connect(self.leave_button) 125 | # 设置漏签补签按键的连接函数 126 | self.ui.bt_supplement.clicked.connect(self.supplyment_button) 127 | # 设置对输入内容的删除提示 128 | self.ui.lineEdit_leave.setClearButtonEnabled(True) 129 | self.ui.lineEdit_supplement.setClearButtonEnabled(True) 130 | # # 设置查看结果(显示未到和迟到)按键的连接函数 131 | self.ui.bt_listName1.clicked.connect(self.show_late_absence) 132 | # 导出函数 133 | self.ui.bt_output.clicked.connect(self.import_late_absence) 134 | # 核验本地人脸数据集与数据库中的ID是否一致,即验证是否有未录入数据库的情况,以及是否有未采集人脸的情况。 135 | self.ui.bt_check_variation.clicked.connect(self.check_variation_db) 136 | # self.check_time_set = '08:00:00' 137 | 138 | 139 | # 显示系统时间以及相关文字提示函数 140 | def show_time_text(self): 141 | # 设置宽度 142 | self.ui.label_time.setFixedWidth(200) 143 | # 设置显示文本格式 144 | self.ui.label_time.setStyleSheet( 145 | # "QLabel{background:white;}" 此处设置背景色 146 | "QLabel{color:rgb(0, 0, 0); font-size:14px; font-weight:bold; font-family:宋体;}" 147 | "QLabel{font-size:14px; font-weight:bold; font-family:宋体;}") 148 | 149 | current_datetime = QDateTime.currentDateTime().toString() 150 | self.ui.label_time.setText("" + current_datetime) 151 | 152 | # 显示“人脸识别分析系统”文字 153 | self.ui.label_title.setFixedWidth(400) 154 | self.ui.label_title.setStyleSheet("QLabel{font-size:26px; font-weight:bold; font-family:宋体;}") 155 | self.ui.label_title.setText("学生行为分析系统") 156 | 157 | def open_camera(self): 158 | # 判断摄像头是否打开,如果打开则为true,反之为false 159 | if not self.cap.isOpened(): 160 | self.ui.label_logo.clear() 161 | # 默认打开Windows系统笔记本自带的摄像头,如果是外接USB,可将0改成1 162 | self.cap.open(self.url) 163 | self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 800) 164 | # self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 600) 165 | self.show_camera() 166 | else: 167 | self.cap.release() 168 | self.ui.label_logo.clear() 169 | self.ui.label_camera.clear() 170 | self.ui.bt_open_camera.setText(u'打开相机') 171 | 172 | 173 | # 进入情绪分析模式,通过switch_bt进行控制的函数 174 | def auto_control(self): 175 | if self.cap.isOpened(): 176 | if self.switch_bt == 0: 177 | self.switch_bt = 1 178 | self.ui.bt_start_check.setText(u'退出分析') 179 | self.show_camera() 180 | elif self.switch_bt == 1: 181 | self.switch_bt = 0 182 | self.ui.bt_start_check.setText(u'开始分析') 183 | self.show_camera() 184 | else: 185 | print("[Error] The value of self.switch_bt must be zero or one!") 186 | else: 187 | QMessageBox.information(self, "Tips", "请先打开摄像头!", QMessageBox.Ok) 188 | 189 | def show_camera(self): 190 | # 如果按键按下 191 | global embedded, le, recognizer 192 | if self.switch_bt == 0: 193 | self.ui.label_logo.clear() 194 | self.ui.bt_open_camera.setText(u'关闭相机') 195 | while self.cap.isOpened(): 196 | # 以BGR格式读取图像 197 | ret, self.image = self.cap.read() 198 | # 告诉QT处理来处理任何没有被处理的事件,并且将控制权返回给调用者,让代码变的没有那么卡 199 | QApplication.processEvents() 200 | # 将图像转换为RGB格式 201 | show = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB) # 这里指的是显示原图 202 | # opencv 读取图片的样式,不能通过Qlabel进行显示,需要转换为Qimage QImage(uchar * data, int width, 203 | self.showImage = QImage(show.data, show.shape[1], show.shape[0], QImage.Format_RGB888) 204 | self.ui.label_camera.setPixmap(QPixmap.fromImage(self.showImage)) 205 | # 因为最后会存留一张图像在lable上,需要对lable进行清理 206 | self.ui.label_camera.clear() 207 | self.ui.bt_open_camera.setText(u'打开相机') 208 | # 设置单张图片背景 209 | self.ui.label_camera.setPixmap(self.bkg_pixmap) 210 | 211 | elif self.switch_bt == 1: 212 | self.ui.label_logo.clear() 213 | self.ui.bt_start_check.setText(u'退出分析') 214 | 215 | # 人脸检测的置信度 216 | confidence_default = 0.5 217 | # 从磁盘加载序列化面部检测器 218 | proto_path = os.path.sep.join([self.detector_path, "deploy.prototxt"]) 219 | model_path = os.path.sep.join([self.detector_path, "res10_300x300_ssd_iter_140000.caffemodel"]) 220 | detector = cv2.dnn.readNetFromCaffe(proto_path, model_path) 221 | # 从磁盘加载序列化面嵌入模型 222 | try: 223 | self.ui.textBrowser_log.append("[INFO] 正在加载人脸识别模型") 224 | self.ui.textBrowser_log.append("[INFO] 正在加载情绪识别模型") 225 | # 加载FaceNet人脸识别模型 226 | embedded = cv2.dnn.readNetFromTorch(self.embedding_model) 227 | except FileNotFoundError as e: 228 | self.ui.textBrowser_log.append("面部嵌入模型的路径不正确!", e) 229 | 230 | # 加载实际的人脸识别模型和标签 231 | try: 232 | recognizer = pickle.loads(open(self.recognizer_path, "rb").read()) 233 | le = pickle.loads(open(self.le_path, "rb").read()) 234 | except FileNotFoundError as e: 235 | self.ui.textBrowser_log.append("人脸识别模型保存路径不正确!", e) 236 | 237 | # 构造人脸id的字典,以便存储检测到每个id的人脸次数,键为人名(ID),值初始化为0,方便统计次数 238 | self.face_name_dict = dict(zip(le.classes_, len(le.classes_) * [0])) 239 | # 初始化循环次数,比如统计10帧中人脸的数量,取最大值进行考勤 240 | loop_num = 0 241 | # 循环来自视频文件流的帧 242 | while self.cap.isOpened(): 243 | loop_num += 1 244 | # 从线程视频流中抓取帧 245 | ret, frame = self.cap.read() 246 | QApplication.processEvents() 247 | if ret: 248 | # 调整框架的大小以使其宽度为900像素(同时保持纵横比),然后抓取图像尺寸 249 | frame = imutils.resize(frame, width=900) 250 | (h, w) = frame.shape[:2] 251 | # 从图像构造一个blob, 缩放为 300 x 300 x 3 像素的图像,为了符合ResNet-SSD的输入尺寸 252 | # OpenCV Blog的使用可参考:https://www.pyimagesearch.com/2017/11/06/deep-learning-opencvs-blobfromimage-works/ 253 | # 如果与情绪识别统一尺寸64x64,识别框对于小物体很难找到 254 | image_blob = cv2.dnn.blobFromImage( 255 | cv2.resize(frame, (300, 300)), 1.0, (300, 300), 256 | (104.0, 177.0, 123.0), swapRB=False, crop=False) 257 | # 应用OpenCV的基于深度学习的人脸检测器来定位输入图像中的人脸 258 | detector.setInput(image_blob) 259 | # 传入到ResNet-SSD以检测人脸 260 | detections = detector.forward() 261 | 262 | # 初始化一个列表,用于保存识别到的人脸学号 263 | face_names = [] 264 | # 初始化一个列表,用于保存识别到的情绪 265 | emo_txt = [] 266 | 267 | # 在检测结果中循环检测 268 | # 注意:这里detection为ResNet-SSD网络的输出,与阈值的设置有关,具体可以参考prototxt文件的输出层,输出shape为[1, 1, 200, 7] 269 | # 7 表示的含义分别为 [batch Id, class Id, confidence, left, top, right, bottom] 270 | # 200 表示检测到的目标数量,具体可参考SSD的论文,针对每幅图像,SSD最终会预测8000多个边界框,通过NMS过滤掉IOU小于0.45的框,剩余200个。 271 | for i in np.arange(0, detections.shape[2]): 272 | # 提取与预测相关的置信度(即概率),detections的第3维 273 | confidence = detections[0, 0, i, 2] 274 | 275 | # 用于更新相机开关按键信息 276 | if not self.cap.isOpened(): 277 | self.ui.bt_open_camera.setText(u'打开相机') 278 | else: 279 | self.ui.bt_open_camera.setText(u'关闭相机') 280 | 281 | 282 | # 过滤弱检测 283 | if confidence > confidence_default: 284 | # 计算面部边界框的(x,y)坐标, 对应detections的4,5,6,7维(索引为3-7),含义分别代表: 285 | # x_left_bottom, y_left_bottom, x_right_top, y_right_top 286 | box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) 287 | (startX, startY, endX, endY) = box.astype("int") 288 | 289 | roi = frame[startY:endY, startX:endX, :] 290 | image = cv2.resize(roi, (64, 64)) 291 | image = image.transpose((2, 0, 1)) # Change data layout from HWC to CHW 翻转 292 | 293 | # 加载FaceNet人脸识别模型 294 | model_xml = self.temper_path + "/emotions-recognition-retail-0003.xml" 295 | model_bin = self.temper_path + "/emotions-recognition-retail-0003.bin" 296 | 297 | labels = ['neutral', 'happy', 'sad', 'surprise', 'anger'] 298 | 299 | ie = IECore() 300 | emotion_net = ie.read_network(model=model_xml, weights=model_bin) 301 | 302 | input_blob = next(iter(emotion_net.input_info)) 303 | 304 | exec_net = ie.load_network(network=emotion_net, device_name="CPU", num_requests=2) 305 | 306 | res = exec_net.infer(inputs={input_blob: [image]}) 307 | prob_emotion = res['prob_emotion'] 308 | probs = np.reshape(prob_emotion, 5) 309 | txt = labels[np.argmax(probs)] # 取概率最大的情绪 310 | 311 | # 提取面部ROI 312 | # 提取人脸的长和宽,本例中为 397 x 289 313 | face = frame[startY:endY, startX:endX] 314 | (fH, fW) = face.shape[:2] 315 | 316 | # 确保面部宽度和高度足够大,以过滤掉小人脸(较远),防止远处人员签到,以及过滤误检测 317 | if fW < 100 or fH < 100: 318 | continue 319 | 320 | # 为面部ROI构造一个blob,然后通过面部嵌入模型传递blob以获得面部的128-d量化 321 | # shape 为 (1, 3, 96, 96) 322 | face_blob = cv2.dnn.blobFromImage(face, 1.0 / 255, 323 | (96, 96), # 调整到 96 x 96 像素 324 | (0, 0, 0), swapRB=True, crop=False) 325 | # 传入到 FaceNet人脸识别模型中,将 96 x 96 x 3 的人脸图像转换为128维度的向量 326 | embedded.setInput(face_blob) 327 | # 128 维的向量,shape=(1, 128) 328 | vec = embedded.forward() 329 | # 使用SVM对人脸向量进行分类 330 | # prediction 为一向量,其shape的第一个维度为人脸库中ID的数量,返回分类概率的列表 331 | prediction = recognizer.predict_proba(vec)[0] 332 | # 取概率最大的索引 333 | j = np.argmax(prediction) 334 | # 得到预测概率 335 | probability = prediction[j] 336 | # 通过索引j找到人名(ID)转化为one-hot编码前的真实名称,也就是人脸数据集的文件夹名称,亦即数据库中ID字段的值 337 | name = le.classes_[j] 338 | 339 | # 统计各人脸被检测到的次数 340 | self.face_name_dict[name] += 1 341 | 342 | # 绘制面部的边界框以及相关的概率 343 | text = "{}: {:.2f}%".format(name, probability * 100) 344 | # 构造人脸边界框 345 | y = startY - 10 if startY - 10 > 10 else startY + 10 346 | cv2.rectangle(frame, (startX, startY), (endX, endY), (0, 0, 255), 2) 347 | frame = cv2.putText(frame, text, (startX, y), cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 255), 348 | 2) 349 | face_names.append(name) 350 | emo_txt.append(txt) 351 | 352 | results_name = '' 353 | 354 | # 执行查询 355 | try: 356 | # 打开数据库连接 357 | db, cursor = connect_to_sql() 358 | except ConnectionError as e: 359 | print("[Error] 数据库连接失败!") 360 | try: 361 | sql = "select name from students where ID = {}".format(name) # 查找姓名 362 | cursor.execute(sql) 363 | results = cursor.fetchall() 364 | finally: 365 | # 提交到数据库执行 366 | db.commit() 367 | cursor.close() 368 | db.close() 369 | 370 | for item in results: 371 | # self.student_ids.append(item[0]) 372 | results_name = item[0] 373 | 374 | 375 | # 放入全局列表中 376 | if len(num_list) == 0: 377 | num_list.append(name) 378 | emo_list.append(txt) 379 | student_name_list.append(results_name) 380 | # 获取系统时间,保存到秒 381 | current_time = datetime.now() 382 | start_time.append(current_time) 383 | else: # 通过学号判断是否需要加入全局列表 384 | flag = 0 385 | num = 0 386 | for i in range(len(num_list)): 387 | if name == num_list[i]: 388 | flag = 1 # 全局列表中已经存在 389 | num = i 390 | break 391 | if flag == 0: 392 | num_list.append(name) 393 | emo_list.append(txt) 394 | student_name_list.append(results_name) 395 | current_time = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 396 | start_time.append(current_time) 397 | else: 398 | if txt != emo_list[num]: # 判断情绪是否相同 399 | emo_list[num] = txt 400 | current_time = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 401 | start_time[num] = current_time 402 | 403 | temp = [] 404 | for i in range(len(num_list)): 405 | flag = 0 406 | for j in range(len(face_names)): 407 | if num_list[i] == face_names[j]: 408 | flag = 1 409 | if flag == 0: 410 | temp.append(i) 411 | 412 | res = 0 413 | for i in temp: 414 | # 已经不在识别框的学号剔除 415 | del num_list[i-res] 416 | del emo_list[i-res] 417 | del start_time[i-res] 418 | del student_name_list[i-res] 419 | res = res + 1 # 删除后全部下标前移一个 420 | for kk in range(len(num_list)): 421 | # 写入数据库中(建立新表) 422 | try: 423 | # 打开数据库连接 424 | db, cursor = connect_to_sql() 425 | except ConnectionError as e: 426 | print("[Error] 数据库连接失败!") 427 | self.line_text_info3.append((num_list[kk], student_name_list[kk], emo_list[kk], start_time[kk])) 428 | # 写入数据库 429 | try: 430 | # 如果存在数据,先删除再写入。前提是设置唯一索引字段或者主键。 431 | insert_sql3 = "replace into detail(ID, Name, Emo, starttime) values(%s, %s, %s, %s)" 432 | users3 = self.line_text_info3 433 | cursor.executemany(insert_sql3, users3) 434 | finally: 435 | # 提交到数据库执行 436 | db.commit() 437 | cursor.close() 438 | db.close() 439 | 440 | # 统计各情绪数量 441 | emo_count = Counter(emo_txt) 442 | emo_len = len(emo_txt) 443 | step1 = step2 = step3 =step4 = step5 = 0 444 | for k, v in emo_count.items(): 445 | if k == 'happy': 446 | step1 = 1 447 | self.ui.label_emo1_1.setText(str(round((v/emo_len)*100,2))+'%') 448 | if k == 'sad': 449 | step2 = 2 450 | self.ui.label_emo2_2.setText(str(round((v/emo_len)*100,2))+'%') 451 | if k == 'surprise': 452 | step3 = 3 453 | self.ui.label_emo3_3.setText(str(round((v/emo_len)*100,2))+'%') 454 | if k == 'neutral': 455 | step4 = 4 456 | self.ui.label_emo6_6.setText(str(round((v/emo_len)*100,2))+'%') 457 | if k == 'anger': 458 | step5 = 5 459 | self.ui.label_emo4_4.setText(str(round((v/emo_len)*100,2))+'%') 460 | # 刷新界面 461 | if step1 == 0: 462 | self.ui.label_emo1_1.setText('0.00%') 463 | if step2 == 0: 464 | self.ui.label_emo2_2.setText('0.00%') 465 | if step3 == 0: 466 | self.ui.label_emo3_3.setText('0.00%') 467 | if step4 == 0: 468 | self.ui.label_emo6_6.setText('0.00%') 469 | if step5 == 0: 470 | self.ui.label_emo4_4.setText('0.00%') 471 | 472 | # 显示输出框架 473 | show_video = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 这里指的是显示原图 474 | # opencv读取图片的样式,不能通过Qlabel进行显示,需要转换为Qimage。 475 | # QImage(uchar * data, int width, int height, int bytesPerLine, Format format) 476 | self.showImage = QImage(show_video.data, show_video.shape[1], show_video.shape[0], 477 | QImage.Format_RGB888) 478 | self.ui.label_camera.setPixmap(QPixmap.fromImage(self.showImage)) 479 | 480 | if loop_num == FR_LOOP_NUM: 481 | # 找到10帧中检测次数最多的人脸 482 | # Python字典按照值的大小降序排列,并返回键值对元组 483 | # 第一个索引[0]表示取排序后的第一个键值对,第二个索引[0]表示取键 484 | most_id_in_dict = \ 485 | sorted(self.face_name_dict.items(), key=lambda kv: (kv[1], kv[0]), reverse=True)[0][0] 486 | # 将当前帧检测到次数最多的人脸保存到self.set_name集合中 487 | self.set_name = set() 488 | self.set_name.add(most_id_in_dict) 489 | self.set_names = tuple(self.set_name) 490 | 491 | self.record_names() 492 | self.face_name_dict = dict(zip(le.classes_, len(le.classes_) * [0])) 493 | loop_num = 0 494 | else: 495 | pass 496 | else: 497 | self.cap.release() 498 | 499 | # 因为最后一张画面会显示在GUI中,此处实现清除。 500 | self.ui.label_camera.clear() 501 | 502 | def record_names(self): 503 | # 如果self.set_names是self.record_names 的子集返回ture 504 | if self.set_name.issubset(self.record_name): 505 | pass # record_name1是要写进数据库中的名字信息 set_name是从摄像头中读出人脸的tuple形式 506 | else: 507 | # 获取到self.set_name有而self.record_name无的名字 508 | self.different_name = self.set_name.difference(self.record_name) 509 | # 把self.record_name变成两个集合的并集 510 | self.record_name = self.set_name.union(self.record_name) 511 | # different_name是为了获取到之前没有捕捉到的人脸,并再次将record_name1进行更新 512 | 513 | # 将集合变成tuple,并统计人数 514 | self.write_data = tuple(self.different_name) 515 | names_num = len(self.write_data) 516 | 517 | if names_num > 0: 518 | # 将签到信息写入数据库 519 | self.line_text_info = [] 520 | try: 521 | # 打开数据库连接 522 | db, cursor = connect_to_sql() 523 | except ConnectionError as e: 524 | print("[Error] 数据库连接失败!") 525 | else: 526 | # 获取系统时间,保存到秒 527 | current_time = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 528 | results2 = self.use_id_get_info(self.write_data[0]) 529 | 530 | # 判断是否迟到,在这里只考虑正常情况,没有设置考勤时间的按钮 531 | self.now = datetime.now() 532 | self.attendance_state = '正常' 533 | self.line_text_info.append((results2[0], results2[1], results2[2], 534 | current_time, 535 | self.attendance_state)) 536 | 537 | # 写入数据库 538 | try: 539 | # 如果存在数据,先删除再写入。前提是设置唯一索引字段或者主键。 540 | insert_sql2 = "replace into checkin(Name, ID, Class, Time, Description) values(%s, %s, %s, %s, %s)" 541 | users2 = self.line_text_info 542 | cursor.executemany(insert_sql2, users2) 543 | except ConnectionAbortedError as e: 544 | self.ui.textBrowser_log.append("[INFO] 数据库写入失败!") 545 | else: 546 | self.ui.textBrowser_log.append("[INFO] 数据库写入成功!") 547 | finally: 548 | # 提交到数据库执行 549 | db.commit() 550 | cursor.close() 551 | db.close() 552 | 553 | 554 | # 请假/补签登记 555 | def leave_button(self): 556 | self.leave_students(1) 557 | 558 | def supplyment_button(self): 559 | self.leave_students(2) 560 | 561 | def leave_students(self, button): 562 | global results 563 | self.lineTextInfo = [] 564 | # 为防止输入为空卡死,先进行是否输入数据的判断 565 | if self.ui.lineEdit_leave.isModified() or self.ui.lineEdit_supplement.isModified(): 566 | # 打开数据库连接 567 | db, cursor = connect_to_sql() 568 | # 获取系统时间,保存到秒 569 | currentTime = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 570 | if button == 1: 571 | self.ui.textBrowser_log.append("[INFO] 正在执行请假登记...") 572 | self.description = "请假" 573 | self.lineText_leave_id = self.ui.lineEdit_leave.text() 574 | results = self.use_id_get_info(self.lineText_leave_id) 575 | elif button == 2: 576 | self.ui.textBrowser_log.append("[INFO] 正在执行漏签补签...") 577 | self.description = "漏签补签" 578 | self.lineText_leave_id = self.ui.lineEdit_supplement.text() 579 | results = self.use_id_get_info(self.lineText_leave_id) 580 | else: 581 | print("[Error] The value of button must be one or two!") 582 | 583 | if len(results) != 0: 584 | try: 585 | self.ui.textBrowser_log.append("[INFO] 正在从数据库获取当前用户信息...") 586 | self.lineTextInfo.append((results[0], results[1], results[2], 587 | currentTime, 588 | self.description)) 589 | except ConnectionAbortedError as e: 590 | self.ui.textBrowser_log.append("[INFO] 从数据库获取信息失败,请保证当前用户的信息和考勤记录已录入数据库!", e) 591 | # 写入数据库 592 | try: 593 | # 如果存在数据,先删除再写入。前提是设置唯一索引字段或者主键。 594 | insert_sql = "replace into checkin(Name, ID, Class, Time, Description) values(%s, %s, %s, %s, %s)" 595 | users = self.lineTextInfo 596 | cursor.executemany(insert_sql, users) 597 | except ValueError as e: 598 | self.ui.textBrowser_log.append("[INFO] 写入数据库失败!", e) 599 | else: 600 | self.ui.textBrowser_log.append("[INFO] 写入数据库成功!") 601 | QMessageBox.warning(self, "Warning", "{} {}登记成功,请勿重复操作!".format(self.lineText_leave_id, 602 | self.description), QMessageBox.Ok) 603 | finally: 604 | # 提交到数据库执行 605 | db.commit() 606 | cursor.close() 607 | db.close() 608 | 609 | else: 610 | QMessageBox.critical(self, "Error", f"您输入的ID {self.lineText_leave_id} 不存在!请先录入数据库!") 611 | else: 612 | QMessageBox.critical(self, "Error", "学号不能为空,请输入后重试!", QMessageBox.Ok) # (QMessageBox.Yes | QMessageBox.No) 613 | # 输入框清零 614 | self.ui.lineEdit_leave.clear() 615 | self.ui.lineEdit_supplement.clear() 616 | 617 | 618 | # 核验本地人脸与数据库信息是否一致 619 | def check_variation_db(self): 620 | try: 621 | db, cursor = connect_to_sql() 622 | except ConnectionAbortedError as e: 623 | self.ui.textBrowser_log.append('[INFO] 连接数据库失败,请检查配置信息!') 624 | else: 625 | sql = "select id, name from students" 626 | # 执行查询 627 | cursor.execute(sql) 628 | results = cursor.fetchall() 629 | self.student_ids = [] 630 | self.student_names = [] 631 | for item in results: 632 | self.student_ids.append(item[0]) 633 | self.student_names.append(item[1]) 634 | print('[INFO] 当前班级内的成员包括:', self.student_ids, self.student_names) 635 | # 统计本地人脸数据信息 636 | num_dict = statical_facedata_nums() 637 | # ID 638 | self.keys = [] 639 | for key in list(num_dict.keys()): 640 | self.keys.append(int(key)) 641 | self.check_variation_set_operate() 642 | finally: 643 | db.commit() 644 | cursor.close() 645 | db.close() 646 | 647 | def check_variation_set_operate(self): 648 | # 并集 649 | union_set = set(self.student_ids).union(set(self.keys)) 650 | # 交集 651 | inter_set = set(self.student_ids).intersection(set(self.keys)) 652 | # 差集 653 | two_diff_set = set(self.student_ids).difference(set(self.keys)) 654 | # 本地与数据库不同的ID 655 | local_diff_set = union_set - set(self.student_ids) 656 | # 数据库与本地不同的ID 657 | db_diff_set = union_set - set(self.keys) 658 | 659 | if len(union_set) == 0: 660 | self.ui.textBrowser_log.append('[Error] 本地人脸库名称与数据库均未录入信息,请先录入!') 661 | print('[Error] union_set:', union_set) 662 | elif len(inter_set) == 0 and len(union_set) != 0: 663 | self.ui.textBrowser_log.append('[Error] 本地人脸库名称与数据库完全不一致,请先修改!') 664 | print('[Error] inter_set:', inter_set) 665 | elif len(two_diff_set) == 0 and len(union_set) != 0: 666 | self.ui.textBrowser_log.append('[Success] 核验完成,未发现问题!') 667 | print('[Success] two_diff_set:', two_diff_set) 668 | 669 | elif len(local_diff_set) != 0: 670 | self.ui.textBrowser_log.append('[Warning] 数据库中以下ID的人脸信息还未采集,请抓紧采集!') 671 | self.ui.textBrowser_log.append(str(local_diff_set)) 672 | print('[Warning] local_diff_set:', local_diff_set) 673 | elif len(db_diff_set) != 0: 674 | self.ui.textBrowser_log.append('[Warning] 本地人脸以下ID的信息还未录入数据库,请抓紧录入!') 675 | self.ui.textBrowser_log.append(str(db_diff_set)) 676 | print('[Warning] db_diff_set:', db_diff_set) 677 | 678 | # 使用ID当索引找到其它信息 679 | def use_id_get_info(self, ID): 680 | global cursor, db 681 | if ID != '': 682 | try: 683 | # 打开数据库连接 684 | db, cursor = connect_to_sql() 685 | # 查询语句,实现通过ID关键字检索个人信息的功能 686 | sql = "select * from students where ID = {}".format(ID) 687 | # 执行查询 688 | cursor.execute(sql) 689 | # 获取所有记录列表 690 | results = cursor.fetchall() 691 | self.check_info = [] 692 | for i in results: 693 | self.check_info.append(i[1]) 694 | self.check_info.append(i[0]) 695 | self.check_info.append(i[2]) 696 | return self.check_info 697 | except ConnectionAbortedError as e: 698 | self.ui.textBrowser_log.append("[ERROR] 数据库连接失败!") 699 | finally: 700 | db.commit() 701 | cursor.close() 702 | db.close() 703 | 704 | # 显示未到 705 | def show_late_absence(self): 706 | try: 707 | db, cursor = connect_to_sql() 708 | except ConnectionAbortedError as e: 709 | self.ui.textBrowser_log.append('[INFO] 连接数据库失败,请检查配置信息!') 710 | else: 711 | sql = "select ID, Name, Class from students" # 学生信息 712 | 713 | sql2 = "select * from checkin" # 考勤信息 714 | # 执行查询,全部学生 715 | cursor.execute(sql) 716 | results = cursor.fetchall() 717 | self.student_ids = [] 718 | self.student_names = [] 719 | self.student_class = [] 720 | # self.student_time = [] 721 | for item in results: 722 | self.student_ids.append(item[0]) 723 | self.student_names.append(item[1]) 724 | self.student_class.append(item[2]) 725 | 726 | # 执行查询,已经到了的 727 | cursor.execute(sql2) 728 | result2 = cursor.fetchall() 729 | self.student_ids2 = [] 730 | self.student_names2 = [] 731 | self.student_class2 = [] 732 | current_year = datetime.now().year 733 | current_month= datetime.now().month 734 | current_day = datetime.now().day 735 | for item in result2: 736 | if current_year == item[3].year and current_month == item[3].month and current_day == item[3].day: 737 | self.student_ids2.append(item[1]) 738 | self.student_names2.append(item[0]) 739 | self.student_class2.append(item[2]) 740 | finally: 741 | db.commit() 742 | cursor.close() 743 | db.close() 744 | if len(self.student_ids) == 0: 745 | QMessageBox.warning(self, "Yes", "全员到齐", QMessageBox.Ok) 746 | else: 747 | # 设置显示数据层次结构,4行4列(包含行表头) 748 | table_view_module = QtGui.QStandardItemModel(len(self.student_ids) - len(self.student_ids2), 1) 749 | table_view_module.setHorizontalHeaderLabels(['学号', '姓名', '班级']) 750 | 751 | step = 0 752 | for i in range(len(self.student_ids)): 753 | flag = 0 754 | for j in range(len(self.student_ids2)): 755 | if self.student_ids[i] == self.student_ids2[j]: 756 | flag = 1 757 | if flag == 0: # 未到 758 | id = self.student_ids[i] 759 | num = QtGui.QStandardItem(str(id)) 760 | name = self.student_names[i] 761 | name = QtGui.QStandardItem(name) 762 | Class = self.student_class[i] 763 | Class = QtGui.QStandardItem(Class) 764 | # 设置每个位置的行名称和文本值 765 | table_view_module.setItem(step, 0, num) 766 | table_view_module.setItem(step, 1, name) 767 | table_view_module.setItem(step, 2, Class) 768 | step = step + 1 769 | 770 | # 指定显示的tableView_escape控件,实例化表格视图 771 | self.ui.tableView_escape.setModel(table_view_module) 772 | 773 | 774 | # 导出未到学生 775 | def import_late_absence(self): 776 | self.address = QtWidgets.QFileDialog.getExistingDirectory(None, "选取文件夹", "C:/") # 起始路径 777 | try: 778 | db, cursor = connect_to_sql() 779 | except ConnectionAbortedError as e: 780 | self.ui.textBrowser_log.append('[INFO] 连接数据库失败,请检查配置信息!') 781 | else: 782 | sql = "select ID, Name, Class from students" # 学生信息 783 | 784 | sql2 = "select * from checkin" # 考勤信息 785 | # 执行查询,全部学生 786 | cursor.execute(sql) 787 | results = cursor.fetchall() 788 | self.student_ids = [] 789 | self.student_names = [] 790 | self.student_class = [] 791 | for item in results: 792 | self.student_ids.append(item[0]) 793 | self.student_names.append(item[1]) 794 | self.student_class.append(item[2]) 795 | 796 | # 执行查询,已经到了的 797 | cursor.execute(sql2) 798 | result2 = cursor.fetchall() 799 | self.student_ids2 = [] 800 | self.student_names2 = [] 801 | self.student_class2 = [] 802 | current_year = datetime.now().year 803 | current_month= datetime.now().month 804 | current_day = datetime.now().day 805 | for item in result2: 806 | if current_year == item[3].year and current_month == item[3].month and current_day == item[3].day: 807 | self.student_ids2.append(item[1]) 808 | self.student_names2.append(item[0]) 809 | self.student_class2.append(item[2]) 810 | finally: 811 | db.commit() 812 | cursor.close() 813 | db.close() 814 | wb = openpyxl.Workbook() # 创建工作簿对象 815 | current_year = str(current_year) 816 | current_month = str(current_month) 817 | current_day = str(current_day) 818 | ws = wb['Sheet'] # 创建子表 819 | ws.append(['学号', '姓名', '班级']) # 添加表头 820 | for i in range(len(self.student_ids)): 821 | flag = 0 822 | for j in range(len(self.student_ids2)): 823 | if self.student_ids[i] == self.student_ids2[j]: 824 | flag = 1 825 | if flag == 0: # 未到 826 | id = self.student_ids[i] 827 | name = self.student_names[i] 828 | Class = self.student_class[i] 829 | data = id, name, Class 830 | ws.append(data) # 每次写入一行 831 | tablename = current_year + '年' + current_month + '月' + current_day + '日缺勤名单' 832 | wb.save(self.address + '\\' + tablename + '.xlsx') 833 | 834 | # 训练人脸识别模型,静态方法 835 | # @staticmethod 836 | def train_model(self): 837 | q_message = QMessageBox.information(self, "Tips", "你确定要重新训练模型吗?", QMessageBox.Yes | QMessageBox.No) 838 | if QMessageBox.Yes == q_message: 839 | GeneratorModel.Generator() 840 | GeneratorModel.TrainModel() 841 | self.ui.textBrowser_log.append('[INFO] 模型训练完成!') 842 | else: 843 | self.ui.textBrowser_log.append('[INFO] 取消模型进程!') 844 | 845 | def open_info_dialog(self): 846 | if self.cap.isOpened(): 847 | QMessageBox.warning(self, "Warning", "为防止摄像头冲突,已自动关闭摄像头!", QMessageBox.Ok) 848 | self.cap.release() 849 | 850 | 851 | def quit_window(self): 852 | if self.cap.isOpened(): 853 | self.cap.release() 854 | QCoreApplication.quit() 855 | 856 | 857 | if __name__ == '__main__': 858 | app = QApplication(sys.argv) 859 | # 创建并显示窗口 860 | mainWindow = MainWindow() 861 | infoWindow = InfoDialog() 862 | detailWindow = Detaillog() 863 | rcWindow = RCDialog() 864 | mainWindow.ui.bt_gathering.clicked.connect(infoWindow.handle_click) 865 | mainWindow.ui.bt_analyse.clicked.connect(detailWindow.handle_click) 866 | mainWindow.ui.bt_random_check.clicked.connect(rcWindow.handle_click) 867 | mainWindow.show() 868 | sys.exit(app.exec_()) 869 | -------------------------------------------------------------------------------- /images/deep_face_recognition_process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/images/deep_face_recognition_process.png -------------------------------------------------------------------------------- /images/detail_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/images/detail_window.png -------------------------------------------------------------------------------- /images/face_alignment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/images/face_alignment.png -------------------------------------------------------------------------------- /images/face_detection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/images/face_detection.png -------------------------------------------------------------------------------- /images/facenet_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/images/facenet_architecture.png -------------------------------------------------------------------------------- /images/info_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/images/info_window.png -------------------------------------------------------------------------------- /images/main_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/images/main_window.png -------------------------------------------------------------------------------- /images/mysql_table1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/images/mysql_table1.png -------------------------------------------------------------------------------- /images/mysql_table2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/images/mysql_table2.png -------------------------------------------------------------------------------- /images/mysql_table3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/images/mysql_table3.png -------------------------------------------------------------------------------- /images/mysql_table4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/images/mysql_table4.png -------------------------------------------------------------------------------- /logo_imgs/bkg1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/logo_imgs/bkg1.png -------------------------------------------------------------------------------- /logo_imgs/bkg2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/logo_imgs/bkg2.png -------------------------------------------------------------------------------- /logo_imgs/brain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/logo_imgs/brain.png -------------------------------------------------------------------------------- /logo_imgs/fcb_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/logo_imgs/fcb_logo.jpg -------------------------------------------------------------------------------- /logo_imgs/fcb_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/logo_imgs/fcb_logo.png -------------------------------------------------------------------------------- /logo_imgs/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/logo_imgs/info.png -------------------------------------------------------------------------------- /logo_imgs/info_icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/logo_imgs/info_icon.jpg -------------------------------------------------------------------------------- /logo_imgs/warn_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/logo_imgs/warn_icon.png -------------------------------------------------------------------------------- /model_face_detection/deploy.prototxt: -------------------------------------------------------------------------------- 1 | input: "data" 2 | input_shape { 3 | dim: 1 4 | dim: 3 5 | dim: 300 6 | dim: 300 7 | } 8 | 9 | layer { 10 | name: "data_bn" 11 | type: "BatchNorm" 12 | bottom: "data" 13 | top: "data_bn" 14 | param { 15 | lr_mult: 0.0 16 | } 17 | param { 18 | lr_mult: 0.0 19 | } 20 | param { 21 | lr_mult: 0.0 22 | } 23 | } 24 | layer { 25 | name: "data_scale" 26 | type: "Scale" 27 | bottom: "data_bn" 28 | top: "data_bn" 29 | param { 30 | lr_mult: 1.0 31 | decay_mult: 1.0 32 | } 33 | param { 34 | lr_mult: 2.0 35 | decay_mult: 1.0 36 | } 37 | scale_param { 38 | bias_term: true 39 | } 40 | } 41 | layer { 42 | name: "conv1_h" 43 | type: "Convolution" 44 | bottom: "data_bn" 45 | top: "conv1_h" 46 | param { 47 | lr_mult: 1.0 48 | decay_mult: 1.0 49 | } 50 | param { 51 | lr_mult: 2.0 52 | decay_mult: 1.0 53 | } 54 | convolution_param { 55 | num_output: 32 56 | pad: 3 57 | kernel_size: 7 58 | stride: 2 59 | weight_filler { 60 | type: "msra" 61 | variance_norm: FAN_OUT 62 | } 63 | bias_filler { 64 | type: "constant" 65 | value: 0.0 66 | } 67 | } 68 | } 69 | layer { 70 | name: "conv1_bn_h" 71 | type: "BatchNorm" 72 | bottom: "conv1_h" 73 | top: "conv1_h" 74 | param { 75 | lr_mult: 0.0 76 | } 77 | param { 78 | lr_mult: 0.0 79 | } 80 | param { 81 | lr_mult: 0.0 82 | } 83 | } 84 | layer { 85 | name: "conv1_scale_h" 86 | type: "Scale" 87 | bottom: "conv1_h" 88 | top: "conv1_h" 89 | param { 90 | lr_mult: 1.0 91 | decay_mult: 1.0 92 | } 93 | param { 94 | lr_mult: 2.0 95 | decay_mult: 1.0 96 | } 97 | scale_param { 98 | bias_term: true 99 | } 100 | } 101 | layer { 102 | name: "conv1_relu" 103 | type: "ReLU" 104 | bottom: "conv1_h" 105 | top: "conv1_h" 106 | } 107 | layer { 108 | name: "conv1_pool" 109 | type: "Pooling" 110 | bottom: "conv1_h" 111 | top: "conv1_pool" 112 | pooling_param { 113 | kernel_size: 3 114 | stride: 2 115 | } 116 | } 117 | layer { 118 | name: "layer_64_1_conv1_h" 119 | type: "Convolution" 120 | bottom: "conv1_pool" 121 | top: "layer_64_1_conv1_h" 122 | param { 123 | lr_mult: 1.0 124 | decay_mult: 1.0 125 | } 126 | convolution_param { 127 | num_output: 32 128 | bias_term: false 129 | pad: 1 130 | kernel_size: 3 131 | stride: 1 132 | weight_filler { 133 | type: "msra" 134 | } 135 | bias_filler { 136 | type: "constant" 137 | value: 0.0 138 | } 139 | } 140 | } 141 | layer { 142 | name: "layer_64_1_bn2_h" 143 | type: "BatchNorm" 144 | bottom: "layer_64_1_conv1_h" 145 | top: "layer_64_1_conv1_h" 146 | param { 147 | lr_mult: 0.0 148 | } 149 | param { 150 | lr_mult: 0.0 151 | } 152 | param { 153 | lr_mult: 0.0 154 | } 155 | } 156 | layer { 157 | name: "layer_64_1_scale2_h" 158 | type: "Scale" 159 | bottom: "layer_64_1_conv1_h" 160 | top: "layer_64_1_conv1_h" 161 | param { 162 | lr_mult: 1.0 163 | decay_mult: 1.0 164 | } 165 | param { 166 | lr_mult: 2.0 167 | decay_mult: 1.0 168 | } 169 | scale_param { 170 | bias_term: true 171 | } 172 | } 173 | layer { 174 | name: "layer_64_1_relu2" 175 | type: "ReLU" 176 | bottom: "layer_64_1_conv1_h" 177 | top: "layer_64_1_conv1_h" 178 | } 179 | layer { 180 | name: "layer_64_1_conv2_h" 181 | type: "Convolution" 182 | bottom: "layer_64_1_conv1_h" 183 | top: "layer_64_1_conv2_h" 184 | param { 185 | lr_mult: 1.0 186 | decay_mult: 1.0 187 | } 188 | convolution_param { 189 | num_output: 32 190 | bias_term: false 191 | pad: 1 192 | kernel_size: 3 193 | stride: 1 194 | weight_filler { 195 | type: "msra" 196 | } 197 | bias_filler { 198 | type: "constant" 199 | value: 0.0 200 | } 201 | } 202 | } 203 | layer { 204 | name: "layer_64_1_sum" 205 | type: "Eltwise" 206 | bottom: "layer_64_1_conv2_h" 207 | bottom: "conv1_pool" 208 | top: "layer_64_1_sum" 209 | } 210 | layer { 211 | name: "layer_128_1_bn1_h" 212 | type: "BatchNorm" 213 | bottom: "layer_64_1_sum" 214 | top: "layer_128_1_bn1_h" 215 | param { 216 | lr_mult: 0.0 217 | } 218 | param { 219 | lr_mult: 0.0 220 | } 221 | param { 222 | lr_mult: 0.0 223 | } 224 | } 225 | layer { 226 | name: "layer_128_1_scale1_h" 227 | type: "Scale" 228 | bottom: "layer_128_1_bn1_h" 229 | top: "layer_128_1_bn1_h" 230 | param { 231 | lr_mult: 1.0 232 | decay_mult: 1.0 233 | } 234 | param { 235 | lr_mult: 2.0 236 | decay_mult: 1.0 237 | } 238 | scale_param { 239 | bias_term: true 240 | } 241 | } 242 | layer { 243 | name: "layer_128_1_relu1" 244 | type: "ReLU" 245 | bottom: "layer_128_1_bn1_h" 246 | top: "layer_128_1_bn1_h" 247 | } 248 | layer { 249 | name: "layer_128_1_conv1_h" 250 | type: "Convolution" 251 | bottom: "layer_128_1_bn1_h" 252 | top: "layer_128_1_conv1_h" 253 | param { 254 | lr_mult: 1.0 255 | decay_mult: 1.0 256 | } 257 | convolution_param { 258 | num_output: 128 259 | bias_term: false 260 | pad: 1 261 | kernel_size: 3 262 | stride: 2 263 | weight_filler { 264 | type: "msra" 265 | } 266 | bias_filler { 267 | type: "constant" 268 | value: 0.0 269 | } 270 | } 271 | } 272 | layer { 273 | name: "layer_128_1_bn2" 274 | type: "BatchNorm" 275 | bottom: "layer_128_1_conv1_h" 276 | top: "layer_128_1_conv1_h" 277 | param { 278 | lr_mult: 0.0 279 | } 280 | param { 281 | lr_mult: 0.0 282 | } 283 | param { 284 | lr_mult: 0.0 285 | } 286 | } 287 | layer { 288 | name: "layer_128_1_scale2" 289 | type: "Scale" 290 | bottom: "layer_128_1_conv1_h" 291 | top: "layer_128_1_conv1_h" 292 | param { 293 | lr_mult: 1.0 294 | decay_mult: 1.0 295 | } 296 | param { 297 | lr_mult: 2.0 298 | decay_mult: 1.0 299 | } 300 | scale_param { 301 | bias_term: true 302 | } 303 | } 304 | layer { 305 | name: "layer_128_1_relu2" 306 | type: "ReLU" 307 | bottom: "layer_128_1_conv1_h" 308 | top: "layer_128_1_conv1_h" 309 | } 310 | layer { 311 | name: "layer_128_1_conv2" 312 | type: "Convolution" 313 | bottom: "layer_128_1_conv1_h" 314 | top: "layer_128_1_conv2" 315 | param { 316 | lr_mult: 1.0 317 | decay_mult: 1.0 318 | } 319 | convolution_param { 320 | num_output: 128 321 | bias_term: false 322 | pad: 1 323 | kernel_size: 3 324 | stride: 1 325 | weight_filler { 326 | type: "msra" 327 | } 328 | bias_filler { 329 | type: "constant" 330 | value: 0.0 331 | } 332 | } 333 | } 334 | layer { 335 | name: "layer_128_1_conv_expand_h" 336 | type: "Convolution" 337 | bottom: "layer_128_1_bn1_h" 338 | top: "layer_128_1_conv_expand_h" 339 | param { 340 | lr_mult: 1.0 341 | decay_mult: 1.0 342 | } 343 | convolution_param { 344 | num_output: 128 345 | bias_term: false 346 | pad: 0 347 | kernel_size: 1 348 | stride: 2 349 | weight_filler { 350 | type: "msra" 351 | } 352 | bias_filler { 353 | type: "constant" 354 | value: 0.0 355 | } 356 | } 357 | } 358 | layer { 359 | name: "layer_128_1_sum" 360 | type: "Eltwise" 361 | bottom: "layer_128_1_conv2" 362 | bottom: "layer_128_1_conv_expand_h" 363 | top: "layer_128_1_sum" 364 | } 365 | layer { 366 | name: "layer_256_1_bn1" 367 | type: "BatchNorm" 368 | bottom: "layer_128_1_sum" 369 | top: "layer_256_1_bn1" 370 | param { 371 | lr_mult: 0.0 372 | } 373 | param { 374 | lr_mult: 0.0 375 | } 376 | param { 377 | lr_mult: 0.0 378 | } 379 | } 380 | layer { 381 | name: "layer_256_1_scale1" 382 | type: "Scale" 383 | bottom: "layer_256_1_bn1" 384 | top: "layer_256_1_bn1" 385 | param { 386 | lr_mult: 1.0 387 | decay_mult: 1.0 388 | } 389 | param { 390 | lr_mult: 2.0 391 | decay_mult: 1.0 392 | } 393 | scale_param { 394 | bias_term: true 395 | } 396 | } 397 | layer { 398 | name: "layer_256_1_relu1" 399 | type: "ReLU" 400 | bottom: "layer_256_1_bn1" 401 | top: "layer_256_1_bn1" 402 | } 403 | layer { 404 | name: "layer_256_1_conv1" 405 | type: "Convolution" 406 | bottom: "layer_256_1_bn1" 407 | top: "layer_256_1_conv1" 408 | param { 409 | lr_mult: 1.0 410 | decay_mult: 1.0 411 | } 412 | convolution_param { 413 | num_output: 256 414 | bias_term: false 415 | pad: 1 416 | kernel_size: 3 417 | stride: 2 418 | weight_filler { 419 | type: "msra" 420 | } 421 | bias_filler { 422 | type: "constant" 423 | value: 0.0 424 | } 425 | } 426 | } 427 | layer { 428 | name: "layer_256_1_bn2" 429 | type: "BatchNorm" 430 | bottom: "layer_256_1_conv1" 431 | top: "layer_256_1_conv1" 432 | param { 433 | lr_mult: 0.0 434 | } 435 | param { 436 | lr_mult: 0.0 437 | } 438 | param { 439 | lr_mult: 0.0 440 | } 441 | } 442 | layer { 443 | name: "layer_256_1_scale2" 444 | type: "Scale" 445 | bottom: "layer_256_1_conv1" 446 | top: "layer_256_1_conv1" 447 | param { 448 | lr_mult: 1.0 449 | decay_mult: 1.0 450 | } 451 | param { 452 | lr_mult: 2.0 453 | decay_mult: 1.0 454 | } 455 | scale_param { 456 | bias_term: true 457 | } 458 | } 459 | layer { 460 | name: "layer_256_1_relu2" 461 | type: "ReLU" 462 | bottom: "layer_256_1_conv1" 463 | top: "layer_256_1_conv1" 464 | } 465 | layer { 466 | name: "layer_256_1_conv2" 467 | type: "Convolution" 468 | bottom: "layer_256_1_conv1" 469 | top: "layer_256_1_conv2" 470 | param { 471 | lr_mult: 1.0 472 | decay_mult: 1.0 473 | } 474 | convolution_param { 475 | num_output: 256 476 | bias_term: false 477 | pad: 1 478 | kernel_size: 3 479 | stride: 1 480 | weight_filler { 481 | type: "msra" 482 | } 483 | bias_filler { 484 | type: "constant" 485 | value: 0.0 486 | } 487 | } 488 | } 489 | layer { 490 | name: "layer_256_1_conv_expand" 491 | type: "Convolution" 492 | bottom: "layer_256_1_bn1" 493 | top: "layer_256_1_conv_expand" 494 | param { 495 | lr_mult: 1.0 496 | decay_mult: 1.0 497 | } 498 | convolution_param { 499 | num_output: 256 500 | bias_term: false 501 | pad: 0 502 | kernel_size: 1 503 | stride: 2 504 | weight_filler { 505 | type: "msra" 506 | } 507 | bias_filler { 508 | type: "constant" 509 | value: 0.0 510 | } 511 | } 512 | } 513 | layer { 514 | name: "layer_256_1_sum" 515 | type: "Eltwise" 516 | bottom: "layer_256_1_conv2" 517 | bottom: "layer_256_1_conv_expand" 518 | top: "layer_256_1_sum" 519 | } 520 | layer { 521 | name: "layer_512_1_bn1" 522 | type: "BatchNorm" 523 | bottom: "layer_256_1_sum" 524 | top: "layer_512_1_bn1" 525 | param { 526 | lr_mult: 0.0 527 | } 528 | param { 529 | lr_mult: 0.0 530 | } 531 | param { 532 | lr_mult: 0.0 533 | } 534 | } 535 | layer { 536 | name: "layer_512_1_scale1" 537 | type: "Scale" 538 | bottom: "layer_512_1_bn1" 539 | top: "layer_512_1_bn1" 540 | param { 541 | lr_mult: 1.0 542 | decay_mult: 1.0 543 | } 544 | param { 545 | lr_mult: 2.0 546 | decay_mult: 1.0 547 | } 548 | scale_param { 549 | bias_term: true 550 | } 551 | } 552 | layer { 553 | name: "layer_512_1_relu1" 554 | type: "ReLU" 555 | bottom: "layer_512_1_bn1" 556 | top: "layer_512_1_bn1" 557 | } 558 | layer { 559 | name: "layer_512_1_conv1_h" 560 | type: "Convolution" 561 | bottom: "layer_512_1_bn1" 562 | top: "layer_512_1_conv1_h" 563 | param { 564 | lr_mult: 1.0 565 | decay_mult: 1.0 566 | } 567 | convolution_param { 568 | num_output: 128 569 | bias_term: false 570 | pad: 1 571 | kernel_size: 3 572 | stride: 1 # 2 573 | weight_filler { 574 | type: "msra" 575 | } 576 | bias_filler { 577 | type: "constant" 578 | value: 0.0 579 | } 580 | } 581 | } 582 | layer { 583 | name: "layer_512_1_bn2_h" 584 | type: "BatchNorm" 585 | bottom: "layer_512_1_conv1_h" 586 | top: "layer_512_1_conv1_h" 587 | param { 588 | lr_mult: 0.0 589 | } 590 | param { 591 | lr_mult: 0.0 592 | } 593 | param { 594 | lr_mult: 0.0 595 | } 596 | } 597 | layer { 598 | name: "layer_512_1_scale2_h" 599 | type: "Scale" 600 | bottom: "layer_512_1_conv1_h" 601 | top: "layer_512_1_conv1_h" 602 | param { 603 | lr_mult: 1.0 604 | decay_mult: 1.0 605 | } 606 | param { 607 | lr_mult: 2.0 608 | decay_mult: 1.0 609 | } 610 | scale_param { 611 | bias_term: true 612 | } 613 | } 614 | layer { 615 | name: "layer_512_1_relu2" 616 | type: "ReLU" 617 | bottom: "layer_512_1_conv1_h" 618 | top: "layer_512_1_conv1_h" 619 | } 620 | layer { 621 | name: "layer_512_1_conv2_h" 622 | type: "Convolution" 623 | bottom: "layer_512_1_conv1_h" 624 | top: "layer_512_1_conv2_h" 625 | param { 626 | lr_mult: 1.0 627 | decay_mult: 1.0 628 | } 629 | convolution_param { 630 | num_output: 256 631 | bias_term: false 632 | pad: 2 # 1 633 | kernel_size: 3 634 | stride: 1 635 | dilation: 2 636 | weight_filler { 637 | type: "msra" 638 | } 639 | bias_filler { 640 | type: "constant" 641 | value: 0.0 642 | } 643 | } 644 | } 645 | layer { 646 | name: "layer_512_1_conv_expand_h" 647 | type: "Convolution" 648 | bottom: "layer_512_1_bn1" 649 | top: "layer_512_1_conv_expand_h" 650 | param { 651 | lr_mult: 1.0 652 | decay_mult: 1.0 653 | } 654 | convolution_param { 655 | num_output: 256 656 | bias_term: false 657 | pad: 0 658 | kernel_size: 1 659 | stride: 1 # 2 660 | weight_filler { 661 | type: "msra" 662 | } 663 | bias_filler { 664 | type: "constant" 665 | value: 0.0 666 | } 667 | } 668 | } 669 | layer { 670 | name: "layer_512_1_sum" 671 | type: "Eltwise" 672 | bottom: "layer_512_1_conv2_h" 673 | bottom: "layer_512_1_conv_expand_h" 674 | top: "layer_512_1_sum" 675 | } 676 | layer { 677 | name: "last_bn_h" 678 | type: "BatchNorm" 679 | bottom: "layer_512_1_sum" 680 | top: "layer_512_1_sum" 681 | param { 682 | lr_mult: 0.0 683 | } 684 | param { 685 | lr_mult: 0.0 686 | } 687 | param { 688 | lr_mult: 0.0 689 | } 690 | } 691 | layer { 692 | name: "last_scale_h" 693 | type: "Scale" 694 | bottom: "layer_512_1_sum" 695 | top: "layer_512_1_sum" 696 | param { 697 | lr_mult: 1.0 698 | decay_mult: 1.0 699 | } 700 | param { 701 | lr_mult: 2.0 702 | decay_mult: 1.0 703 | } 704 | scale_param { 705 | bias_term: true 706 | } 707 | } 708 | layer { 709 | name: "last_relu" 710 | type: "ReLU" 711 | bottom: "layer_512_1_sum" 712 | top: "fc7" 713 | } 714 | 715 | layer { 716 | name: "conv6_1_h" 717 | type: "Convolution" 718 | bottom: "fc7" 719 | top: "conv6_1_h" 720 | param { 721 | lr_mult: 1 722 | decay_mult: 1 723 | } 724 | param { 725 | lr_mult: 2 726 | decay_mult: 0 727 | } 728 | convolution_param { 729 | num_output: 128 730 | pad: 0 731 | kernel_size: 1 732 | stride: 1 733 | weight_filler { 734 | type: "xavier" 735 | } 736 | bias_filler { 737 | type: "constant" 738 | value: 0 739 | } 740 | } 741 | } 742 | layer { 743 | name: "conv6_1_relu" 744 | type: "ReLU" 745 | bottom: "conv6_1_h" 746 | top: "conv6_1_h" 747 | } 748 | layer { 749 | name: "conv6_2_h" 750 | type: "Convolution" 751 | bottom: "conv6_1_h" 752 | top: "conv6_2_h" 753 | param { 754 | lr_mult: 1 755 | decay_mult: 1 756 | } 757 | param { 758 | lr_mult: 2 759 | decay_mult: 0 760 | } 761 | convolution_param { 762 | num_output: 256 763 | pad: 1 764 | kernel_size: 3 765 | stride: 2 766 | weight_filler { 767 | type: "xavier" 768 | } 769 | bias_filler { 770 | type: "constant" 771 | value: 0 772 | } 773 | } 774 | } 775 | layer { 776 | name: "conv6_2_relu" 777 | type: "ReLU" 778 | bottom: "conv6_2_h" 779 | top: "conv6_2_h" 780 | } 781 | layer { 782 | name: "conv7_1_h" 783 | type: "Convolution" 784 | bottom: "conv6_2_h" 785 | top: "conv7_1_h" 786 | param { 787 | lr_mult: 1 788 | decay_mult: 1 789 | } 790 | param { 791 | lr_mult: 2 792 | decay_mult: 0 793 | } 794 | convolution_param { 795 | num_output: 64 796 | pad: 0 797 | kernel_size: 1 798 | stride: 1 799 | weight_filler { 800 | type: "xavier" 801 | } 802 | bias_filler { 803 | type: "constant" 804 | value: 0 805 | } 806 | } 807 | } 808 | layer { 809 | name: "conv7_1_relu" 810 | type: "ReLU" 811 | bottom: "conv7_1_h" 812 | top: "conv7_1_h" 813 | } 814 | layer { 815 | name: "conv7_2_h" 816 | type: "Convolution" 817 | bottom: "conv7_1_h" 818 | top: "conv7_2_h" 819 | param { 820 | lr_mult: 1 821 | decay_mult: 1 822 | } 823 | param { 824 | lr_mult: 2 825 | decay_mult: 0 826 | } 827 | convolution_param { 828 | num_output: 128 829 | pad: 1 830 | kernel_size: 3 831 | stride: 2 832 | weight_filler { 833 | type: "xavier" 834 | } 835 | bias_filler { 836 | type: "constant" 837 | value: 0 838 | } 839 | } 840 | } 841 | layer { 842 | name: "conv7_2_relu" 843 | type: "ReLU" 844 | bottom: "conv7_2_h" 845 | top: "conv7_2_h" 846 | } 847 | layer { 848 | name: "conv8_1_h" 849 | type: "Convolution" 850 | bottom: "conv7_2_h" 851 | top: "conv8_1_h" 852 | param { 853 | lr_mult: 1 854 | decay_mult: 1 855 | } 856 | param { 857 | lr_mult: 2 858 | decay_mult: 0 859 | } 860 | convolution_param { 861 | num_output: 64 862 | pad: 0 863 | kernel_size: 1 864 | stride: 1 865 | weight_filler { 866 | type: "xavier" 867 | } 868 | bias_filler { 869 | type: "constant" 870 | value: 0 871 | } 872 | } 873 | } 874 | layer { 875 | name: "conv8_1_relu" 876 | type: "ReLU" 877 | bottom: "conv8_1_h" 878 | top: "conv8_1_h" 879 | } 880 | layer { 881 | name: "conv8_2_h" 882 | type: "Convolution" 883 | bottom: "conv8_1_h" 884 | top: "conv8_2_h" 885 | param { 886 | lr_mult: 1 887 | decay_mult: 1 888 | } 889 | param { 890 | lr_mult: 2 891 | decay_mult: 0 892 | } 893 | convolution_param { 894 | num_output: 128 895 | pad: 1 896 | kernel_size: 3 897 | stride: 1 898 | weight_filler { 899 | type: "xavier" 900 | } 901 | bias_filler { 902 | type: "constant" 903 | value: 0 904 | } 905 | } 906 | } 907 | layer { 908 | name: "conv8_2_relu" 909 | type: "ReLU" 910 | bottom: "conv8_2_h" 911 | top: "conv8_2_h" 912 | } 913 | layer { 914 | name: "conv9_1_h" 915 | type: "Convolution" 916 | bottom: "conv8_2_h" 917 | top: "conv9_1_h" 918 | param { 919 | lr_mult: 1 920 | decay_mult: 1 921 | } 922 | param { 923 | lr_mult: 2 924 | decay_mult: 0 925 | } 926 | convolution_param { 927 | num_output: 64 928 | pad: 0 929 | kernel_size: 1 930 | stride: 1 931 | weight_filler { 932 | type: "xavier" 933 | } 934 | bias_filler { 935 | type: "constant" 936 | value: 0 937 | } 938 | } 939 | } 940 | layer { 941 | name: "conv9_1_relu" 942 | type: "ReLU" 943 | bottom: "conv9_1_h" 944 | top: "conv9_1_h" 945 | } 946 | layer { 947 | name: "conv9_2_h" 948 | type: "Convolution" 949 | bottom: "conv9_1_h" 950 | top: "conv9_2_h" 951 | param { 952 | lr_mult: 1 953 | decay_mult: 1 954 | } 955 | param { 956 | lr_mult: 2 957 | decay_mult: 0 958 | } 959 | convolution_param { 960 | num_output: 128 961 | pad: 1 962 | kernel_size: 3 963 | stride: 1 964 | weight_filler { 965 | type: "xavier" 966 | } 967 | bias_filler { 968 | type: "constant" 969 | value: 0 970 | } 971 | } 972 | } 973 | layer { 974 | name: "conv9_2_relu" 975 | type: "ReLU" 976 | bottom: "conv9_2_h" 977 | top: "conv9_2_h" 978 | } 979 | layer { 980 | name: "conv4_3_norm" 981 | type: "Normalize" 982 | bottom: "layer_256_1_bn1" 983 | top: "conv4_3_norm" 984 | norm_param { 985 | across_spatial: false 986 | scale_filler { 987 | type: "constant" 988 | value: 20 989 | } 990 | channel_shared: false 991 | } 992 | } 993 | layer { 994 | name: "conv4_3_norm_mbox_loc" 995 | type: "Convolution" 996 | bottom: "conv4_3_norm" 997 | top: "conv4_3_norm_mbox_loc" 998 | param { 999 | lr_mult: 1 1000 | decay_mult: 1 1001 | } 1002 | param { 1003 | lr_mult: 2 1004 | decay_mult: 0 1005 | } 1006 | convolution_param { 1007 | num_output: 16 1008 | pad: 1 1009 | kernel_size: 3 1010 | stride: 1 1011 | weight_filler { 1012 | type: "xavier" 1013 | } 1014 | bias_filler { 1015 | type: "constant" 1016 | value: 0 1017 | } 1018 | } 1019 | } 1020 | layer { 1021 | name: "conv4_3_norm_mbox_loc_perm" 1022 | type: "Permute" 1023 | bottom: "conv4_3_norm_mbox_loc" 1024 | top: "conv4_3_norm_mbox_loc_perm" 1025 | permute_param { 1026 | order: 0 1027 | order: 2 1028 | order: 3 1029 | order: 1 1030 | } 1031 | } 1032 | layer { 1033 | name: "conv4_3_norm_mbox_loc_flat" 1034 | type: "Flatten" 1035 | bottom: "conv4_3_norm_mbox_loc_perm" 1036 | top: "conv4_3_norm_mbox_loc_flat" 1037 | flatten_param { 1038 | axis: 1 1039 | } 1040 | } 1041 | layer { 1042 | name: "conv4_3_norm_mbox_conf" 1043 | type: "Convolution" 1044 | bottom: "conv4_3_norm" 1045 | top: "conv4_3_norm_mbox_conf" 1046 | param { 1047 | lr_mult: 1 1048 | decay_mult: 1 1049 | } 1050 | param { 1051 | lr_mult: 2 1052 | decay_mult: 0 1053 | } 1054 | convolution_param { 1055 | num_output: 8 # 84 1056 | pad: 1 1057 | kernel_size: 3 1058 | stride: 1 1059 | weight_filler { 1060 | type: "xavier" 1061 | } 1062 | bias_filler { 1063 | type: "constant" 1064 | value: 0 1065 | } 1066 | } 1067 | } 1068 | layer { 1069 | name: "conv4_3_norm_mbox_conf_perm" 1070 | type: "Permute" 1071 | bottom: "conv4_3_norm_mbox_conf" 1072 | top: "conv4_3_norm_mbox_conf_perm" 1073 | permute_param { 1074 | order: 0 1075 | order: 2 1076 | order: 3 1077 | order: 1 1078 | } 1079 | } 1080 | layer { 1081 | name: "conv4_3_norm_mbox_conf_flat" 1082 | type: "Flatten" 1083 | bottom: "conv4_3_norm_mbox_conf_perm" 1084 | top: "conv4_3_norm_mbox_conf_flat" 1085 | flatten_param { 1086 | axis: 1 1087 | } 1088 | } 1089 | layer { 1090 | name: "conv4_3_norm_mbox_priorbox" 1091 | type: "PriorBox" 1092 | bottom: "conv4_3_norm" 1093 | bottom: "data" 1094 | top: "conv4_3_norm_mbox_priorbox" 1095 | prior_box_param { 1096 | min_size: 30.0 1097 | max_size: 60.0 1098 | aspect_ratio: 2 1099 | flip: true 1100 | clip: false 1101 | variance: 0.1 1102 | variance: 0.1 1103 | variance: 0.2 1104 | variance: 0.2 1105 | step: 8 1106 | offset: 0.5 1107 | } 1108 | } 1109 | layer { 1110 | name: "fc7_mbox_loc" 1111 | type: "Convolution" 1112 | bottom: "fc7" 1113 | top: "fc7_mbox_loc" 1114 | param { 1115 | lr_mult: 1 1116 | decay_mult: 1 1117 | } 1118 | param { 1119 | lr_mult: 2 1120 | decay_mult: 0 1121 | } 1122 | convolution_param { 1123 | num_output: 24 1124 | pad: 1 1125 | kernel_size: 3 1126 | stride: 1 1127 | weight_filler { 1128 | type: "xavier" 1129 | } 1130 | bias_filler { 1131 | type: "constant" 1132 | value: 0 1133 | } 1134 | } 1135 | } 1136 | layer { 1137 | name: "fc7_mbox_loc_perm" 1138 | type: "Permute" 1139 | bottom: "fc7_mbox_loc" 1140 | top: "fc7_mbox_loc_perm" 1141 | permute_param { 1142 | order: 0 1143 | order: 2 1144 | order: 3 1145 | order: 1 1146 | } 1147 | } 1148 | layer { 1149 | name: "fc7_mbox_loc_flat" 1150 | type: "Flatten" 1151 | bottom: "fc7_mbox_loc_perm" 1152 | top: "fc7_mbox_loc_flat" 1153 | flatten_param { 1154 | axis: 1 1155 | } 1156 | } 1157 | layer { 1158 | name: "fc7_mbox_conf" 1159 | type: "Convolution" 1160 | bottom: "fc7" 1161 | top: "fc7_mbox_conf" 1162 | param { 1163 | lr_mult: 1 1164 | decay_mult: 1 1165 | } 1166 | param { 1167 | lr_mult: 2 1168 | decay_mult: 0 1169 | } 1170 | convolution_param { 1171 | num_output: 12 # 126 1172 | pad: 1 1173 | kernel_size: 3 1174 | stride: 1 1175 | weight_filler { 1176 | type: "xavier" 1177 | } 1178 | bias_filler { 1179 | type: "constant" 1180 | value: 0 1181 | } 1182 | } 1183 | } 1184 | layer { 1185 | name: "fc7_mbox_conf_perm" 1186 | type: "Permute" 1187 | bottom: "fc7_mbox_conf" 1188 | top: "fc7_mbox_conf_perm" 1189 | permute_param { 1190 | order: 0 1191 | order: 2 1192 | order: 3 1193 | order: 1 1194 | } 1195 | } 1196 | layer { 1197 | name: "fc7_mbox_conf_flat" 1198 | type: "Flatten" 1199 | bottom: "fc7_mbox_conf_perm" 1200 | top: "fc7_mbox_conf_flat" 1201 | flatten_param { 1202 | axis: 1 1203 | } 1204 | } 1205 | layer { 1206 | name: "fc7_mbox_priorbox" 1207 | type: "PriorBox" 1208 | bottom: "fc7" 1209 | bottom: "data" 1210 | top: "fc7_mbox_priorbox" 1211 | prior_box_param { 1212 | min_size: 60.0 1213 | max_size: 111.0 1214 | aspect_ratio: 2 1215 | aspect_ratio: 3 1216 | flip: true 1217 | clip: false 1218 | variance: 0.1 1219 | variance: 0.1 1220 | variance: 0.2 1221 | variance: 0.2 1222 | step: 16 1223 | offset: 0.5 1224 | } 1225 | } 1226 | layer { 1227 | name: "conv6_2_mbox_loc" 1228 | type: "Convolution" 1229 | bottom: "conv6_2_h" 1230 | top: "conv6_2_mbox_loc" 1231 | param { 1232 | lr_mult: 1 1233 | decay_mult: 1 1234 | } 1235 | param { 1236 | lr_mult: 2 1237 | decay_mult: 0 1238 | } 1239 | convolution_param { 1240 | num_output: 24 1241 | pad: 1 1242 | kernel_size: 3 1243 | stride: 1 1244 | weight_filler { 1245 | type: "xavier" 1246 | } 1247 | bias_filler { 1248 | type: "constant" 1249 | value: 0 1250 | } 1251 | } 1252 | } 1253 | layer { 1254 | name: "conv6_2_mbox_loc_perm" 1255 | type: "Permute" 1256 | bottom: "conv6_2_mbox_loc" 1257 | top: "conv6_2_mbox_loc_perm" 1258 | permute_param { 1259 | order: 0 1260 | order: 2 1261 | order: 3 1262 | order: 1 1263 | } 1264 | } 1265 | layer { 1266 | name: "conv6_2_mbox_loc_flat" 1267 | type: "Flatten" 1268 | bottom: "conv6_2_mbox_loc_perm" 1269 | top: "conv6_2_mbox_loc_flat" 1270 | flatten_param { 1271 | axis: 1 1272 | } 1273 | } 1274 | layer { 1275 | name: "conv6_2_mbox_conf" 1276 | type: "Convolution" 1277 | bottom: "conv6_2_h" 1278 | top: "conv6_2_mbox_conf" 1279 | param { 1280 | lr_mult: 1 1281 | decay_mult: 1 1282 | } 1283 | param { 1284 | lr_mult: 2 1285 | decay_mult: 0 1286 | } 1287 | convolution_param { 1288 | num_output: 12 # 126 1289 | pad: 1 1290 | kernel_size: 3 1291 | stride: 1 1292 | weight_filler { 1293 | type: "xavier" 1294 | } 1295 | bias_filler { 1296 | type: "constant" 1297 | value: 0 1298 | } 1299 | } 1300 | } 1301 | layer { 1302 | name: "conv6_2_mbox_conf_perm" 1303 | type: "Permute" 1304 | bottom: "conv6_2_mbox_conf" 1305 | top: "conv6_2_mbox_conf_perm" 1306 | permute_param { 1307 | order: 0 1308 | order: 2 1309 | order: 3 1310 | order: 1 1311 | } 1312 | } 1313 | layer { 1314 | name: "conv6_2_mbox_conf_flat" 1315 | type: "Flatten" 1316 | bottom: "conv6_2_mbox_conf_perm" 1317 | top: "conv6_2_mbox_conf_flat" 1318 | flatten_param { 1319 | axis: 1 1320 | } 1321 | } 1322 | layer { 1323 | name: "conv6_2_mbox_priorbox" 1324 | type: "PriorBox" 1325 | bottom: "conv6_2_h" 1326 | bottom: "data" 1327 | top: "conv6_2_mbox_priorbox" 1328 | prior_box_param { 1329 | min_size: 111.0 1330 | max_size: 162.0 1331 | aspect_ratio: 2 1332 | aspect_ratio: 3 1333 | flip: true 1334 | clip: false 1335 | variance: 0.1 1336 | variance: 0.1 1337 | variance: 0.2 1338 | variance: 0.2 1339 | step: 32 1340 | offset: 0.5 1341 | } 1342 | } 1343 | layer { 1344 | name: "conv7_2_mbox_loc" 1345 | type: "Convolution" 1346 | bottom: "conv7_2_h" 1347 | top: "conv7_2_mbox_loc" 1348 | param { 1349 | lr_mult: 1 1350 | decay_mult: 1 1351 | } 1352 | param { 1353 | lr_mult: 2 1354 | decay_mult: 0 1355 | } 1356 | convolution_param { 1357 | num_output: 24 1358 | pad: 1 1359 | kernel_size: 3 1360 | stride: 1 1361 | weight_filler { 1362 | type: "xavier" 1363 | } 1364 | bias_filler { 1365 | type: "constant" 1366 | value: 0 1367 | } 1368 | } 1369 | } 1370 | layer { 1371 | name: "conv7_2_mbox_loc_perm" 1372 | type: "Permute" 1373 | bottom: "conv7_2_mbox_loc" 1374 | top: "conv7_2_mbox_loc_perm" 1375 | permute_param { 1376 | order: 0 1377 | order: 2 1378 | order: 3 1379 | order: 1 1380 | } 1381 | } 1382 | layer { 1383 | name: "conv7_2_mbox_loc_flat" 1384 | type: "Flatten" 1385 | bottom: "conv7_2_mbox_loc_perm" 1386 | top: "conv7_2_mbox_loc_flat" 1387 | flatten_param { 1388 | axis: 1 1389 | } 1390 | } 1391 | layer { 1392 | name: "conv7_2_mbox_conf" 1393 | type: "Convolution" 1394 | bottom: "conv7_2_h" 1395 | top: "conv7_2_mbox_conf" 1396 | param { 1397 | lr_mult: 1 1398 | decay_mult: 1 1399 | } 1400 | param { 1401 | lr_mult: 2 1402 | decay_mult: 0 1403 | } 1404 | convolution_param { 1405 | num_output: 12 # 126 1406 | pad: 1 1407 | kernel_size: 3 1408 | stride: 1 1409 | weight_filler { 1410 | type: "xavier" 1411 | } 1412 | bias_filler { 1413 | type: "constant" 1414 | value: 0 1415 | } 1416 | } 1417 | } 1418 | layer { 1419 | name: "conv7_2_mbox_conf_perm" 1420 | type: "Permute" 1421 | bottom: "conv7_2_mbox_conf" 1422 | top: "conv7_2_mbox_conf_perm" 1423 | permute_param { 1424 | order: 0 1425 | order: 2 1426 | order: 3 1427 | order: 1 1428 | } 1429 | } 1430 | layer { 1431 | name: "conv7_2_mbox_conf_flat" 1432 | type: "Flatten" 1433 | bottom: "conv7_2_mbox_conf_perm" 1434 | top: "conv7_2_mbox_conf_flat" 1435 | flatten_param { 1436 | axis: 1 1437 | } 1438 | } 1439 | layer { 1440 | name: "conv7_2_mbox_priorbox" 1441 | type: "PriorBox" 1442 | bottom: "conv7_2_h" 1443 | bottom: "data" 1444 | top: "conv7_2_mbox_priorbox" 1445 | prior_box_param { 1446 | min_size: 162.0 1447 | max_size: 213.0 1448 | aspect_ratio: 2 1449 | aspect_ratio: 3 1450 | flip: true 1451 | clip: false 1452 | variance: 0.1 1453 | variance: 0.1 1454 | variance: 0.2 1455 | variance: 0.2 1456 | step: 64 1457 | offset: 0.5 1458 | } 1459 | } 1460 | layer { 1461 | name: "conv8_2_mbox_loc" 1462 | type: "Convolution" 1463 | bottom: "conv8_2_h" 1464 | top: "conv8_2_mbox_loc" 1465 | param { 1466 | lr_mult: 1 1467 | decay_mult: 1 1468 | } 1469 | param { 1470 | lr_mult: 2 1471 | decay_mult: 0 1472 | } 1473 | convolution_param { 1474 | num_output: 16 1475 | pad: 1 1476 | kernel_size: 3 1477 | stride: 1 1478 | weight_filler { 1479 | type: "xavier" 1480 | } 1481 | bias_filler { 1482 | type: "constant" 1483 | value: 0 1484 | } 1485 | } 1486 | } 1487 | layer { 1488 | name: "conv8_2_mbox_loc_perm" 1489 | type: "Permute" 1490 | bottom: "conv8_2_mbox_loc" 1491 | top: "conv8_2_mbox_loc_perm" 1492 | permute_param { 1493 | order: 0 1494 | order: 2 1495 | order: 3 1496 | order: 1 1497 | } 1498 | } 1499 | layer { 1500 | name: "conv8_2_mbox_loc_flat" 1501 | type: "Flatten" 1502 | bottom: "conv8_2_mbox_loc_perm" 1503 | top: "conv8_2_mbox_loc_flat" 1504 | flatten_param { 1505 | axis: 1 1506 | } 1507 | } 1508 | layer { 1509 | name: "conv8_2_mbox_conf" 1510 | type: "Convolution" 1511 | bottom: "conv8_2_h" 1512 | top: "conv8_2_mbox_conf" 1513 | param { 1514 | lr_mult: 1 1515 | decay_mult: 1 1516 | } 1517 | param { 1518 | lr_mult: 2 1519 | decay_mult: 0 1520 | } 1521 | convolution_param { 1522 | num_output: 8 # 84 1523 | pad: 1 1524 | kernel_size: 3 1525 | stride: 1 1526 | weight_filler { 1527 | type: "xavier" 1528 | } 1529 | bias_filler { 1530 | type: "constant" 1531 | value: 0 1532 | } 1533 | } 1534 | } 1535 | layer { 1536 | name: "conv8_2_mbox_conf_perm" 1537 | type: "Permute" 1538 | bottom: "conv8_2_mbox_conf" 1539 | top: "conv8_2_mbox_conf_perm" 1540 | permute_param { 1541 | order: 0 1542 | order: 2 1543 | order: 3 1544 | order: 1 1545 | } 1546 | } 1547 | layer { 1548 | name: "conv8_2_mbox_conf_flat" 1549 | type: "Flatten" 1550 | bottom: "conv8_2_mbox_conf_perm" 1551 | top: "conv8_2_mbox_conf_flat" 1552 | flatten_param { 1553 | axis: 1 1554 | } 1555 | } 1556 | layer { 1557 | name: "conv8_2_mbox_priorbox" 1558 | type: "PriorBox" 1559 | bottom: "conv8_2_h" 1560 | bottom: "data" 1561 | top: "conv8_2_mbox_priorbox" 1562 | prior_box_param { 1563 | min_size: 213.0 1564 | max_size: 264.0 1565 | aspect_ratio: 2 1566 | flip: true 1567 | clip: false 1568 | variance: 0.1 1569 | variance: 0.1 1570 | variance: 0.2 1571 | variance: 0.2 1572 | step: 100 1573 | offset: 0.5 1574 | } 1575 | } 1576 | layer { 1577 | name: "conv9_2_mbox_loc" 1578 | type: "Convolution" 1579 | bottom: "conv9_2_h" 1580 | top: "conv9_2_mbox_loc" 1581 | param { 1582 | lr_mult: 1 1583 | decay_mult: 1 1584 | } 1585 | param { 1586 | lr_mult: 2 1587 | decay_mult: 0 1588 | } 1589 | convolution_param { 1590 | num_output: 16 1591 | pad: 1 1592 | kernel_size: 3 1593 | stride: 1 1594 | weight_filler { 1595 | type: "xavier" 1596 | } 1597 | bias_filler { 1598 | type: "constant" 1599 | value: 0 1600 | } 1601 | } 1602 | } 1603 | layer { 1604 | name: "conv9_2_mbox_loc_perm" 1605 | type: "Permute" 1606 | bottom: "conv9_2_mbox_loc" 1607 | top: "conv9_2_mbox_loc_perm" 1608 | permute_param { 1609 | order: 0 1610 | order: 2 1611 | order: 3 1612 | order: 1 1613 | } 1614 | } 1615 | layer { 1616 | name: "conv9_2_mbox_loc_flat" 1617 | type: "Flatten" 1618 | bottom: "conv9_2_mbox_loc_perm" 1619 | top: "conv9_2_mbox_loc_flat" 1620 | flatten_param { 1621 | axis: 1 1622 | } 1623 | } 1624 | layer { 1625 | name: "conv9_2_mbox_conf" 1626 | type: "Convolution" 1627 | bottom: "conv9_2_h" 1628 | top: "conv9_2_mbox_conf" 1629 | param { 1630 | lr_mult: 1 1631 | decay_mult: 1 1632 | } 1633 | param { 1634 | lr_mult: 2 1635 | decay_mult: 0 1636 | } 1637 | convolution_param { 1638 | num_output: 8 # 84 1639 | pad: 1 1640 | kernel_size: 3 1641 | stride: 1 1642 | weight_filler { 1643 | type: "xavier" 1644 | } 1645 | bias_filler { 1646 | type: "constant" 1647 | value: 0 1648 | } 1649 | } 1650 | } 1651 | layer { 1652 | name: "conv9_2_mbox_conf_perm" 1653 | type: "Permute" 1654 | bottom: "conv9_2_mbox_conf" 1655 | top: "conv9_2_mbox_conf_perm" 1656 | permute_param { 1657 | order: 0 1658 | order: 2 1659 | order: 3 1660 | order: 1 1661 | } 1662 | } 1663 | layer { 1664 | name: "conv9_2_mbox_conf_flat" 1665 | type: "Flatten" 1666 | bottom: "conv9_2_mbox_conf_perm" 1667 | top: "conv9_2_mbox_conf_flat" 1668 | flatten_param { 1669 | axis: 1 1670 | } 1671 | } 1672 | layer { 1673 | name: "conv9_2_mbox_priorbox" 1674 | type: "PriorBox" 1675 | bottom: "conv9_2_h" 1676 | bottom: "data" 1677 | top: "conv9_2_mbox_priorbox" 1678 | prior_box_param { 1679 | min_size: 264.0 1680 | max_size: 315.0 1681 | aspect_ratio: 2 1682 | flip: true 1683 | clip: false 1684 | variance: 0.1 1685 | variance: 0.1 1686 | variance: 0.2 1687 | variance: 0.2 1688 | step: 300 1689 | offset: 0.5 1690 | } 1691 | } 1692 | layer { 1693 | name: "mbox_loc" 1694 | type: "Concat" 1695 | bottom: "conv4_3_norm_mbox_loc_flat" 1696 | bottom: "fc7_mbox_loc_flat" 1697 | bottom: "conv6_2_mbox_loc_flat" 1698 | bottom: "conv7_2_mbox_loc_flat" 1699 | bottom: "conv8_2_mbox_loc_flat" 1700 | bottom: "conv9_2_mbox_loc_flat" 1701 | top: "mbox_loc" 1702 | concat_param { 1703 | axis: 1 1704 | } 1705 | } 1706 | layer { 1707 | name: "mbox_conf" 1708 | type: "Concat" 1709 | bottom: "conv4_3_norm_mbox_conf_flat" 1710 | bottom: "fc7_mbox_conf_flat" 1711 | bottom: "conv6_2_mbox_conf_flat" 1712 | bottom: "conv7_2_mbox_conf_flat" 1713 | bottom: "conv8_2_mbox_conf_flat" 1714 | bottom: "conv9_2_mbox_conf_flat" 1715 | top: "mbox_conf" 1716 | concat_param { 1717 | axis: 1 1718 | } 1719 | } 1720 | layer { 1721 | name: "mbox_priorbox" 1722 | type: "Concat" 1723 | bottom: "conv4_3_norm_mbox_priorbox" 1724 | bottom: "fc7_mbox_priorbox" 1725 | bottom: "conv6_2_mbox_priorbox" 1726 | bottom: "conv7_2_mbox_priorbox" 1727 | bottom: "conv8_2_mbox_priorbox" 1728 | bottom: "conv9_2_mbox_priorbox" 1729 | top: "mbox_priorbox" 1730 | concat_param { 1731 | axis: 2 1732 | } 1733 | } 1734 | 1735 | layer { 1736 | name: "mbox_conf_reshape" 1737 | type: "Reshape" 1738 | bottom: "mbox_conf" 1739 | top: "mbox_conf_reshape" 1740 | reshape_param { 1741 | shape { 1742 | dim: 0 1743 | dim: -1 1744 | dim: 2 1745 | } 1746 | } 1747 | } 1748 | layer { 1749 | name: "mbox_conf_softmax" 1750 | type: "Softmax" 1751 | bottom: "mbox_conf_reshape" 1752 | top: "mbox_conf_softmax" 1753 | softmax_param { 1754 | axis: 2 1755 | } 1756 | } 1757 | layer { 1758 | name: "mbox_conf_flatten" 1759 | type: "Flatten" 1760 | bottom: "mbox_conf_softmax" 1761 | top: "mbox_conf_flatten" 1762 | flatten_param { 1763 | axis: 1 1764 | } 1765 | } 1766 | 1767 | layer { 1768 | name: "detection_out" 1769 | type: "DetectionOutput" 1770 | bottom: "mbox_loc" 1771 | bottom: "mbox_conf_flatten" 1772 | bottom: "mbox_priorbox" 1773 | top: "detection_out" 1774 | include { 1775 | phase: TEST 1776 | } 1777 | detection_output_param { 1778 | num_classes: 2 1779 | share_location: true 1780 | background_label_id: 0 1781 | nms_param { 1782 | nms_threshold: 0.45 1783 | top_k: 400 1784 | } 1785 | code_type: CENTER_SIZE 1786 | keep_top_k: 200 1787 | confidence_threshold: 0.01 1788 | } 1789 | } 1790 | -------------------------------------------------------------------------------- /model_face_detection/model_architecture_visualization.py: -------------------------------------------------------------------------------- 1 | 2 | # 网络架构可视化 3 | 4 | """ 5 | 1. 6 | 将prototxt文件内容拷贝到网页中,shift+enter显示网络结构 7 | https://ethereon.github.io/netscope/#/editor 8 | 9 | 2. 10 | https://transcranial.github.io/keras-js/#/inception-v3 11 | 12 | 3. 13 | https://github.com/lutzroeder/Netron 14 | """ -------------------------------------------------------------------------------- /model_face_detection/res10_300x300_ssd_iter_140000.caffemodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/model_face_detection/res10_300x300_ssd_iter_140000.caffemodel -------------------------------------------------------------------------------- /model_face_detection/resnet-ssd-deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/model_face_detection/resnet-ssd-deploy.png -------------------------------------------------------------------------------- /model_face_detection/resnet-ssd-deploy2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/model_face_detection/resnet-ssd-deploy2.png -------------------------------------------------------------------------------- /model_facenet/openface_nn4.small2.v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/model_facenet/openface_nn4.small2.v1.png -------------------------------------------------------------------------------- /model_facenet/openface_nn4.small2.v1.t7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/model_facenet/openface_nn4.small2.v1.t7 -------------------------------------------------------------------------------- /mysql_table/facerecognition.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : MySQL 5 | Source Server Type : MySQL 6 | Source Server Version : 80012 7 | Source Host : localhost:3306 8 | Source Schema : facerecognition 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80012 12 | File Encoding : 65001 13 | 14 | Date: 27/05/2021 13:55:36 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for checkin 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `checkin`; 24 | CREATE TABLE `checkin` ( 25 | `Name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 26 | `ID` int(11) NULL DEFAULT NULL, 27 | `Class` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 28 | `Time` datetime NULL DEFAULT NULL, 29 | `Description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL 30 | ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 31 | 32 | -- ---------------------------- 33 | -- Records of checkin 34 | -- ---------------------------- 35 | INSERT INTO `checkin` VALUES ('Messi', 10, '2020001', '2020-05-25 00:20:27', '迟到'); 36 | INSERT INTO `checkin` VALUES ('Messi', 10, '2020001', '2021-05-27 01:20:59', '旷课'); 37 | INSERT INTO `checkin` VALUES ('Messi', 10, '2020001', '2021-05-27 03:35:03', '旷课'); 38 | INSERT INTO `checkin` VALUES ('Messi', 10, '2020001', '2021-05-27 08:58:47', '旷课'); 39 | INSERT INTO `checkin` VALUES ('Messi', 10, '2020001', '2021-05-27 10:19:34', '旷课'); 40 | 41 | -- ---------------------------- 42 | -- Table structure for studentnums 43 | -- ---------------------------- 44 | DROP TABLE IF EXISTS `studentnums`; 45 | CREATE TABLE `studentnums` ( 46 | `Class` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 47 | `Num` int(11) NULL DEFAULT NULL 48 | ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 49 | 50 | -- ---------------------------- 51 | -- Records of studentnums 52 | -- ---------------------------- 53 | INSERT INTO `studentnums` VALUES ('2020001', 23); 54 | INSERT INTO `studentnums` VALUES ('2020002', 24); 55 | 56 | -- ---------------------------- 57 | -- Table structure for students 58 | -- ---------------------------- 59 | DROP TABLE IF EXISTS `students`; 60 | CREATE TABLE `students` ( 61 | `ID` int(11) NOT NULL, 62 | `Name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 63 | `Class` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 64 | `Sex` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 65 | `Birthday` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 66 | PRIMARY KEY (`ID`) USING BTREE 67 | ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 68 | 69 | -- ---------------------------- 70 | -- Records of students 71 | -- ---------------------------- 72 | INSERT INTO `students` VALUES (1, 'TerStegen', '2020001', 'male', '19920430'); 73 | INSERT INTO `students` VALUES (2, 'Dest', '2020002', 'male', '20001103'); 74 | INSERT INTO `students` VALUES (3, 'Pique', '2020001', 'male', '19870202'); 75 | INSERT INTO `students` VALUES (4, 'Araújo', '2020002', 'male', '19990307'); 76 | INSERT INTO `students` VALUES (5, 'Busquets', '2020001', 'male', '19880716'); 77 | INSERT INTO `students` VALUES (6, 'Wijnaldum', '2020002', 'male', '19901111'); 78 | INSERT INTO `students` VALUES (7, 'Griezmann', '2020001', 'male', '19910321'); 79 | INSERT INTO `students` VALUES (9, 'Kun', '2020002', 'male', '19880602'); 80 | INSERT INTO `students` VALUES (10, 'Messi', '2020001', 'male', '19870624'); 81 | INSERT INTO `students` VALUES (11, 'Dembélé', '2020001', 'male', '19970515'); 82 | INSERT INTO `students` VALUES (12, 'Depay', '2020002', 'male', '19940213'); 83 | INSERT INTO `students` VALUES (13, 'Ignacio', '2020002', 'male', '19990302'); 84 | INSERT INTO `students` VALUES (14, 'Coutinho', '2020001', 'male', '19920612'); 85 | INSERT INTO `students` VALUES (15, 'Lenglet', '2020002', 'male', '19950617'); 86 | INSERT INTO `students` VALUES (16, 'Pedri', '2020001', 'male', '20021125'); 87 | INSERT INTO `students` VALUES (17, 'Emerson ', '2020001', 'male', '19990114'); 88 | INSERT INTO `students` VALUES (18, 'Alba', '2020001', 'male', '19890321'); 89 | INSERT INTO `students` VALUES (21, 'Frenkie', '2020001', 'male', '19970512'); 90 | INSERT INTO `students` VALUES (22, 'AnsuFati', '2020002', 'male', '20021031'); 91 | INSERT INTO `students` VALUES (23, ' Garcia', '2020002', 'male', '20010109'); 92 | INSERT INTO `students` VALUES (27, 'Moriba', '2020002', 'male', '20030119'); 93 | 94 | SET FOREIGN_KEY_CHECKS = 1; 95 | 96 | -- Table structure for detail 97 | -- ---------------------------- 98 | DROP TABLE IF EXISTS `detail`; 99 | CREATE TABLE `studentnums` ( 100 | `ID` int(11) NOT NULL, 101 | `Name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 102 | `Emo` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 103 | `Time` datetime NULL DEFAULT NULL, 104 | ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 105 | 106 | -- ---------------------------- 107 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/requirements.txt -------------------------------------------------------------------------------- /saved_weights/embeddings.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/saved_weights/embeddings.pickle -------------------------------------------------------------------------------- /saved_weights/le.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/saved_weights/le.pickle -------------------------------------------------------------------------------- /saved_weights/recognizer.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/saved_weights/recognizer.pickle -------------------------------------------------------------------------------- /ui/RandomCheckUI.py: -------------------------------------------------------------------------------- 1 | from PyQt5 import QtCore, QtGui, QtWidgets 2 | 3 | 4 | class Ui_Form(object): 5 | def setupUi(self, Form): 6 | Form.setObjectName("Form") 7 | Form.resize(480, 232) 8 | self.pb_success = QtWidgets.QPushButton(Form) 9 | self.pb_success.setGeometry(QtCore.QRect(30, 160, 101, 41)) 10 | self.pb_success.setObjectName("pb_success") # 成功回答 11 | self.label = QtWidgets.QLabel(Form) # 姓名 12 | self.label.setGeometry(QtCore.QRect(220, 90, 51, 41)) 13 | self.label.setObjectName("label") 14 | self.pb_fail = QtWidgets.QPushButton(Form) 15 | self.pb_fail.setGeometry(QtCore.QRect(200, 160, 101, 41)) 16 | self.pb_fail.setObjectName("pb_fail") # 回答失败 17 | self.label_id = QtWidgets.QLabel(Form) # 学号 18 | self.label_id.setGeometry(QtCore.QRect(30, 90, 51, 41)) 19 | self.label_id.setObjectName("label_id") 20 | self.pb_other = QtWidgets.QPushButton(Form) # 退出 21 | self.pb_other.setGeometry(QtCore.QRect(370, 160, 81, 41)) 22 | self.pb_other.setObjectName("pb_other") 23 | self.lcdNumber_id = QtWidgets.QLCDNumber(Form) 24 | self.lcdNumber_id.setGeometry(QtCore.QRect(100, 90, 101, 41)) # 学号输出框 25 | self.lcdNumber_id.setObjectName("lcdNumber_id") 26 | self.pb_start = QtWidgets.QPushButton(Form) # 开始筛选幸运观众 27 | self.pb_start.setGeometry(QtCore.QRect(180, 20, 271, 41)) 28 | self.pb_start.setObjectName("pb_start") 29 | self.pb_connect_db = QtWidgets.QPushButton(Form) # 连接数据库 30 | self.pb_connect_db.setGeometry(QtCore.QRect(30, 20, 131, 41)) 31 | self.pb_connect_db.setObjectName("pb_connect_db") 32 | self.textBrowser_name = QtWidgets.QTextBrowser(Form) 33 | self.textBrowser_name.setGeometry(QtCore.QRect(280, 90, 181, 41)) # 姓名输出框 34 | self.textBrowser_name.setObjectName("textBrowser_name") 35 | 36 | self.retranslateUi(Form) 37 | QtCore.QMetaObject.connectSlotsByName(Form) 38 | 39 | def retranslateUi(self, Form): 40 | _translate = QtCore.QCoreApplication.translate 41 | Form.setWindowTitle(_translate("Form", "Form")) 42 | self.pb_success.setText(_translate("Form", "成功回答")) 43 | self.label.setText(_translate("Form", "姓名")) 44 | self.pb_fail.setText(_translate("Form", "答题失败")) 45 | self.label_id.setText(_translate("Form", "学号")) 46 | self.pb_other.setText(_translate("Form", "退出")) 47 | self.pb_start.setText(_translate("Form", "开始筛选幸运观众")) 48 | self.pb_connect_db.setText(_translate("Form", "连接数据库")) 49 | 50 | 51 | if __name__ == "__main__": 52 | import sys 53 | app = QtWidgets.QApplication(sys.argv) 54 | Form = QtWidgets.QWidget() 55 | ui = Ui_Form() 56 | ui.setupUi(Form) 57 | Form.show() 58 | sys.exit(app.exec_()) 59 | -------------------------------------------------------------------------------- /ui/RandomCheckUI.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 480 10 | 232 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 30 20 | 160 21 | 101 22 | 41 23 | 24 | 25 | 26 | 成功回答 27 | 28 | 29 | 30 | 31 | 32 | 270 33 | 90 34 | 31 35 | 41 36 | 37 | 38 | 39 | 姓名 40 | 41 | 42 | 43 | 44 | 45 | 150 46 | 160 47 | 101 48 | 41 49 | 50 | 51 | 52 | 答题失败 53 | 54 | 55 | 56 | 57 | 58 | 40 59 | 90 60 | 31 61 | 41 62 | 63 | 64 | 65 | 学号 66 | 67 | 68 | 69 | 70 | 71 | 370 72 | 160 73 | 81 74 | 41 75 | 76 | 77 | 78 | 其他 79 | 80 | 81 | 82 | 83 | 84 | 280 85 | 90 86 | 171 87 | 31 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 80 98 | 90 99 | 171 100 | 41 101 | 102 | 103 | 104 | 105 | 106 | 107 | 150 108 | 20 109 | 301 110 | 41 111 | 112 | 113 | 114 | 开始筛选幸运观众 115 | 116 | 117 | 118 | 119 | 120 | 30 121 | 20 122 | 101 123 | 41 124 | 125 | 126 | 127 | 连接数据库 128 | 129 | 130 | 131 | 132 | 133 | 310 134 | 90 135 | 141 136 | 41 137 | 138 | 139 | 140 | 141 | 142 | 143 | 270 144 | 160 145 | 81 146 | 41 147 | 148 | 149 | 150 | 未到 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /ui/__pycache__/RandomCheckUI.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/ui/__pycache__/RandomCheckUI.cpython-38.pyc -------------------------------------------------------------------------------- /ui/__pycache__/detail.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/ui/__pycache__/detail.cpython-38.pyc -------------------------------------------------------------------------------- /ui/__pycache__/infoUI.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/ui/__pycache__/infoUI.cpython-38.pyc -------------------------------------------------------------------------------- /ui/__pycache__/main.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/ui/__pycache__/main.cpython-36.pyc -------------------------------------------------------------------------------- /ui/__pycache__/main.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/ui/__pycache__/main.cpython-38.pyc -------------------------------------------------------------------------------- /ui/__pycache__/mainwindow.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/ui/__pycache__/mainwindow.cpython-38.pyc -------------------------------------------------------------------------------- /ui/detail.py: -------------------------------------------------------------------------------- 1 | from PyQt5 import QtCore, QtGui, QtWidgets 2 | 3 | 4 | class Ui_Form(object): 5 | def setupUi(self, Form): 6 | Form.setObjectName("Form") 7 | Form.resize(1060, 560) 8 | 9 | self.pb_other = QtWidgets.QPushButton(Form) # 查询详情 10 | self.pb_other.setGeometry(QtCore.QRect(240, 500, 101, 41)) 11 | self.pb_other.setObjectName("pb_other") 12 | self.pb_exit = QtWidgets.QPushButton(Form) # 退出 13 | self.pb_exit.setGeometry(QtCore.QRect(640, 500, 101, 41)) 14 | self.pb_exit.setObjectName("pb_exit") 15 | self.tableView = QtWidgets.QTableView(Form) # 查询数据库所有结果框 16 | self.tableView.setGeometry(QtCore.QRect(100, 30, 841, 441)) 17 | self.tableView.setObjectName("tableView") 18 | 19 | self.retranslateUi(Form) 20 | QtCore.QMetaObject.connectSlotsByName(Form) 21 | 22 | def retranslateUi(self, Form): 23 | _translate = QtCore.QCoreApplication.translate 24 | Form.setWindowTitle(_translate("Form", "Form")) 25 | self.pb_other.setText(_translate("Form", "刷新")) 26 | self.pb_exit.setText(_translate("Form", "退出")) 27 | 28 | 29 | if __name__ == "__main__": 30 | import sys 31 | app = QtWidgets.QApplication(sys.argv) 32 | Form = QtWidgets.QWidget() 33 | ui = Ui_Form() 34 | ui.setupUi(Form) 35 | Form.show() 36 | sys.exit(app.exec_()) 37 | -------------------------------------------------------------------------------- /ui/infoUI.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'InfoUI.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.4 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_Form(object): 15 | def setupUi(self, Form): 16 | Form.setObjectName("Form") 17 | Form.resize(760, 560) 18 | Form.setMinimumSize(QtCore.QSize(760, 560)) 19 | Form.setMaximumSize(QtCore.QSize(760, 560)) 20 | self.bt_start_collect = QtWidgets.QPushButton(Form) # 开始采集 21 | self.bt_start_collect.setGeometry(QtCore.QRect(10, 460, 131, 91)) 22 | self.bt_start_collect.setObjectName("bt_start_collect") 23 | self.label_title = QtWidgets.QLabel(Form) # 人脸识别个人信息采集系统 24 | self.label_title.setGeometry(QtCore.QRect(130, 10, 291, 20)) 25 | self.label_title.setObjectName("label_title") 26 | self.label_capture = QtWidgets.QLabel(Form) 27 | self.label_capture.setGeometry(QtCore.QRect(10, 40, 500, 400)) 28 | self.label_capture.setMinimumSize(QtCore.QSize(500, 400)) 29 | self.label_capture.setMaximumSize(QtCore.QSize(500, 400)) 30 | self.label_capture.setText("") 31 | self.label_capture.setObjectName("label_capture") 32 | self.bt_take_photo = QtWidgets.QPushButton(Form) # 拍照 33 | self.bt_take_photo.setGeometry(QtCore.QRect(150, 510, 151, 41)) 34 | self.bt_take_photo.setObjectName("bt_take_photo") 35 | self.bt_exit = QtWidgets.QPushButton(Form) # 拍照 36 | self.bt_exit.setGeometry(QtCore.QRect(350, 510, 151, 41)) 37 | self.bt_exit.setObjectName("bt_exit") 38 | self.tableView = QtWidgets.QTableView(Form) # 查询数据库所有结果框 39 | self.tableView.setGeometry(QtCore.QRect(530, 30, 221, 441)) 40 | self.tableView.setObjectName("tableView") 41 | self.lcdNumber_collection_nums = QtWidgets.QLCDNumber(Form) # 已采集数 42 | self.lcdNumber_collection_nums.setGeometry(QtCore.QRect(430, 470, 71, 31)) 43 | self.lcdNumber_collection_nums.setObjectName("lcdNumber_collection_nums") 44 | self.label_collected = QtWidgets.QLabel(Form) # 已采集 45 | self.label_collected.setGeometry(QtCore.QRect(350, 475, 81, 21)) 46 | self.label_collected.setObjectName("label_collected_num") 47 | self.label_collected_num = QtWidgets.QLabel(Form) # 采集数目 48 | self.label_collected_num.setGeometry(QtCore.QRect(140, 475, 101, 21)) 49 | self.label_collected_num.setObjectName("label_collected_num") 50 | self.spinBox_set_num = QtWidgets.QSpinBox(Form) # 采集数设置 51 | self.spinBox_set_num.setGeometry(QtCore.QRect(250, 471, 81, 31)) 52 | self.spinBox_set_num.setObjectName("spinBox_set_num") 53 | self.checkBox_auto_collect = QtWidgets.QCheckBox(Form) # 自动采集 54 | self.checkBox_auto_collect.setGeometry(QtCore.QRect(150, 450, 171, 21)) 55 | self.checkBox_auto_collect.setObjectName("checkBox_auto_collect") 56 | self.bt_check_dirs_faces = QtWidgets.QPushButton(Form) 57 | self.bt_check_dirs_faces.setGeometry(QtCore.QRect(530, 490, 221, 61)) 58 | self.bt_check_dirs_faces.setObjectName("bt_check_dirs_faces") 59 | self.bt_start_collect.raise_() 60 | self.label_title.raise_() 61 | self.label_capture.raise_() 62 | self.bt_take_photo.raise_() 63 | self.bt_exit.raise_() 64 | self.tableView.raise_() 65 | self.lcdNumber_collection_nums.raise_() 66 | self.label_collected.raise_() 67 | self.label_collected_num.raise_() 68 | self.spinBox_set_num.raise_() 69 | self.checkBox_auto_collect.raise_() 70 | self.bt_check_dirs_faces.raise_() 71 | 72 | self.retranslateUi(Form) 73 | QtCore.QMetaObject.connectSlotsByName(Form) 74 | # Form.setTabOrder控件顺序 75 | Form.setTabOrder(self.bt_start_collect, self.checkBox_auto_collect) 76 | Form.setTabOrder(self.checkBox_auto_collect, self.spinBox_set_num) 77 | Form.setTabOrder(self.spinBox_set_num, self.bt_take_photo) 78 | Form.setTabOrder(self.bt_take_photo, self.bt_exit) 79 | Form.setTabOrder(self.bt_exit, self.tableView) 80 | Form.setTabOrder(self.tableView, self.bt_check_dirs_faces) 81 | 82 | def retranslateUi(self, Form): 83 | _translate = QtCore.QCoreApplication.translate 84 | Form.setWindowTitle(_translate("Form", "Form")) 85 | self.bt_start_collect.setText(_translate("Form", "开始采集")) 86 | self.label_title.setText(_translate("Form", "人脸识别个人信息采集系统")) 87 | self.bt_take_photo.setText(_translate("Form", "拍照")) 88 | self.bt_exit.setText(_translate("Form", "退出")) 89 | self.label_collected.setText(_translate("Form", "已采集")) 90 | self.label_collected_num.setText(_translate("Form", "采集数目")) 91 | self.checkBox_auto_collect.setText(_translate("Form", "自动采集")) 92 | self.bt_check_dirs_faces.setText(_translate("Form", "查询所有已采集数据")) 93 | 94 | 95 | if __name__ == "__main__": 96 | import sys 97 | app = QtWidgets.QApplication(sys.argv) 98 | Form = QtWidgets.QWidget() 99 | ui = Ui_Form() 100 | ui.setupUi(Form) 101 | Form.show() 102 | sys.exit(app.exec_()) 103 | -------------------------------------------------------------------------------- /ui/infoUI.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Form 4 | 5 | 6 | 7 | 0 8 | 0 9 | 760 10 | 560 11 | 12 | 13 | 14 | 15 | 760 16 | 560 17 | 18 | 19 | 20 | 21 | 760 22 | 560 23 | 24 | 25 | 26 | Form 27 | 28 | 29 | 30 | 31 | 530 32 | 70 33 | 71 34 | 21 35 | 36 | 37 | 38 | 请输入姓名: 39 | 40 | 41 | 42 | 43 | 44 | 530 45 | 100 46 | 71 47 | 21 48 | 49 | 50 | 51 | 请输入班级: 52 | 53 | 54 | 55 | 56 | 57 | 530 58 | 40 59 | 71 60 | 21 61 | 62 | 63 | 64 | 请输入学号: 65 | 66 | 67 | 68 | 69 | 70 | 310 71 | 510 72 | 211 73 | 41 74 | 75 | 76 | 77 | 添加或修改信息(DB) 78 | 79 | 80 | 81 | 82 | 83 | 10 84 | 460 85 | 131 86 | 91 87 | 88 | 89 | 90 | 开始采集 91 | 92 | 93 | 94 | 95 | 96 | 170 97 | 10 98 | 251 99 | 16 100 | 101 | 102 | 103 | 人脸识别个人信息采集系统 104 | 105 | 106 | 107 | 108 | 109 | 310 110 | 460 111 | 101 112 | 41 113 | 114 | 115 | 116 | 查询信息(DB) 117 | 118 | 119 | 120 | 121 | 122 | 530 123 | 130 124 | 71 125 | 21 126 | 127 | 128 | 129 | 请输入性别: 130 | 131 | 132 | 133 | 134 | 135 | 530 136 | 160 137 | 71 138 | 21 139 | 140 | 141 | 142 | 请输入生日: 143 | 144 | 145 | 146 | 147 | 148 | 10 149 | 40 150 | 500 151 | 400 152 | 153 | 154 | 155 | 156 | 500 157 | 400 158 | 159 | 160 | 161 | 162 | 500 163 | 400 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 150 174 | 510 175 | 151 176 | 41 177 | 178 | 179 | 180 | 拍照 181 | 182 | 183 | 184 | 185 | 186 | 610 187 | 40 188 | 141 189 | 21 190 | 191 | 192 | 193 | 194 | 195 | 196 | 610 197 | 70 198 | 141 199 | 21 200 | 201 | 202 | 203 | 204 | 205 | 206 | 610 207 | 100 208 | 141 209 | 21 210 | 211 | 212 | 213 | 214 | 215 | 216 | 610 217 | 130 218 | 141 219 | 21 220 | 221 | 222 | 223 | 224 | 225 | 226 | 610 227 | 160 228 | 141 229 | 21 230 | 231 | 232 | 233 | 234 | 235 | 236 | 530 237 | 190 238 | 221 239 | 291 240 | 241 | 242 | 243 | 244 | 245 | 246 | 230 247 | 470 248 | 71 249 | 31 250 | 251 | 252 | 253 | 254 | 255 | 256 | 260 257 | 450 258 | 41 259 | 21 260 | 261 | 262 | 263 | 已采集 264 | 265 | 266 | 267 | 268 | 269 | 150 270 | 471 271 | 71 272 | 31 273 | 274 | 275 | 276 | 277 | 278 | 279 | 150 280 | 450 281 | 71 282 | 21 283 | 284 | 285 | 286 | 自动采集 287 | 288 | 289 | 290 | 291 | 292 | 420 293 | 460 294 | 101 295 | 41 296 | 297 | 298 | 299 | 查询人脸数量 300 | 301 | 302 | 303 | 304 | 305 | 530 306 | 490 307 | 221 308 | 61 309 | 310 | 311 | 312 | 查询数据中所有人脸数量 313 | 314 | 315 | label_name 316 | label_class 317 | label_id 318 | bt_start_collect 319 | label_title 320 | bt_check_info 321 | label_sex 322 | label_birth 323 | bt_change_info 324 | label_capture 325 | bt_take_photo 326 | lineEdit_id 327 | lineEdit_name 328 | lineEdit_class 329 | lineEdit_sex 330 | lineEdit_birth 331 | tableView 332 | lcdNumber_collection_nums 333 | label_collected 334 | spinBox_set_num 335 | checkBox_auto_collect 336 | bt_check_id_faces 337 | bt_check_dirs_faces 338 | 339 | 340 | bt_start_collect 341 | checkBox_auto_collect 342 | spinBox_set_num 343 | bt_take_photo 344 | lineEdit_id 345 | lineEdit_name 346 | lineEdit_class 347 | lineEdit_sex 348 | lineEdit_birth 349 | bt_check_info 350 | bt_change_info 351 | bt_check_id_faces 352 | tableView 353 | bt_check_dirs_faces 354 | 355 | 356 | 357 | 358 | -------------------------------------------------------------------------------- /ui/mainwindow.py: -------------------------------------------------------------------------------- 1 | from PyQt5 import QtCore, QtGui, QtWidgets 2 | 3 | 4 | class Ui_MainWindow(object): 5 | def setupUi(self, MainWindow): 6 | MainWindow.setObjectName("MainWindow") 7 | MainWindow.resize(1086, 907) 8 | self.centralwidget = QtWidgets.QWidget(MainWindow) 9 | self.centralwidget.setObjectName("centralwidget") 10 | self.label_camera = QtWidgets.QLabel(self.centralwidget) # 未打开相机的黑色界面 11 | self.label_camera.setGeometry(QtCore.QRect(30, 80, 800, 600)) 12 | self.label_camera.setText("") 13 | self.label_camera.setObjectName("label_camera") 14 | self.label_time = QtWidgets.QLabel(self.centralwidget) # 显示时间 15 | self.label_time.setGeometry(QtCore.QRect(850, 20, 231, 30)) 16 | self.label_time.setText("") 17 | self.label_time.setObjectName("label_time") 18 | self.bt_random_check = QtWidgets.QPushButton(self.centralwidget) # 随机抽签 19 | self.bt_random_check.setGeometry(QtCore.QRect(860, 740, 211, 41)) 20 | self.bt_random_check.setObjectName("bt_random_check") 21 | self.bt_supplement = QtWidgets.QPushButton(self.centralwidget) # 漏签补签 22 | self.bt_supplement.setGeometry(QtCore.QRect(970, 700, 101, 31)) 23 | self.bt_supplement.setObjectName("bt_supplement") 24 | self.line_1 = QtWidgets.QFrame(self.centralwidget) 25 | self.line_1.setGeometry(QtCore.QRect(150, 690, 31, 131)) 26 | self.line_1.setFrameShape(QtWidgets.QFrame.VLine) 27 | self.line_1.setFrameShadow(QtWidgets.QFrame.Sunken) 28 | self.line_1.setObjectName("line_1") 29 | self.lineEdit_supplement = QtWidgets.QLineEdit(self.centralwidget) # 漏签补签输入框 30 | self.lineEdit_supplement.setGeometry(QtCore.QRect(860, 700, 101, 31)) 31 | self.lineEdit_supplement.setObjectName("lineEdit_supplement") 32 | self.textBrowser_log = QtWidgets.QTextBrowser(self.centralwidget) 33 | self.textBrowser_log.setGeometry(QtCore.QRect(350, 690, 301, 161)) 34 | self.textBrowser_log.setObjectName("textBrowser_log") 35 | self.progressBar = QtWidgets.QProgressBar(self.centralwidget) 36 | self.progressBar.setGeometry(QtCore.QRect(30, 833, 301, 16)) 37 | self.progressBar.setProperty("value", 24) 38 | self.progressBar.setObjectName("progressBar") 39 | self.bt_start_check = QtWidgets.QPushButton(self.centralwidget) # 开始分析 40 | self.bt_start_check.setGeometry(QtCore.QRect(180, 760, 151, 61)) 41 | self.bt_start_check.setObjectName("bt_start_check") 42 | self.bt_generator = QtWidgets.QPushButton(self.centralwidget) # 训练模型 43 | self.bt_generator.setGeometry(QtCore.QRect(180, 690, 151, 61)) 44 | self.bt_generator.setObjectName("bt_generator") 45 | self.bt_analyse = QtWidgets.QPushButton(self.centralwidget) # 分析详情 46 | self.bt_analyse.setGeometry(QtCore.QRect(660, 760, 181, 61)) 47 | self.bt_analyse.setObjectName("bt_analyse") 48 | self.tableView_escape = QtWidgets.QTableView(self.centralwidget) # 未到记录表 49 | self.tableView_escape.setGeometry(QtCore.QRect(860, 450, 211,201)) 50 | self.tableView_escape.setObjectName("tableView_escape") 51 | self.bt_check_variation = QtWidgets.QPushButton(self.centralwidget) # 核验数据库 52 | self.bt_check_variation.setGeometry(QtCore.QRect(20, 760, 131, 61)) 53 | self.bt_check_variation.setObjectName("bt_check_variation") 54 | self.bt_open_camera = QtWidgets.QPushButton(self.centralwidget) # 打开相机 55 | self.bt_open_camera.setGeometry(QtCore.QRect(660, 690, 181, 61)) 56 | self.bt_open_camera.setObjectName("bt_open_camera") 57 | self.label_title = QtWidgets.QLabel(self.centralwidget) # 标题 58 | self.label_title.setGeometry(QtCore.QRect(360, 20, 400, 40)) 59 | self.label_title.setText("") 60 | self.label_title.setObjectName("label_title") 61 | self.label_logo = QtWidgets.QLabel(self.centralwidget) 62 | self.label_logo.setGeometry(QtCore.QRect(30, 70, 800, 600)) 63 | # self.label_logo.setMinimumSize(QtCore.QSize(800, 600)) 64 | # self.label_logo.setMaximumSize(QtCore.QSize(800, 600)) 65 | self.label_logo.setText("") 66 | self.label_logo.setObjectName("label_logo") 67 | self.bt_leave = QtWidgets.QPushButton(self.centralwidget) # 请假登记 68 | self.bt_leave.setGeometry(QtCore.QRect(970, 660, 101, 31)) 69 | self.bt_leave.setObjectName("bt_leave") 70 | self.bt_exit = QtWidgets.QPushButton(self.centralwidget) # 退出系统 71 | self.bt_exit.setGeometry(QtCore.QRect(860, 790, 211, 61)) 72 | self.bt_exit.setObjectName("bt_exit") 73 | self.line_4 = QtWidgets.QFrame(self.centralwidget) 74 | self.line_4.setGeometry(QtCore.QRect(20, 60, 821, 21)) 75 | self.line_4.setFrameShape(QtWidgets.QFrame.HLine) 76 | self.line_4.setFrameShadow(QtWidgets.QFrame.Sunken) 77 | self.line_4.setObjectName("line_4") 78 | self.lineEdit_leave = QtWidgets.QLineEdit(self.centralwidget) # 请假登记输入 79 | self.lineEdit_leave.setGeometry(QtCore.QRect(860, 660, 101, 31)) 80 | self.lineEdit_leave.setObjectName("lineEdit_leave") 81 | self.bt_listName1 = QtWidgets.QPushButton(self.centralwidget) # 未到 82 | self.bt_listName1.setGeometry(QtCore.QRect(880, 410, 50, 30)) 83 | self.bt_listName1.setObjectName("bt_listName1") 84 | 85 | self.bt_output = QtWidgets.QPushButton(self.centralwidget) # 未到导出 86 | self.bt_output.setGeometry(QtCore.QRect(980, 410, 50, 30)) 87 | self.bt_output.setObjectName("bt_output") 88 | 89 | self.label_emo1 = QtWidgets.QLabel(self.centralwidget) # 情绪1 90 | self.label_emo1.setGeometry(QtCore.QRect(880, 80, 50, 30)) 91 | self.label_emo1.setObjectName("label_emo1") 92 | 93 | self.label_emo1_1 = QtWidgets.QLabel(self.centralwidget) # 情绪1 94 | self.label_emo1_1.setGeometry(QtCore.QRect(980, 80, 70, 30)) 95 | self.label_emo1_1.setObjectName("label_emo1_1") 96 | 97 | self.label_emo2 = QtWidgets.QLabel(self.centralwidget) # 情绪2 98 | self.label_emo2.setGeometry(QtCore.QRect(880, 140, 50, 30)) 99 | self.label_emo2.setObjectName("label_emo2") 100 | 101 | self.label_emo2_2 = QtWidgets.QLabel(self.centralwidget) # 情绪2 102 | self.label_emo2_2.setGeometry(QtCore.QRect(980, 140, 70, 30)) 103 | self.label_emo2_2.setObjectName("label_emo2_2") 104 | 105 | self.label_emo3 = QtWidgets.QLabel(self.centralwidget) # 情绪3 106 | self.label_emo3.setGeometry(QtCore.QRect(880, 200, 50, 30)) 107 | self.label_emo3.setObjectName("label_emo3") 108 | 109 | self.label_emo3_3 = QtWidgets.QLabel(self.centralwidget) # 情绪3 110 | self.label_emo3_3.setGeometry(QtCore.QRect(980, 200, 70, 30)) 111 | self.label_emo3_3.setObjectName("label_emo3_3") 112 | 113 | self.label_emo4 = QtWidgets.QLabel(self.centralwidget) # 情绪4 114 | self.label_emo4.setGeometry(QtCore.QRect(880, 260, 50, 30)) 115 | self.label_emo4.setObjectName("label_emo4") 116 | 117 | self.label_emo4_4 = QtWidgets.QLabel(self.centralwidget) # 情绪4 118 | self.label_emo4_4.setGeometry(QtCore.QRect(980, 260, 70, 30)) 119 | self.label_emo4_4.setObjectName("label_emo4") 120 | 121 | self.label_emo6 = QtWidgets.QLabel(self.centralwidget) # 情绪5 122 | self.label_emo6.setGeometry(QtCore.QRect(880, 320, 50, 30)) 123 | self.label_emo6.setObjectName("label_emo6") 124 | 125 | self.label_emo6_6 = QtWidgets.QLabel(self.centralwidget) # 情绪5 126 | self.label_emo6_6.setGeometry(QtCore.QRect(980, 320, 70, 30)) 127 | self.label_emo6_6.setObjectName("label_emo6") 128 | 129 | self.bt_gathering = QtWidgets.QPushButton(self.centralwidget) # 信息采集 130 | self.bt_gathering.setGeometry(QtCore.QRect(20, 690, 131, 61)) 131 | self.bt_gathering.setObjectName("bt_gathering") 132 | 133 | self.line_2 = QtWidgets.QFrame(self.centralwidget) 134 | self.line_2.setGeometry(QtCore.QRect(840, 70, 20, 781)) 135 | self.line_2.setFrameShape(QtWidgets.QFrame.VLine) 136 | self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken) 137 | self.line_2.setObjectName("line_2") 138 | 139 | MainWindow.setCentralWidget(self.centralwidget) 140 | self.menubar = QtWidgets.QMenuBar(MainWindow) 141 | self.menubar.setGeometry(QtCore.QRect(0, 0, 1086, 23)) 142 | self.menubar.setObjectName("menubar") 143 | self.menuFile = QtWidgets.QMenu(self.menubar) 144 | self.menuFile.setObjectName("menuFile") 145 | self.menuEdit = QtWidgets.QMenu(self.menubar) 146 | self.menuEdit.setObjectName("menuEdit") 147 | self.menuView = QtWidgets.QMenu(self.menubar) 148 | self.menuView.setObjectName("menuView") 149 | self.menuHelp = QtWidgets.QMenu(self.menubar) 150 | self.menuHelp.setObjectName("menuHelp") 151 | MainWindow.setMenuBar(self.menubar) 152 | self.statusbar = QtWidgets.QStatusBar(MainWindow) 153 | self.statusbar.setObjectName("statusbar") 154 | MainWindow.setStatusBar(self.statusbar) 155 | self.actionAbout = QtWidgets.QAction(MainWindow) 156 | self.actionAbout.setObjectName("actionAbout") 157 | self.actionOpen = QtWidgets.QAction(MainWindow) 158 | self.actionOpen.setObjectName("actionOpen") 159 | self.actionSetting = QtWidgets.QAction(MainWindow) 160 | self.actionSetting.setObjectName("actionSetting") 161 | self.actionExport = QtWidgets.QAction(MainWindow) 162 | self.actionExport.setObjectName("actionExport") 163 | self.menuFile.addAction(self.actionOpen) 164 | self.menuFile.addAction(self.actionSetting) 165 | self.menuFile.addAction(self.actionExport) 166 | self.menuHelp.addAction(self.actionAbout) 167 | self.menubar.addAction(self.menuFile.menuAction()) 168 | self.menubar.addAction(self.menuEdit.menuAction()) 169 | self.menubar.addAction(self.menuView.menuAction()) 170 | self.menubar.addAction(self.menuHelp.menuAction()) 171 | 172 | self.retranslateUi(MainWindow) 173 | QtCore.QMetaObject.connectSlotsByName(MainWindow) 174 | 175 | def retranslateUi(self, MainWindow): 176 | _translate = QtCore.QCoreApplication.translate 177 | MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) 178 | self.bt_random_check.setText(_translate("MainWindow", "随机抽签")) 179 | self.bt_supplement.setText(_translate("MainWindow", "漏签补签")) 180 | self.bt_start_check.setText(_translate("MainWindow", "开始分析")) 181 | self.bt_generator.setText(_translate("MainWindow", "训练模型")) 182 | self.bt_analyse.setText(_translate("MainWindow", "分析详情")) 183 | self.bt_check_variation.setText(_translate("MainWindow", "未采集人员")) 184 | self.bt_open_camera.setText(_translate("MainWindow", "打开相机")) 185 | self.bt_leave.setText(_translate("MainWindow", "请假登记")) 186 | self.bt_exit.setText(_translate("MainWindow", "退出系统")) 187 | self.label_emo1.setText(_translate("MainWindow", "高兴")) 188 | self.label_emo1_1.setText(_translate("MainWindow", " ")) 189 | self.label_emo2.setText(_translate("MainWindow", "伤心")) 190 | self.label_emo2_2.setText(_translate("MainWindow", " ")) 191 | self.label_emo3.setText(_translate("MainWindow", "惊讶")) 192 | self.label_emo3_3.setText(_translate("MainWindow", " ")) 193 | self.label_emo4.setText(_translate("MainWindow", "生气")) 194 | self.label_emo4_4.setText(_translate("MainWindow", " ")) 195 | self.label_emo6.setText(_translate("MainWindow", "中立")) 196 | self.label_emo6_6.setText(_translate("MainWindow", " ")) 197 | self.bt_listName1.setText(_translate("MainWindow", "未到")) 198 | self.bt_output.setText(_translate("MainWindow", "导出")) 199 | self.bt_gathering.setText(_translate("MainWindow", "信息采集")) 200 | self.menuFile.setTitle(_translate("MainWindow", "File")) 201 | self.menuEdit.setTitle(_translate("MainWindow", "Edit")) 202 | self.menuView.setTitle(_translate("MainWindow", "View")) 203 | self.menuHelp.setTitle(_translate("MainWindow", "Help")) 204 | self.actionAbout.setText(_translate("MainWindow", "About")) 205 | self.actionAbout.setWhatsThis(_translate("MainWindow", "Author: datamonday")) 206 | self.actionOpen.setText(_translate("MainWindow", "Open")) 207 | self.actionSetting.setText(_translate("MainWindow", "Setting")) 208 | self.actionExport.setText(_translate("MainWindow", "Export")) 209 | 210 | 211 | if __name__ == "__main__": 212 | import sys 213 | app = QtWidgets.QApplication(sys.argv) 214 | MainWindow = QtWidgets.QMainWindow() 215 | ui = Ui_MainWindow() 216 | ui.setupUi(MainWindow) 217 | MainWindow.show() 218 | sys.exit(app.exec_()) 219 | -------------------------------------------------------------------------------- /ui/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1086 10 | 907 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 30 21 | 80 22 | 700 23 | 600 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 850 34 | 20 35 | 231 36 | 30 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 860 47 | 740 48 | 211 49 | 41 50 | 51 | 52 | 53 | 随机点名 54 | 55 | 56 | 57 | 58 | 59 | 970 60 | 300 61 | 101 62 | 31 63 | 64 | 65 | 66 | 漏签补签 67 | 68 | 69 | 70 | 71 | 72 | 860 73 | 360 74 | 211 75 | 141 76 | 77 | 78 | 79 | 80 | 81 | 82 | 860 83 | 100 84 | 61 85 | 16 86 | 87 | 88 | 89 | 考勤地点 90 | 91 | 92 | 93 | 94 | 95 | 150 96 | 690 97 | 31 98 | 131 99 | 100 | 101 | 102 | Qt::Vertical 103 | 104 | 105 | 106 | 107 | 108 | 860 109 | 300 110 | 101 111 | 31 112 | 113 | 114 | 115 | 116 | 117 | 118 | 350 119 | 690 120 | 301 121 | 161 122 | 123 | 124 | 125 | 126 | 127 | 128 | 30 129 | 833 130 | 301 131 | 16 132 | 133 | 134 | 135 | 24 136 | 137 | 138 | 139 | 140 | 141 | 770 142 | 770 143 | 71 144 | 81 145 | 146 | 147 | 148 | 活体检测 149 | 150 | 151 | 152 | 153 | 154 | 660 155 | 770 156 | 101 157 | 81 158 | 159 | 160 | 161 | 开始考勤 162 | 163 | 164 | 165 | 166 | 167 | 180 168 | 690 169 | 151 170 | 21 171 | 172 | 173 | 174 | 175 | FaceNet (ResNet-SSD) 176 | 177 | 178 | 179 | 180 | FaceNet (MTCNN) 181 | 182 | 183 | 184 | 185 | RetinaFace 186 | 187 | 188 | 189 | 190 | ArcFace 191 | 192 | 193 | 194 | 195 | CenterFace 196 | 197 | 198 | 199 | 200 | DBFace 201 | 202 | 203 | 204 | 205 | FaceBoxes 206 | 207 | 208 | 209 | 210 | S3FD 211 | 212 | 213 | 214 | 215 | 216 | 217 | 180 218 | 760 219 | 151 220 | 61 221 | 222 | 223 | 224 | 训练模型 225 | 226 | 227 | 228 | 229 | 230 | 860 231 | 530 232 | 211 233 | 141 234 | 235 | 236 | 237 | 238 | 239 | 240 | 180 241 | 720 242 | 151 243 | 31 244 | 245 | 246 | 247 | 加载模型 248 | 249 | 250 | 251 | 252 | 253 | 860 254 | 680 255 | 211 256 | 51 257 | 258 | 259 | 260 | 查看结果 261 | 262 | 263 | 264 | 265 | 266 | 860 267 | 190 268 | 31 269 | 16 270 | 271 | 272 | 273 | 实到 274 | 275 | 276 | 277 | 278 | 279 | 860 280 | 70 281 | 51 282 | 16 283 | 284 | 285 | 286 | 考勤班级 287 | 288 | 289 | 290 | 291 | 292 | 940 293 | 221 294 | 61 295 | 31 296 | 297 | 298 | 299 | 300 | 301 | 302 | 900 303 | 140 304 | 71 305 | 31 306 | 307 | 308 | 309 | 310 | 311 | 312 | 20 313 | 760 314 | 131 315 | 61 316 | 317 | 318 | 319 | 核验人脸数据库信息 320 | 321 | 322 | 323 | 324 | 325 | 860 326 | 510 327 | 41 328 | 16 329 | 330 | 331 | 332 | 迟到 333 | 334 | 335 | 336 | 337 | 338 | 900 339 | 180 340 | 71 341 | 31 342 | 343 | 344 | 345 | 346 | 347 | 348 | 660 349 | 690 350 | 181 351 | 71 352 | 353 | 354 | 355 | 打开相机 356 | 357 | 358 | 359 | 360 | 361 | 360 362 | 20 363 | 400 364 | 40 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 30 375 | 70 376 | 800 377 | 600 378 | 379 | 380 | 381 | 382 | 800 383 | 600 384 | 385 | 386 | 387 | 388 | 800 389 | 600 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 970 400 | 260 401 | 101 402 | 31 403 | 404 | 405 | 406 | 请假登记 407 | 408 | 409 | 410 | 411 | 412 | 980 413 | 140 414 | 91 415 | 71 416 | 417 | 418 | 419 | 查询 420 | 421 | 422 | 423 | 424 | 425 | 860 426 | 230 427 | 81 428 | 16 429 | 430 | 431 | 432 | 设置考勤时间 433 | 434 | 435 | 436 | 437 | 438 | 860 439 | 790 440 | 211 441 | 61 442 | 443 | 444 | 445 | 退出系统 446 | 447 | 448 | 449 | 450 | 451 | 20 452 | 60 453 | 821 454 | 21 455 | 456 | 457 | 458 | Qt::Horizontal 459 | 460 | 461 | 462 | 463 | 464 | 940 465 | 70 466 | 131 467 | 21 468 | 469 | 470 | 471 | 472 | 2020001 473 | 474 | 475 | 476 | 477 | 2020002 478 | 479 | 480 | 481 | 482 | 483 | 484 | 860 485 | 260 486 | 101 487 | 31 488 | 489 | 490 | 491 | 492 | 493 | 494 | 860 495 | 340 496 | 31 497 | 16 498 | 499 | 500 | 501 | 未到 502 | 503 | 504 | 505 | 506 | 507 | 20 508 | 690 509 | 131 510 | 61 511 | 512 | 513 | 514 | 信息采集 515 | 516 | 517 | 518 | 519 | 520 | 860 521 | 150 522 | 31 523 | 16 524 | 525 | 526 | 527 | 应到 528 | 529 | 530 | 531 | 532 | 533 | 840 534 | 70 535 | 20 536 | 781 537 | 538 | 539 | 540 | Qt::Vertical 541 | 542 | 543 | 544 | 545 | 546 | 1011 547 | 220 548 | 61 549 | 31 550 | 551 | 552 | 553 | 554 | 555 | 556 | 940 557 | 100 558 | 131 559 | 21 560 | 561 | 562 | 563 | 564 | A 565 | 566 | 567 | 568 | 569 | B 570 | 571 | 572 | 573 | 574 | C 575 | 576 | 577 | 578 | 579 | D 580 | 581 | 582 | 583 | 584 | E 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 0 593 | 0 594 | 1086 595 | 23 596 | 597 | 598 | 599 | 600 | File 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | Edit 609 | 610 | 611 | 612 | 613 | View 614 | 615 | 616 | 617 | 618 | Help 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | About 631 | 632 | 633 | Author: datamonday 634 | 635 | 636 | 637 | 638 | Open 639 | 640 | 641 | 642 | 643 | Setting 644 | 645 | 646 | 647 | 648 | Export 649 | 650 | 651 | 652 | 653 | 654 | 655 | -------------------------------------------------------------------------------- /utils2/Detaillog.py: -------------------------------------------------------------------------------- 1 | import os 2 | from datetime import datetime 3 | from PyQt5 import QtCore, QtGui, QtWidgets 4 | from PyQt5.QtGui import QImage, QIcon, QPixmap 5 | from PyQt5.QtCore import QCoreApplication, QThread 6 | from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QInputDialog 7 | from utils2.GlobalVar import num_list 8 | 9 | # 导入分析详情框界面 10 | from ui import detail 11 | 12 | # 将根目录(execute所在目录)添加到环境变量 13 | from utils2.GlobalVar import add_path_to_sys, connect_to_sql 14 | rootdir = add_path_to_sys() 15 | 16 | 17 | class Detaillog(QWidget): 18 | def __init__(self): 19 | super().__init__() 20 | 21 | self.Dialog = detail.Ui_Form() 22 | self.Dialog.setupUi(self) 23 | 24 | # 实现路径错误提示,方便定位错误 25 | self.current_filename = os.path.basename(__file__) 26 | 27 | try: 28 | # 设置窗口名称和图标 29 | self.setWindowTitle("情绪分析详情界面") 30 | self.setWindowIcon(QIcon(f'{rootdir}/logo_imgs/fcb_logo.jpg')) 31 | except FileNotFoundError as e: 32 | print("[ERROR] UI背景图片路径不正确!(source file: {})".format(self.current_filename), e) 33 | 34 | self.Dialog.pb_other.clicked.connect(self.answer_other) 35 | self.Dialog.pb_exit.clicked.connect(self.pd_exit) 36 | 37 | # 查询详情 38 | def answer_other(self): 39 | try: 40 | db, cursor = connect_to_sql() 41 | except ConnectionAbortedError as e: 42 | self.ui.textBrowser_log.append('[INFO] 连接数据库失败,请检查配置信息!') 43 | else: 44 | sql = "select * from detail" 45 | # 执行查询 46 | cursor.execute(sql) 47 | results = cursor.fetchall() 48 | self.student_ids = [] 49 | self.student_names = [] 50 | self.student_emo = [] 51 | self.student_time = [] 52 | for item in results: 53 | self.student_ids.append(item[0]) 54 | self.student_names.append(item[1]) 55 | self.student_emo.append(item[2]) 56 | self.student_time.append(item[3]) 57 | finally: 58 | db.commit() 59 | cursor.close() 60 | db.close() 61 | if len(self.student_ids) == 0: 62 | QMessageBox.warning(self, "Error", "暂无情绪分析结果!", QMessageBox.Ok) 63 | else: 64 | # 设置显示数据层次结构,4行4列(包含行表头) 65 | table_view_module = QtGui.QStandardItemModel(len(num_list), 1) 66 | table_view_module.setHorizontalHeaderLabels(['学号', '姓名', '当前表情', '情绪持续时间/s']) 67 | 68 | step = 0 69 | for i in num_list: 70 | id = int(i) # 要显示的学号 71 | k = self.student_ids.index(id) 72 | num = QtGui.QStandardItem(str(id)) 73 | name = self.student_names[k] 74 | name = QtGui.QStandardItem(name) 75 | emo = self.student_emo[k] 76 | emo = QtGui.QStandardItem(emo) 77 | current_time = datetime.now() 78 | last_time = (current_time - self.student_time[k]).seconds 79 | last_time = QtGui.QStandardItem(str(last_time)) 80 | 81 | 82 | # 设置每个位置的行名称和文本值 83 | table_view_module.setItem(step, 0, num) 84 | table_view_module.setItem(step, 1, name) 85 | table_view_module.setItem(step, 2, emo) 86 | table_view_module.setItem(step, 3, last_time) 87 | step = step + 1 88 | 89 | # 指定显示的tableView控件,实例化表格视图 90 | self.Dialog.tableView.setModel(table_view_module) 91 | 92 | def pd_exit(self): 93 | self.close() 94 | 95 | def handle_click(self): 96 | if not self.isVisible(): 97 | self.show() 98 | 99 | -------------------------------------------------------------------------------- /utils2/GeneratorModel.py: -------------------------------------------------------------------------------- 1 | from imutils import paths 2 | import numpy as np 3 | import imutils 4 | import pickle 5 | import cv2 6 | import os 7 | import sys 8 | from sklearn.preprocessing import LabelEncoder 9 | from sklearn.svm import SVC 10 | 11 | # 将根目录(execute所在目录)添加到环境变量 12 | from utils2.GlobalVar import add_path_to_sys 13 | rootdir = add_path_to_sys() 14 | 15 | 16 | def Generator(): 17 | # 人脸数据所在路径 18 | global embedder 19 | face_data = f"{rootdir}/face_dataset" 20 | # 输出面部嵌入的序列化数据库的路径 21 | embeddings = f"{rootdir}/saved_weights/embeddings.pickle" 22 | # OpenCV深度学习人脸检测器的路径 23 | detector_path = f"{rootdir}/model_face_detection/" 24 | # OpenCV深度学习面部嵌入模型的路径;Torch深度学习模型,可产生128-D面部嵌入 25 | # https://cmusatyalab.github.io/openface/models-and-accuracies/ 26 | embedding_model = f"{rootdir}/model_facenet/openface_nn4.small2.v1.t7" 27 | # 置信度 28 | default_confidence = 0.5 29 | 30 | # 从磁盘加载序列化面部检测器 31 | print("[INFO] loading face detector...") 32 | proto_path = os.path.sep.join([detector_path, "deploy.prototxt"]) 33 | model_path = os.path.sep.join([detector_path, "res10_300x300_ssd_iter_140000.caffemodel"]) 34 | detector = cv2.dnn.readNetFromCaffe(proto_path, model_path) 35 | 36 | try: 37 | # 从磁盘加载序列化面嵌入模型 38 | print("[INFO] loading face recognizer...") 39 | embedder = cv2.dnn.readNetFromTorch(embedding_model) 40 | except IOError: 41 | print("[ERROR] Please check filepath!") 42 | 43 | # 人脸图像的路径 44 | print("[INFO] quantifying faces...") 45 | image_paths = list(paths.list_images(face_data)) 46 | 47 | # 初始化提取的面部嵌入列表和相应的人名 48 | known_embeddings = [] 49 | known_names = [] 50 | 51 | # 初始化处理的人脸总数 52 | total = 0 53 | 54 | for (i, imagePath) in enumerate(image_paths): 55 | # 从图像路径中提取人名 56 | print("[INFO] processing image {}/{}".format(i + 1, len(image_paths))) 57 | name = imagePath.split(os.path.sep)[-2] 58 | 59 | # 加载图像,将其大小调整为宽度为600像素(同时保持纵横比),然后抓取图像尺寸 60 | image = cv2.imread(imagePath) 61 | image = imutils.resize(image, width=600) 62 | (h, w) = image.shape[:2] 63 | 64 | # 从图像构造一个blob 65 | image_blob = cv2.dnn.blobFromImage( 66 | cv2.resize(image, (300, 300)), 1.0, (300, 300), 67 | (104.0, 177.0, 123.0), swapRB=False, crop=False) 68 | 69 | # 应用OpenCV的基于深度学习的人脸检测器来定位输入图像中的人脸 70 | detector.setInput(image_blob) 71 | detections = detector.forward() 72 | 73 | # 确保至少找到一张脸 74 | if len(detections) > 0: 75 | # 假设每个图像只有一张脸,找到概率最大的边界框 76 | i = np.argmax(detections[0, 0, :, 2]) 77 | confidence = detections[0, 0, i, 2] 78 | 79 | # 确保最大概率的检测也意味着最小概率测试(从而帮助滤除弱检测) 80 | if confidence > default_confidence: 81 | # 计算面部边界框的(x,y)坐标 82 | box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) 83 | (startX, startY, endX, endY) = box.astype("int") 84 | 85 | # 提取面部ROI并获取ROI维度 86 | face = image[startY:endY, startX:endX] 87 | (fH, fW) = face.shape[:2] 88 | 89 | # 确保面部宽度和高度足够大 90 | if fW < 20 or fH < 20: 91 | continue 92 | 93 | # 为面部ROI构造一个blob,然后通过面部嵌入模型传递blob以获得面部的128-d量化 94 | face_blob = cv2.dnn.blobFromImage(face, 1.0 / 255, 95 | (96, 96), (0, 0, 0), swapRB=True, crop=False) 96 | embedder.setInput(face_blob) 97 | vec = embedder.forward() 98 | 99 | # 将人物名和相应的脸部嵌入添加到各自的列表中 100 | known_names.append(name) 101 | known_embeddings.append(vec.flatten()) 102 | total += 1 103 | 104 | # 将面部嵌入+名称 保存到磁盘 105 | print("[INFO] serializing {} encodings...".format(total)) 106 | data = {"embeddings": known_embeddings, "names": known_names} 107 | f = open(embeddings, "wb") 108 | f.write(pickle.dumps(data)) 109 | f.close() 110 | 111 | 112 | def TrainModel(): 113 | # 面部嵌入的序列化db的路径 114 | embeddings_path = f"{rootdir}/saved_weights/embeddings.pickle" 115 | # 训练识别面部的输出模型的路径 116 | recognizer_path = f"{rootdir}/saved_weights/recognizer.pickle" 117 | # 输出标签编码器的路径 118 | le_path = f"{rootdir}/saved_weights/le.pickle" 119 | 120 | # 加载面嵌入 121 | print("[INFO] loading face embeddings...") 122 | data = pickle.loads(open(embeddings_path, "rb").read()) 123 | 124 | # 编码标签 125 | print("[INFO] encoding labels...") 126 | le = LabelEncoder() 127 | labels = le.fit_transform(data["names"]) 128 | 129 | # 训练用于接受人脸128-d嵌入的模型,然后产生实际的人脸识别 130 | print("[INFO] training model...") 131 | recognizer = SVC(C=1.0, kernel="linear", probability=True) 132 | recognizer.fit(data["embeddings"], labels) 133 | 134 | # 将实际的人脸识别模型写入磁盘 135 | f = open(recognizer_path, "wb") 136 | f.write(pickle.dumps(recognizer)) 137 | f.close() 138 | 139 | # 将标签编码器发送到磁盘 140 | f = open(le_path, "wb") 141 | f.write(pickle.dumps(le)) 142 | f.close() 143 | -------------------------------------------------------------------------------- /utils2/GlobalVar.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | import pymysql 5 | 6 | # 全局变量 7 | # 学号 8 | num_list =[] 9 | # 摄像头ID 10 | CAMERA_ID = 0 11 | # 默认采集人脸数量 12 | COLLENCT_FACE_NUM_DEFAULT = 100 13 | 14 | # 多少次循环保存一帧图像 15 | LOOP_FRAME = 20 16 | 17 | 18 | # 初始化循环次数,比如统计20帧中人脸的数量,取最大值进行考勤 19 | FR_LOOP_NUM = 10 20 | 21 | # 将execute文件所在目录添加到根目录 22 | def add_path_to_sys(): 23 | rootdir = "E:/soft/smart/Face-Recognition-Class-Attendance-System-master/" 24 | # rootdir = os.getcwd() 25 | sys.path.append(rootdir) 26 | 27 | return rootdir 28 | 29 | # 连接数据库操作 30 | def connect_to_sql(): 31 | db = pymysql.connect(host="localhost", user="root", password="123456", database="facedata") 32 | # 使用cursor()方法获取操作游标 33 | cursor = db.cursor() 34 | 35 | return db, cursor 36 | 37 | # 遍历人脸数据文件夹,并统计各人脸的图片数量 38 | def statical_facedata_nums(): 39 | # 人脸数据文件夹根目录 40 | files_dir = "E:/soft/smart/Face-Recognition-Class-Attendance-System-master/face_dataset/" 41 | # print(os.listdir(files_dir)) 42 | 43 | dirs = os.listdir(files_dir) 44 | # 初始化字典 45 | files_num_dict = dict(zip(dirs, [0] * len(dirs))) 46 | 47 | for dir in dirs: 48 | for file in os.listdir(files_dir + dir): 49 | if file.endswith('.jpg') or file.endswith('.png'): 50 | files_num_dict[dir] += 1 51 | 52 | return files_num_dict 53 | 54 | 55 | if __name__ == '__main__': 56 | files = statical_facedata_nums() -------------------------------------------------------------------------------- /utils2/InfoDialog.py: -------------------------------------------------------------------------------- 1 | from PyQt5 import QtCore, QtGui, QtWidgets 2 | from PyQt5.QtGui import QImage, QIcon, QPixmap 3 | from PyQt5.QtCore import QCoreApplication, QThread 4 | from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QInputDialog 5 | # 导入信息采集框界面 6 | from ui import infoUI 7 | 8 | import threading 9 | import cv2 10 | import imutils 11 | import os 12 | import sys 13 | from datetime import datetime 14 | # 导入全局变量,主要包含摄像头ID,默认采集人脸数量等 15 | from utils2.GlobalVar import CAMERA_ID, COLLENCT_FACE_NUM_DEFAULT, LOOP_FRAME 16 | # 将根目录(execute所在目录)添加到环境变量 17 | from utils2.GlobalVar import add_path_to_sys, statical_facedata_nums 18 | rootdir = add_path_to_sys() 19 | 20 | 21 | class InfoDialog(QWidget): 22 | def __init__(self): 23 | # super()构造器方法返回父级的对象。__init__()方法是构造器的一个方法。 24 | super().__init__() 25 | 26 | self.Dialog = infoUI.Ui_Form() 27 | self.Dialog.setupUi(self) 28 | 29 | # 实现路径错误提示,方便定位错误 30 | self.current_filename = os.path.basename(__file__) 31 | 32 | try: 33 | # 设置窗口名称和图标 34 | self.setWindowTitle('个人信息采集') 35 | self.setWindowIcon(QIcon(f'{rootdir}/logo_imgs/fcb_logo.jpg')) 36 | # 设置单张图片背景 37 | pixmap = QPixmap(f'{rootdir}/logo_imgs/bkg2.png') 38 | self.Dialog.label_capture.setPixmap(pixmap) 39 | except FileNotFoundError as e: 40 | print("[ERROR] UI背景图片路径不正确!(source file: {})".format(self.current_filename), e) 41 | 42 | # 设置信息采集按键连接函数 43 | self.Dialog.bt_start_collect.clicked.connect(self.open_camera) 44 | # 设置拍照按键连接函数 45 | self.Dialog.bt_take_photo.clicked.connect(self.take_photo) 46 | # 查看人脸图片数量案件连接函数 47 | self.Dialog.bt_check_dirs_faces.clicked.connect(self.check_dir_faces_num) 48 | # 退出 49 | self.Dialog.bt_exit.clicked.connect(self.check_exit) 50 | 51 | # 初始化摄像头 52 | self.cap = cv2.VideoCapture() 53 | 54 | # 设置默认采集的照片数量 55 | self.Dialog.spinBox_set_num.setValue(COLLENCT_FACE_NUM_DEFAULT) 56 | # 初始化已经采集的人脸数目 57 | self.have_token_photos = 0 58 | 59 | # 初始化一个变量用来存储上一次采集人脸的ID 60 | self.dialog_text_id_past = None 61 | 62 | self.collect_photos = int(self.Dialog.spinBox_set_num.text()) 63 | 64 | def handle_click(self): 65 | if not self.isVisible(): 66 | self.show() 67 | 68 | def handle_close(self): 69 | self.close() 70 | 71 | def open_camera(self): 72 | # 判断摄像头是否打开,如果打开则为true,反之为false 73 | if not self.cap.isOpened(): 74 | # 通过对话框设置被采集人学号 75 | self.dialog_text_id, ok = QInputDialog.getText(self, '创建个人图像数据库', '请输入学号:') 76 | if ok and self.dialog_text_id != '': 77 | 78 | # 如果两次输入的学号不同,则将上一次的采集图像数量图片等删除 79 | if self.dialog_text_id != self.dialog_text_id_past: 80 | self.have_token_photos = 0 81 | self.Dialog.lcdNumber_collection_nums.display(0) 82 | 83 | self.Dialog.label_capture.clear() 84 | self.cap.open(CAMERA_ID) 85 | self.show_capture() 86 | else: 87 | self.cap.release() 88 | self.Dialog.label_capture.clear() 89 | self.Dialog.bt_start_collect.setText(u'开始采集') 90 | 91 | def show_capture(self): 92 | self.Dialog.bt_start_collect.setText(u'停止采集') 93 | self.Dialog.label_capture.clear() 94 | print("[INFO] starting video stream...") 95 | loop_num = 0 96 | 97 | # 循环来自视频文件流的帧 98 | while self.cap.isOpened(): 99 | # 获取设定的最大采集张数 100 | self.collect_photos = int(self.Dialog.spinBox_set_num.text()) 101 | # 循环自增 102 | loop_num += 1 103 | 104 | ret, frame = self.cap.read() 105 | QApplication.processEvents() 106 | frame = imutils.resize(frame, width=500) 107 | show_video = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 这里指的是显示原图 108 | # opencv读取图片的样式,不能通过Qlabel进行显示,需要转换为Qimage。 109 | # QImage(uchar * data, int width, int height, int bytesPerLine, Format format) 110 | self.showImage = QImage(show_video.data, 111 | show_video.shape[1], 112 | show_video.shape[0], 113 | QImage.Format_RGB888) 114 | self.Dialog.label_capture.setPixmap(QPixmap.fromImage(self.showImage)) 115 | # 是否为自动采集 116 | is_auto_collect = self.Dialog.checkBox_auto_collect.isChecked() 117 | 118 | if is_auto_collect: 119 | # 如果达到设定的最大采集数量则退出 120 | if self.have_token_photos != self.collect_photos: 121 | # 大量I/O,为保证每次都写入磁盘,20次循环写入一次 122 | if loop_num == LOOP_FRAME: 123 | self.have_token_photos += 1 124 | self.save_image() 125 | 126 | loop_num = 0 127 | if self.have_token_photos == self.collect_photos: 128 | QMessageBox.warning(self, "Warning", "已达到最大采集张数:{}!请增加采集数量或停止采集!".format(self.have_token_photos), QMessageBox.Ok) 129 | self.cap.release() 130 | self.Dialog.bt_start_collect.setText('开始采集') 131 | break 132 | else: 133 | QMessageBox.information(self, "Tips", "已退出自动采集模式,请手动采集!") 134 | break 135 | 136 | # 记录上次需要采集的照片数量 137 | self.dialog_text_id_past = self.dialog_text_id 138 | 139 | # 因为最后一张画面会显示在GUI中,此处实现清除。 140 | self.Dialog.label_capture.clear() 141 | 142 | def save_image(self, method='qt5'): 143 | self.filename = '{}/face_dataset/{}/'.format(rootdir, self.dialog_text_id) 144 | self.mk_folder(self.filename) 145 | if method == 'qt5': 146 | photo_save_path = os.path.join(os.path.dirname(os.path.abspath('__file__')), '{}'.format(self.filename)) 147 | save_filename = datetime.now().strftime("%Y%m%d%H%M%S") + ".png" 148 | self.showImage.save(photo_save_path + save_filename) 149 | 150 | self.Dialog.lcdNumber_collection_nums.display(self.have_token_photos) 151 | 152 | # 创建文件夹 153 | def mk_folder(self, path): 154 | # 去除首位空格 155 | path = path.strip() 156 | # 去除尾部 \ 符号 157 | path = path.rstrip("\\") 158 | # 判断路径是否存在, 存在=True; 不存在=False 159 | is_dir_exists = os.path.exists(path) 160 | # 判断结果 161 | if not is_dir_exists: 162 | # 如果不存在则创建目录 163 | os.makedirs(path) 164 | return True 165 | 166 | def take_photo(self): 167 | if self.cap.isOpened(): 168 | self.collect_photos = int(self.Dialog.spinBox_set_num.text()) 169 | if self.have_token_photos != self.collect_photos: 170 | self.have_token_photos += 1 171 | try: 172 | self.save_image() 173 | except FileNotFoundError as e: 174 | print("[ERROR] 路径不正确!(source file: {})".format(self.current_filename), e) 175 | 176 | else: 177 | QMessageBox.information(self, "Information", self.tr("已达到设置的最大采集张数!"), QMessageBox.Ok) 178 | 179 | else: 180 | QMessageBox.information(self, "Information", self.tr("请先打开摄像头!"), QMessageBox.Ok) 181 | 182 | 183 | # 检查本地人脸数据信息,包括文件夹名(ID)及图片数量 184 | def check_dir_faces_num(self): 185 | num_dict = statical_facedata_nums() 186 | keys = list(num_dict.keys()) 187 | # values = list(num_dict.values()) 188 | # print(values) 189 | # 如果没有人脸文件夹,则提示用户采集数据 190 | if len(keys) == 0: 191 | QMessageBox.warning(self, "Error", "face_dataset文件夹下没有人脸数据,请马上录入!", QMessageBox.Ok) 192 | else: 193 | # 设置显示数据层次结构,行1列(包含行表头) 194 | table_view_module = QtGui.QStandardItemModel(len(keys), 1) 195 | table_view_module.setHorizontalHeaderLabels(['ID']) 196 | 197 | for row, key in enumerate(keys): 198 | # print(key, values[row]) 199 | id = QtGui.QStandardItem(key) 200 | # num = QtGui.QStandardItem(str(values[row])) 201 | 202 | # 设置每个位置的行名称和文本值 203 | table_view_module.setItem(row, 0, id) 204 | # table_view_module.setItem(row, 1, num) 205 | 206 | # 指定显示的tableView控件,实例化表格视图 207 | self.Dialog.tableView.setModel(table_view_module) 208 | 209 | 210 | def check_exit(self): 211 | if self.cap.isOpened(): 212 | self.cap.release() 213 | self.close() 214 | -------------------------------------------------------------------------------- /utils2/RandomCheck.py: -------------------------------------------------------------------------------- 1 | import random 2 | from copy import deepcopy 3 | 4 | 5 | def random_check(choices): 6 | # 停止标志 7 | exit_flag = False 8 | # 防止原地修改 9 | choices = deepcopy(choices) 10 | while len(choices) != 0: 11 | random_choice = random.choice(choices) 12 | print(random_choice) 13 | choices.remove(random_choice) 14 | 15 | if exit_flag: 16 | break 17 | 18 | 19 | if __name__ == '__main__': 20 | choice_list = ['1', '2', '3', '4', '5', '7', '9', '11', '12', '13', '16', '18', '21', '22', '23'] 21 | random_check(choice_list) 22 | 23 | import os 24 | from datetime import datetime 25 | from PyQt5 import QtCore, QtGui, QtWidgets 26 | from PyQt5.QtGui import QImage, QIcon, QPixmap 27 | from PyQt5.QtCore import QCoreApplication, QThread 28 | from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox, QInputDialog 29 | 30 | 31 | # 导入信息采集框界面 32 | from ui import RandomCheckUI 33 | 34 | # 将根目录(execute所在目录)添加到环境变量 35 | from utils2.GlobalVar import add_path_to_sys, connect_to_sql 36 | rootdir = add_path_to_sys() 37 | 38 | 39 | class RCDialog(QWidget): 40 | def __init__(self): 41 | super().__init__() 42 | 43 | self.Dialog = RandomCheckUI.Ui_Form() 44 | self.Dialog.setupUi(self) 45 | 46 | # 实现路径错误提示,方便定位错误 47 | self.current_filename = os.path.basename(__file__) 48 | 49 | try: 50 | # 设置窗口名称和图标 51 | self.setWindowTitle("随机点名答题系统") 52 | self.setWindowIcon(QIcon(f'{rootdir}/logo_imgs/fcb_logo.jpg')) 53 | except FileNotFoundError as e: 54 | print("[ERROR] UI背景图片路径不正确!(source file: {})".format(self.current_filename), e) 55 | # else: 56 | # print("[INFO] 设置icon成功!") 57 | 58 | self.Dialog.pb_connect_db.clicked.connect(self.get_id_name_from_db) 59 | 60 | self.Dialog.pb_start.clicked.connect(self.start_random_check) 61 | 62 | self.Dialog.pb_success.clicked.connect(self.answer_success) 63 | 64 | self.Dialog.pb_fail.clicked.connect(self.answer_fail) 65 | 66 | 67 | self.Dialog.pb_other.clicked.connect(self.answer_other) 68 | 69 | self.get_id_name_from_db() 70 | 71 | # 核验本地人脸与数据库信息是否一致 72 | def get_id_name_from_db(self): 73 | try: 74 | db, cursor = connect_to_sql() 75 | except ConnectionAbortedError as e: 76 | self.ui.textBrowser_log.append('[INFO] 连接数据库失败,请检查配置信息!') 77 | else: 78 | sql = "select id, name from students" 79 | # 执行查询 80 | cursor.execute(sql) 81 | results = cursor.fetchall() 82 | self.student_ids = [] 83 | self.student_names = [] 84 | for item in results: 85 | self.student_ids.append(item[0]) 86 | self.student_names.append(item[1]) 87 | # 初始化点名列表 88 | self.random_check_names = deepcopy(self.student_names) 89 | self.random_check_ids = deepcopy(self.student_ids) 90 | 91 | # 随机点名 92 | def start_random_check(self): 93 | try: 94 | if len(self.random_check_ids) == 0 or len(self.random_check_names) == 0: 95 | box_choose = QMessageBox.information(self, "Notice", "已经遍历完毕,是否重新开始点名?", QMessageBox.Yes | QMessageBox.No) 96 | if box_choose == QMessageBox.Yes: 97 | self.random_check_names = deepcopy(self.student_names) 98 | self.random_check_ids = deepcopy(self.student_ids) 99 | else: 100 | pass 101 | else: 102 | self.rc_id = random.choice(self.random_check_ids) 103 | self.rc_name = self.random_check_names[self.random_check_ids.index(self.rc_id)] 104 | self.random_check_ids.remove(self.rc_id) 105 | self.random_check_names.remove(self.rc_name) 106 | 107 | self.Dialog.lcdNumber_id.display(self.rc_id) 108 | self.Dialog.textBrowser_name.append(self.rc_name) 109 | 110 | # 获取系统时间,保存到秒 111 | self.current_time = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 112 | 113 | except Exception as e: 114 | QMessageBox.critical(self, "Error", "[Error] 随机点名列表未定义!请先连接数据库!", QMessageBox.Ok) 115 | print("[Exception]: ", e) 116 | 117 | # 成功回答 118 | def answer_success(self): 119 | print(self.rc_id, self.rc_name, self.current_time, "成功回答") 120 | 121 | # 回答失败 122 | def answer_fail(self): 123 | print(self.rc_id, self.rc_name, self.current_time, "回答失败") 124 | 125 | 126 | # 退出 127 | def answer_other(self): 128 | self.close() 129 | 130 | def handle_click(self): 131 | if not self.isVisible(): 132 | self.show() 133 | -------------------------------------------------------------------------------- /utils2/__pycache__/Detaillog.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/utils2/__pycache__/Detaillog.cpython-38.pyc -------------------------------------------------------------------------------- /utils2/__pycache__/GeneratorModel.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/utils2/__pycache__/GeneratorModel.cpython-38.pyc -------------------------------------------------------------------------------- /utils2/__pycache__/GlobalVar.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/utils2/__pycache__/GlobalVar.cpython-38.pyc -------------------------------------------------------------------------------- /utils2/__pycache__/InfoDialog.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/utils2/__pycache__/InfoDialog.cpython-38.pyc -------------------------------------------------------------------------------- /utils2/__pycache__/InfoUI.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/utils2/__pycache__/InfoUI.cpython-38.pyc -------------------------------------------------------------------------------- /utils2/__pycache__/MainUI.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/utils2/__pycache__/MainUI.cpython-38.pyc -------------------------------------------------------------------------------- /utils2/__pycache__/RandomCheck.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/utils2/__pycache__/RandomCheck.cpython-38.pyc -------------------------------------------------------------------------------- /utils2/microsoft.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Memory555/Student-behavior-system/198313230e7c7ba4d5f762afa67b2b6df8a2700f/utils2/microsoft.ttf --------------------------------------------------------------------------------