暑假学校软工实践项目,节选了一部分上传,可以实现手动上传图片和拍照保存,数据库的表需要name, encoding_str两个字段
代码如下:
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import cv2
import face_recognition
import pymysql
import numpy as np
class Ui_MainWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent) # 父类的构造函数
self.timer_camera = QtCore.QTimer() # 定义定时器,用于控制显示视频的帧率
self.cap = cv2.VideoCapture() # 视频流
self.CAM_NUM = 0 # 为0时表示视频流来自笔记本内置摄像头
self.set_ui() # 初始化程序界面
self.slot_init() # 初始化槽函数
'''程序界面布局'''
def set_ui(self):
self.__layout_main = QtWidgets.QHBoxLayout() # 总布局
self.__layout_fun_button = QtWidgets.QVBoxLayout() # 按键布局
self.__layout_data_show = QtWidgets.QVBoxLayout() # 数据(视频)显示布局
self.button_open_camera = QtWidgets.QPushButton('打开相机') # 建立用于打开摄像头的按键
self.button_close = QtWidgets.QPushButton('退出') # 建立用于退出程序的按键
self.button_save = QtWidgets.QPushButton('保存')
self.button_load = QtWidgets.QPushButton('上传')
self.nameLineEdit = QtWidgets.QLineEdit("请输入姓名")
self.button_open_camera.setMinimumHeight(50) # 设置按键大小
self.button_close.setMinimumHeight(50)
self.button_save.setMinimumHeight(50)
self.button_load.setMinimumHeight(50)
self.button_close.move(10, 100) # 移动按键
'''信息显示'''
self.label_show_camera = QtWidgets.QLabel() # 定义显示视频的Label
self.label_show_camera.setFixedSize(641, 481) # 给显示视频的Label设置大小为641x481
'''把按键加入到按键布局中'''
self.__layout_fun_button.addWidget(self.nameLineEdit)
self.__layout_fun_button.addWidget(self.button_open_camera) # 把打开摄像头的按键放到按键布局中
self.__layout_fun_button.addWidget(self.button_load)
self.__layout_fun_button.addWidget(self.button_close) # 把退出程序的按键放到按键布局中
'''把某些控件加入到总布局中'''
self.__layout_main.addLayout(self.__layout_fun_button) # 把按键布局加入到总布局中
self.__layout_main.addWidget(self.label_show_camera) # 把用于显示视频的Label加入到总布局中
'''总布局布置好后就可以把总布局作为参数传入下面函数'''
self.setLayout(self.__layout_main) # 到这步才会显示所有控件
'''初始化所有槽函数'''
def slot_init(self):
self.button_open_camera.clicked.connect(
self.button_open_camera_clicked) # 若该按键被点击,则调用button_open_camera_clicked()
self.timer_camera.timeout.connect(self.show_camera) # 若定时器结束,则调用show_camera()
self.button_close.clicked.connect(self.close) # 若该按键被点击,则调用close(),注意这个close是父类QtWidgets.QWidget自带的,会关闭程序
self.button_save.clicked.connect(self.save_photo)
self.button_load.clicked.connect(self.load_photo)
'''槽函数之一'''
def load_photo(self):
path, _ = QtWidgets.QFileDialog.getOpenFileName(self, '选择图片', 'c:\\', 'Image files(*.jpeg *.jpg *.gif *.png)')
name = self.nameLineEdit.text()
if name=="":
print("保存失败")
elif path=="":
print("保存失败")
else:
load_image(path, name)
print("上传成功")
def save_photo(self):
ret, frame = self.cap.read()
name = self.nameLineEdit.text()
# cv2.imwrite("fangjian.jpg", frame)
if name=="":
print("保存失败")
else:
load_image_video(frame, name)
print("保存成功")
def button_open_camera_clicked(self):
if self.timer_camera.isActive() == False: # 若定时器未启动
flag = self.cap.open(self.CAM_NUM) # 参数是0,表示打开笔记本的内置摄像头,参数是视频文件路径则打开视频
if flag == False: # flag表示open()成不成功
msg = QtWidgets.QMessageBox.warning(self, 'warning', "请检查相机于电脑是否连接正确", buttons=QtWidgets.QMessageBox.Ok)
else:
self.timer_camera.start(30) # 定时器开始计时30ms,结果是每过30ms从摄像头中取一帧显示
self.button_save.setHidden(False)
self.button_open_camera.setText('关闭相机')
else:
self.timer_camera.stop() # 关闭定时器
self.cap.release() # 释放视频流
self.label_show_camera.clear() # 清空视频显示区域
self.button_save.setHidden(True)
self.button_open_camera.setText('打开相机')
def show_camera(self):
self.__layout_fun_button.addWidget(self.button_save)
flag, self.image = self.cap.read() # 从视频流中读取
show = cv2.resize(self.image, (640, 480)) # 把读到的帧的大小重新设置为 640x480
show = cv2.cvtColor(show, cv2.COLOR_BGR2RGB) # 视频色彩转换回RGB,这样才是现实的颜色
showImage = QtGui.QImage(show.data, show.shape[1], show.shape[0],
QtGui.QImage.Format_RGB888) # 把读取到的视频数据变成QImage形式
self.label_show_camera.setPixmap(QtGui.QPixmap.fromImage(showImage)) # 往显示视频的Label里 显示QImage
# 加载图像及获取人脸特征
def load_image(input_image, input_name):
# 加载本地图像文件到一个numpy ndarray类型的对象上
image = cv2.imread(input_image)
# 返回图像中每个面的128维人脸编码
# 图像中可能存在多张人脸,取下标为0的人脸编码,表示识别出来的最清晰的人脸
image_face_encoding = face_recognition.face_encodings(image)[0]
# 将numpy array类型转化为列表
encoding__array_list = image_face_encoding.tolist()
# 将列表里的元素转化为字符串
encoding_str_list = [str(i) for i in encoding__array_list]
# 拼接列表里的字符串
encoding_str = ','.join(encoding_str_list)
# 被识别者姓名
name = input_name
# 将人脸特征编码存进数据库
save_encoding(encoding_str, name)
def load_image_video(image, input_name):
# 返回图像中每个面的128维人脸编码
# 图像中可能存在多张人脸,取下标为0的人脸编码,表示识别出来的最清晰的人脸
image_face_encoding = face_recognition.face_encodings(image)[0]
# 将numpy array类型转化为列表
encoding__array_list = image_face_encoding.tolist()
# 将列表里的元素转化为字符串
encoding_str_list = [str(i) for i in encoding__array_list]
# 拼接列表里的字符串
encoding_str = ','.join(encoding_str_list)
# 被识别者姓名
name = input_name
# 将人脸特征编码存进数据库
save_encoding(encoding_str, name)
# 人脸特征信息保存
def save_encoding(encoding_str, name):
# 创建数据库连接对象
conn = pymysql.connect(
# 数据库的IP地址
host="127.0.0.1",
# 数据库用户名称
user="root",
# 数据库用户密码
password="123",
# 数据库名称
db="hd_faces",
# 数据库端口名称
port=3306,
# 数据库的编码方式 注意是utf8
charset="utf8"
)
# 使用 cursor() 方法创建一个游标对象 cursor
cursor = conn.cursor()
# SQL插入语句
query_sql = 'select * from face where name='+'"'+name+'"'
print("query sql:",query_sql)
# 执行sql语句
cursor.execute(query_sql)
results = cursor.fetchall()
if len(results) == 0:
# SQL插入语句
insert_sql = "insert into face(name,encoding) values(%s,%s)"
try:
# 执行sql语句
cursor.execute(insert_sql, (name, encoding_str))
# 提交到数据库执行
conn.commit()
except Exception as e:
# 如果发生错误则回滚并打印错误信息
conn.rollback()
print(e)
else:
print("已有记录,勿重复添加!")
# 关闭游标
cursor.close()
# 关闭数据库连接
conn.close()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv) # 固定的,表示程序应用
ui = Ui_MainWindow() # 实例化Ui_MainWindow
ui.show() # 调用ui的show()以显示。同样show()是源于父类QtWidgets.QWidget的
sys.exit(app.exec_()) # 不加这句,程序界面会一闪而过