参考学习文章:
Keras|基于深度学习的人脸表情识别系统
PyQt5+QtDesigner编写摄像头界面程序(一)——pyqt5、qtdesigner安装和环境设置
本次设计参考以上两篇文章,非常感谢博主们的分享,我收获良多!
本设计是基于Python的人脸表情识别和情绪分类,在TensorFlow框架下,使用其内部高级API——Keras搭建训练模型,并利用OpenCV库中的相关函数共同实现人脸识别、人脸表情识别、情绪分类的目的,最后借助PyQt5增加界面,通过界面可对输入的画面进行调整颜色、调整曝光度、亮度等操作,还可以通过“开始”、“暂停”、“结束”功能控制识别过程的通断,通过“录像”功能保存所需的画面,从而使整个设计更友好、更加人性化。
本设计选用相对简单易上手的Python语言,其具体的介绍可查看Python百度百科,此处不做过多陈述。
具体内容请查看TensorFlow官网。TensorFlow是Google开源的基于数据流图的机器学习框架,支持python和c++程序开发语言。TensorFlow支持卷积神经网络(CNN)和循环卷积网络(RNN),以及RNN的一个特例长短期记忆网络(LSTM),以上都是目前在计算机视觉、语音识别、自然语言处理方面最流行的深度神经网络模型。
在TensorFlow官网中介绍了TensorFlow的以下六大优势:
●高度灵活性
●真正的可移植性
●将科研和产品结合在一起
●自动求微分
●多语言支持
●最优化性能
因为本设计仅使用TensorFlow框架作为程序运行环境,主要是使用其高级API——Keras,因此不过多介绍。
具体内容请查看Keras官网。Keras是一个高级的Python神经网络框架。Keras已经被添加到TensorFlow中,成为其默认的框架,作为TensorFlow高级API之一。Keras作为TensorFlow的高层封装,可以与TensorFlow联合使用,用它快速搭建原型。
Keras是高度封装的,非常适合新手使用,代码更新速度较快,有大量公开示例代码,文档和讨论区也比较完善。
在Keras官网描述了其以下几个有点:
●模块化:模型的各部分,如神经层、成本函数、优化器、初始化、激活函数、规范化都是独立的模块,可以组合在一起来创建模型。
●极简主义:每个模块都保持简短和简单。
●易扩展性:很容易添加新模块,因此Keras更适合做进一步的高级研究。
●使用Python语言:模型使用Python实现,非常易于调试和扩展。
Python学习笔记(一)
Python+OpenCV(一)——基础操作
Python+Tensorflow学习(二)——初试keras
Win10+Python3.6.5+Anaconda3-5.2.0+Tensorflow安装
本设计主要代码结构整体上分为两大部分,在各部分再细分以下几点:
代码主要结构主要分为两大部分:
一、人脸表情识别和情绪分类部分
(一)卷积神经网络模型的训练
1数据集的获取
2.加载pre-model网络与权重;
3.训练模型
4.保存训练模型
(二)面部表情识别
1.打开摄像头
2.人脸识别
3.图像预处理
4.人脸表情识别
5.情绪分类
二、PyQt5界面创建
1.PyQt+QtDesigner及opencv等工具的安装与设置;
2.基于QtDesigner的界面设计;
3.各部分程序的编写;
4.笔记本摄像头的读取、显示和参数控制;
以下逐点介绍。
在人脸表情识别和情绪分类部分分为过程:卷积神经网络模型的训练与面部表情的识别。
为了节约素材收集时间,同时也为更公平的评价模型以及人脸表情识别分类器的性能,我们采用使用公开的数据集。
本次设计使用了kaggle面部表情识别竞赛所使用的fer2013人脸表情数据库,图片统一以csv的格式存储,利用python可将csv文件转为单通道灰度图片,并根据标签将其分类在不同的文件夹中。
首先根据用途label分成三个csv(分别是训练集(train)、测试集(test)、验证集(val));
# -*- coding = utf-8 -*-
# @Time : 2021/8/7 09:12
# @Author : 西兰花
# @File : convert_fer2013.py
# @Software : PyCharm
"""
根据用途label分成三个csv(分别是训练集(train)、测试集(test)、验证集(val));
"""
import csv
database_path = 'F:/test05/表情识别/表情识别/emotion_classifier-master/fer2013/'
datasets_path = './fer2013/'
csv_file = database_path+'fer2013.csv'
train_csv = datasets_path+'train.csv'
val_csv = datasets_path+'val.csv'
test_csv = datasets_path+'test.csv'
with open(csv_file) as f:
csvr = csv.reader(f)
header = next(csvr)
print(header)
rows = [row for row in csvr]
trn = [row[:-1] for row in rows if row[-1] == 'Training']
csv.writer(open(train_csv, 'w+'), lineterminator='\n').writerows([header[:-1]] + trn)
print(len(trn))
val = [row[:-1] for row in rows if row[-1] == 'PublicTest']
csv.writer(open(val_csv, 'w+'), lineterminator='\n').writerows([header[:-1]] + val)
print(len(val))
tst = [row[:-1] for row in rows if row[-1] == 'PrivateTest']
csv.writer(open(test_csv, 'w+'), lineterminator='\n').writerows([header[:-1]] + tst)
print(len(tst))
文件夹“test”存放测试集数据;
文件夹“train”存放训练集数据;
文件夹“valt”存放验证集数据;
各文件夹中将图片分为七种,并存放在相应标签(label)中,标签“0”至“7”分别对应情绪“angry”、 “disgust”、“fear”、“happy”、“sad”、“surprise”、“neutral”。
将图像转换为单通道灰度图:
# -*- coding = utf-8 -*-
# @Time : 2021/8/7 09:18
# @Author : 西兰花
# @File : convert_csv2gray.py
# @Software : PyCharm
"""
将图像转换为单通道灰度图
"""
import csv
import os
from PIL import Image
import numpy as np
datasets_path = r'.\fer2013'
train_csv = os.path.join(datasets_path, 'train.csv')
val_csv = os.path.join(datasets_path, 'val.csv')
test_csv = os.path.join(datasets_path, 'test.csv')
train_set = os.path.join(datasets_path, 'train')
val_set = os.path.join(datasets_path, 'val')
test_set = os.path.join(datasets_path, 'test')
for save_path, csv_file in [(train_set, train_csv), (val_set, val_csv), (test_set, test_csv)]:
if not os.path.exists(save_path):
os.makedirs(save_path)
num = 1
with open(csv_file) as f:
csvr = csv.reader(f)
header = next(csvr)
for i, (label, pixel) in enumerate(csvr):
pixel = np.asarray([float(p) for p in pixel.split()]).reshape(48, 48)
subfolder = os.path.join(save_path, label)
if not os.path.exists(subfolder):
os.makedirs(subfolder)
im = Image.fromarray(pixel).convert('L')
image_name = os.path.join(subfolder, '{:05d}.jpg'.format(i))
# print(image_name)
im.save(image_name)
数据集已成功获取,并按照label进行分类,第二步是建立卷积神经网络模型(CNN),本步骤是实现人脸表情识别关键一步。
在前人的基础上,本设计在输入层后加入了1*1的卷积层,使输入增加了非线性的表示、加深了网络、提升了模型的表达能力,同时基本不增加计算量。
模型代码如下:
def build_model(self):
self.model = Sequential() # Sequential模型是keras两种模型之一,另一种是model模型
"""构建模型"""
# 第一层卷积,需要指定input_shape的参数
self.model.add(Conv2D(32, (1, 1), strides=1, padding='same', input_shape=(img_size, img_size, 1)))
self.model.add(Activation('relu')) # 激活函数
self.model.add(Conv2D(32, (5, 5), padding='same'))
self.model.add(Activation('relu'))
self.model.add(MaxPooling2D(pool_size=(2, 2))) # 最大池化
self.model.add(Conv2D(32, (3, 3), padding='same'))
self.model.add(Activation('relu'))
self.model.add(MaxPooling2D(pool_size=(2, 2)))
self.model.add(Conv2D(64, (5, 5), padding='same'))
self.model.add(Activation('relu'))
self.model.add(MaxPooling2D(pool_size=(2, 2)))
self.model.add(Flatten())
self.model.add(Dense(2048)) # 全连接层
self.model.add(Activation('relu'))
self.model.add(Dropout(0.5))
self.model.add(Dense(1024))
self.model.add(Activation('relu'))
self.model.add(Dropout(0.5))
self.model.add(Dense(num_classes))
self.model.add(Activation('softmax'))
self.model.summary() # 显示训练模型结构
通过 self.model.summary() 函数查看训练模型结构;训练模型结构如下:
模型:Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_1 (Conv2D) (None, 48, 48, 32) 64
_________________________________________________________________
activation_1 (Activation) (None, 48, 48, 32) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 48, 48, 32) 25632
_________________________________________________________________
activation_2 (Activation) (None, 48, 48, 32) 0
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 24, 24, 32) 0
_________________________________________________________________
conv2d_3 (Conv2D) (None, 24, 24, 32) 9248
_________________________________________________________________
activation_3 (Activation) (None, 24, 24, 32) 0
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 12, 12, 32) 0
_________________________________________________________________
conv2d_4 (Conv2D) (None, 12, 12, 64) 51264
_________________________________________________________________
activation_4 (Activation) (None, 12, 12, 64) 0
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 6, 6, 64) 0
_________________________________________________________________
flatten_1 (Flatten) (None, 2304) 0
_________________________________________________________________
dense_1 (Dense) (None, 2048) 4720640
_________________________________________________________________
activation_5 (Activation) (None, 2048) 0
_________________________________________________________________
dropout_1 (Dropout) (None, 2048) 0
_________________________________________________________________
dense_2 (Dense) (None, 1024) 2098176
_________________________________________________________________
activation_6 (Activation) (None, 1024) 0
_________________________________________________________________
dropout_2 (Dropout) (None, 1024) 0
_________________________________________________________________
dense_3 (Dense) (None, 7) 7175
_________________________________________________________________
activation_7 (Activation) (None, 7) 0
=================================================================
Total params: 6,912,199
Trainable params: 6,912,199
Non-trainable params: 0
在训练过程中使用ImageDataGenerator()函数实现数据增强,并通过flow_from_directory()函数根据文件名划分label;优化算法选择SGD,损失函数选择categorical_crossentropy;
def train_model(self):
sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
# 指定损失函数和优化器
self.model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])
# 自动扩充训练样本
train_datagen = ImageDataGenerator(
rescale=1./255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True)
# 归一化验证集
val_datagen = ImageDataGenerator(rescale=1./255)
eval_datagen = ImageDataGenerator(rescale=1./255)
# 以文件分类名划分label
train_generator = train_datagen.flow_from_directory(
root_path+'/train',
target_size=(img_size, img_size),
color_mode='grayscale',
batch_size=batch_siz,
class_mode='categorical')
val_generator = val_datagen.flow_from_directory(
root_path+'/val',
target_size=(img_size,img_size),
color_mode='grayscale',
batch_size=batch_siz,
class_mode='categorical')
eval_generator = eval_datagen.flow_from_directory(
root_path+'/test',
target_size=(img_size,img_size),
color_mode='grayscale',
batch_size=batch_siz,
class_mode='categorical')
# early_stopping = EarlyStopping(monitor='loss',patience=3)
# 用model.fit()函数来训练模型,输入训练集和测试数据
history_fit = self.model.fit_generator(
train_generator,
steps_per_epoch=800/(batch_siz/32), # 28709
nb_epoch=nb_epoch,
validation_data=val_generator,
validation_steps=2000,
# callbacks=[early_stopping]
)
# history_eval=self.model.evaluate_generator(eval_generator, steps=2000) # 用model.evaluate_generator()函数来评估模型
history_predict=self.model.predict_generator(eval_generator, steps=2000) # 训练模型预测
def save_model(self): # 存储训练模型数据
# 使用model.to_json()函数只保存模型的结构,而不包含其权重及训练的配置(损失函数、优化器)
model_json = self.model.to_json()
with open(root_path+"/model_json.json", "w") as json_file:
json_file.write(model_json)
self.model.save_weights(root_path+'/model_weight.h5')
self.model.save(root_path+'/model.h5')
print('model saved')
到此,人脸表情识别训练模型已构建完毕。
keras模型分为model和weight两部分
保存model方法:通过json文件或yaml文件
json文件:
model_json = model.to_json()
with open(“model.json”, “w”) as json_file:
json_file.write(model_json)
yaml文件:
yaml_string = model.to_yaml()
保存权重的方法:通过保存权重(系数)
HDF5文件:
model.save_weights(“model.h5”)
print(“Saved model to disk”)
载入model的方法
json & hdf5:
#load json and create model
json_file = open(‘model.json’, ‘r’)
loaded_model_json = json_file.read()
json_file.close()
loaded_model = model_from_json(loaded_model_json)
from keras.models import load_model
model = load_model(‘model.h5’)
载入权重:
#load weights into new model
loaded_model.load_weights(“model.h5”)
print(“Loaded model from disk”)
emo_labels = ['angry', 'disgust', 'fear', 'happy', 'sad', 'surprise', 'neutral']
num_class = len(emo_labels)
# 使用json文件打开keras中model部分
json_file = open(model_path+'model_json.json')
loaded_model_json = json_file.read()
json_file.close()
model = model_from_json(loaded_model_json)
# keras.models.load_model()读取网络、权重
# keras.models.load_weights()仅读取权重
# 打开hdf5文件,即权重存放的文件
model.load_weights(model_path+'model_weight.h5')
在Python中提供强大的视觉处理库——OpenCV,我们可以里OpenCV库中提供的函数实现人脸识别,进而对识别到的人脸进行灰度处理、裁切、翻转、几何归一化等处理,并通过双线内插值算法(cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]))将图像统一重塑为48*48像素。
通过 cv2.VideoCapture()函数调用系统摄像头。值为0表示调取系统摄像头,值为路径+文件名(仅限视频/图片)表示打开视频/图片文件;
# 捕获指定摄像头的实时视频流
cap = cv2.VideoCapture(0)
使用haarcascade_frontalface_alt.xml级联器进行人脸识别,或者使用lbpcascade_frontalcatface.xml级联器;之前笔者分别使用这两个级联器进行人脸识别,两者识别率不相上下,也可能是跟环境有关,因此笔者认为二者都适用,具体测试可查看Python+OpenCV(十七)——人脸识别。
# 人脸识别分类器本地存储路径
cascade_path = root_path+"haarcascade_frontalface_alt.xml" # 哈尔级联器
# 图像灰化,降低计算复杂度
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 使用人脸识别分类器,读入分类器
cascade = cv2.CascadeClassifier(cascade_path)
# 利用分类器识别出哪个区域为人脸
faceRects = cascade.detectMultiScale(frame_gray, scaleFactor=1.1, minNeighbors=1, minSize=(120, 120))
if len(faceRects) > 0:
for faceRect in faceRects:
x, y, w, h = faceRect
images = []
rs_sum = np.array([0.0]*num_class)
# 截取脸部图像提交给模型识别表情
image = frame_gray[y: y + h, x: x + w] # 灰度处理
# cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]])
# src:[必需]原图像
# dsize:[必需]输出图像所需大小
# fx:[必需]沿水平轴的比例因子
# fy:[必需]沿垂直轴的比例因子
# interpolation:[必需]插值方式
image = cv2.resize(image, (img_size, img_size)) # 将灰度处理后的图像按比例调整为像素大小48*48
image = image*(1./255)
images.append(image) # 在末尾添加新的图片
images.append(cv2.flip(image, 1))
images.append(cv2.resize(image[2:45, :], (img_size, img_size)))
使用之前训练好的模型进行预测,对多个处理过的脸部预测结果进行线性加权融合,最后得出预测结果;
def predict_emotion(face_img):
face_img = face_img * (1. / 255)
resized_img = cv2.resize(face_img, (img_size, img_size)) # ,interpolation=cv2.INTER_LINEAR
rsz_img = []
rsh_img = []
results = []
# print (len(resized_img[0]),type(resized_img))
rsz_img.append(resized_img[:, :]) # resized_img[1:46,1:46]
rsz_img.append(resized_img[2:45, :])
rsz_img.append(cv2.flip(rsz_img[0], 1))
# rsz_img.append(cv2.flip(rsz_img[1],1))
'''rsz_img.append(resized_img[0:45,0:45])
rsz_img.append(resized_img[2:47,0:45])
rsz_img.append(resized_img[2:47,2:47])
rsz_img.append(cv2.flip(rsz_img[2],1))
rsz_img.append(cv2.flip(rsz_img[3],1))
rsz_img.append(cv2.flip(rsz_img[4],1))'''
i = 0
for rsz_image in rsz_img:
rsz_img[i] = cv2.resize(rsz_image, (img_size, img_size))
# =========================
# cv2.imshow('%d'%i,rsz_img[i])
i += 1
# why 4 parameters here, what's it means?
for rsz_image in rsz_img:
rsh_img.append(rsz_image.reshape(1, img_size, img_size, 1))
i = 0
for rsh_image in rsh_img:
list_of_list = model.predict_proba(rsh_image, batch_size=32, verbose=1) # predict
result = [prob for lst in list_of_list for prob in lst]
results.append(result)
return results
predict_proba()函数:模型预测输入样本属于每种类别的概率,概率和为1,每个位置的概率分别对应classes_中对应位置的类别标签。predict_proba输出概率最大值索引位置对应的classes_元素就是样本所属的类别。
list_of_list = model.predict_proba(image, batch_size=32, verbose=1) # predict
result = [prob for lst in list_of_list for prob in lst]
rs_sum += np.array(result)
print(rs_sum)
label = np.argmax(rs_sum)
emo = emo_labels[label]
print('Emotion : ', emo) # 输出情绪分类结果
(具体操作请参考本文章开头的参考文章)
由于工程庞大,以下仅展示主要代码:
(代码中主要功能基本上附上了注释,如有疑惑部分可查看本文章开头列出的参考文章,或者评论本文章皆可)
# -*- coding = utf-8 -*-
# @Time : 2021/8/19 19:45
# @Author : 西兰花
# @File : QtOpenCV.py
# @Software : PyCharm
"""
代码主要结构主要分为两大部分:
一、人脸表情识别和情绪分类部分
(一)卷积神经网络模型的训练
1数据集的获取
2.加载pre-model网络与权重;
3.训练模型
4.保存训练模型
(二)面部表情识别
1.打开摄像头
2.人脸识别
3.图像预处理
4.人脸表情识别
5.情绪分类
二、PyQt5界面创建
1.PyQt+QtDesigner及opencv等工具的安装与设置;
2.基于QtDesigner的界面设计;
3.各部分程序的编写;
4.笔记本摄像头的读取、显示和参数控制;
"""
# PyQt界面部分
from OboardCamDisp import Ui_MainWindow
# import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog
from PyQt5.QtCore import QTimer, QCoreApplication
from PyQt5.QtGui import QPixmap
# import cv2
import qimage2ndarray
import time
# 人脸表情识别+情绪分类部分
import cv2
import sys
import numpy as np
from keras.models import model_from_json
root_path = './pic/'
model_path = root_path+'/model/'
img_size = 48 # 图像像素大小48*48
# load json and create model arch
"""
keras模型分为model和weight两部分
保存model方法:通过json文件或yaml文件
json文件:
model_json = model.to_json()
with open("model.json", "w") as json_file:
json_file.write(model_json)
yaml文件:
yaml_string = model.to_yaml()
保存权重的方法:通过保存权重(系数)
HDF5文件:
model.save_weights("model.h5")
print("Saved model to disk")
载入model的方法
json & hdf5:
# load json and create model
json_file = open('model.json', 'r')
loaded_model_json = json_file.read()
json_file.close()
loaded_model = model_from_json(loaded_model_json)
from keras.models import load_model
model = load_model('model.h5')
载入权重:
# load weights into new model
loaded_model.load_weights("model.h5")
print("Loaded model from disk")
"""
emo_labels = ['angry', 'disgust', 'fear', 'happy', 'sad', 'surprise', 'neutral']
num_class = len(emo_labels)
# 使用json文件打开keras中model部分
json_file = open(model_path+'model_json.json')
loaded_model_json = json_file.read()
json_file.close()
model = model_from_json(loaded_model_json)
# keras.models.load_model()读取网络、权重
# keras.models.load_weights()仅读取权重
# 打开hdf5文件,即权重存放的文件
model.load_weights(model_path+'model_weight.h5')
# PyQt5创建界面
class CamShow(QMainWindow, Ui_MainWindow): # 定义一个类,实现程序的主要功能
def __del__(self):
try:
self.camera.release() # 释放资源
except:
return
def __init__(self, parent=None): # 初始化函数
super(CamShow, self).__init__(parent)
self.setupUi(self)
self.PrepSliders() # PrepSliders()函数实现各个slider和对应的spinbox的关联,保证两个控件的值始终相等
self.PrepWidgets() # PrepWidgets()函数初始化各个控件
self.PrepParameters() # PrepParameters()函数定义并初始化程序运行过程中会用到的变量
# CallBackFunctions()函数各个控件背后的功能函数的集合,定义了在程序界面上进行某项操作后实际执行的代码
self.CallBackFunctions()
"""Timer = QTimer()函数、Timer.timeout.connect(self.TimerOutFun)函数
计时器的定义和调用,Timer = QTimer()函数定义了一个定时器,等我们执行计时器开始的代码后,该计时器就开始计时,
每次计时结束都会调用一次函数TimerOutFun,通过计时器实现对摄像头图像的循环读取和显示"""
self.Timer = QTimer()
self.Timer.timeout.connect(self.TimerOutFun)
# Slider和SpinBox
def PrepSliders(self):
self.RedColorSld.valueChanged.connect(self.RedColorSpB.setValue)
self.RedColorSpB.valueChanged.connect(self.RedColorSld.setValue)
self.GreenColorSld.valueChanged.connect(self.GreenColorSpB.setValue)
self.GreenColorSpB.valueChanged.connect(self.GreenColorSld.setValue)
self.BlueColorSld.valueChanged.connect(self.BlueColorSpB.setValue)
self.BlueColorSpB.valueChanged.connect(self.BlueColorSld.setValue)
self.ExpTimeSld.valueChanged.connect(self.ExpTimeSpB.setValue)
self.ExpTimeSpB.valueChanged.connect(self.ExpTimeSld.setValue)
self.GainSld.valueChanged.connect(self.GainSpB.setValue)
self.GainSpB.valueChanged.connect(self.GainSld.setValue)
self.BrightSld.valueChanged.connect(self.BrightSpB.setValue)
self.BrightSpB.valueChanged.connect(self.BrightSld.setValue)
self.ContrastSld.valueChanged.connect(self.ContrastSpB.setValue)
self.ContrastSpB.valueChanged.connect(self.ContrastSld.setValue)
# 控件初始化
def PrepWidgets(self):
self.PrepCamera()
self.StopBt.setEnabled(False)
self.RecordBt.setEnabled(False)
self.GrayImgCkB.setEnabled(False)
self.RedColorSld.setEnabled(False)
self.RedColorSpB.setEnabled(False)
self.GreenColorSld.setEnabled(False)
self.GreenColorSpB.setEnabled(False)
self.BlueColorSld.setEnabled(False)
self.BlueColorSpB.setEnabled(False)
self.ExpTimeSld.setEnabled(False)
self.ExpTimeSpB.setEnabled(False)
self.GainSld.setEnabled(False)
self.GainSpB.setEnabled(False)
self.BrightSld.setEnabled(False)
self.BrightSpB.setEnabled(False)
self.ContrastSld.setEnabled(False)
self.ContrastSpB.setEnabled(False)
# 初始化摄像头
def PrepCamera(self):
try:
# camera=cv2.VideoCapture(0)函数,调用OpenCV的VideoCapture函数打开摄像头
# 并使用变量self.camera代表该摄像头,参数0意味着打开笔记本自带的摄像头
self.camera = cv2.VideoCapture(0)
print("摄像头已打开!")
self.MsgTE.clear() # 清空文本框MsgTE中的内容
# 在文本框MsgTE中显示'Oboard camera connected.'
# append()函数表示在现有的内容后继续添加内容
# setPlainText函数,用括号中的文本覆盖原来的文本
self.MsgTE.append('Oboard camera connected.')
self.MsgTE.setPlainText()
except Exception as e:
self.MsgTE.clear()
self.MsgTE.append(str(e))
# 参数初始化
def PrepParameters(self):
# 变量self.RecordFlag,该值为0时不保存视频,为1时开始保存视频
self.RecordFlag = 0
# 变量self.RecordPath定义默认的文件存储路径
self.RecordPath = 'F:/test05/表情识别/表情识别/emotion_classifier-master/Qt/'
# self.FilePathLE.setText(self.RecordPath)将路径名显示在文本框FilePathLE中
self.FilePathLE.setText(self.RecordPath)
# self.Image_num定义读取图片的次数
self.Image_num = 0
# self.R、self.G、self.B为三个颜色通道的强度增益系数
self.R = 1
self.G = 1
self.B = 1
# 初始化摄像头参数控件的值,即读取摄像头的曝光、增益、亮度、对比度等参数,并将这些值显示在相应的控件上
self.ExpTimeSld.setValue(self.camera.get(15))
self.SetExposure()
self.GainSld.setValue(self.camera.get(14))
self.SetGain()
self.BrightSld.setValue(self.camera.get(10))
self.SetBrightness()
self.ContrastSld.setValue(self.camera.get(11))
self.SetContrast()
self.MsgTE.clear()
# 控件回调函数
def CallBackFunctions(self):
self.FilePathBt.clicked.connect(self.SetFilePath)
self.ShowBt.clicked.connect(self.StartCamera)
self.StopBt.clicked.connect(self.StopCamera)
self.RecordBt.clicked.connect(self.RecordCamera)
self.ExitBt.clicked.connect(self.ExitApp)
self.GrayImgCkB.stateChanged.connect(self.SetGray)
self.ExpTimeSld.valueChanged.connect(self.SetExposure)
self.GainSld.valueChanged.connect(self.SetGain)
self.BrightSld.valueChanged.connect(self.SetBrightness)
self.ContrastSld.valueChanged.connect(self.SetContrast)
self.RedColorSld.valueChanged.connect(self.SetR)
self.GreenColorSld.valueChanged.connect(self.SetG)
self.BlueColorSld.valueChanged.connect(self.SetB)
# 实现灰度转换
# 通道R
def SetR(self):
R = self.RedColorSld.value()
self.R = R/255
# 通道G
def SetG(self):
G = self.GreenColorSld.value()
self.G = G/255
# 通道B
def SetB(self):
B = self.BlueColorSld.value()
self.B = B/255
def SetContrast(self):
contrast_toset=self.ContrastSld.value()
try:
self.camera.set(11,contrast_toset)
self.MsgTE.setPlainText('The contrast is set to ' + str(self.camera.get(11)))
except Exception as e:
self.MsgTE.setPlainText(str(e))
def SetBrightness(self):
brightness_toset=self.BrightSld.value()
try:
self.camera.set(10,brightness_toset)
self.MsgTE.setPlainText('The brightness is set to ' + str(self.camera.get(10)))
except Exception as e:
self.MsgTE.setPlainText(str(e))
def SetGain(self):
gain_toset=self.GainSld.value()
try:
self.camera.set(14,gain_toset)
self.MsgTE.setPlainText('The gain is set to '+str(self.camera.get(14)))
except Exception as e:
self.MsgTE.setPlainText(str(e))
# 摄像头参数设置
def SetExposure(self):
try:
exposure_time_toset = self.ExpTimeSld.value()
self.camera.set(15,exposure_time_toset)
self.MsgTE.setPlainText('The exposure time is set to '+str(self.camera.get(15)))
except Exception as e:
self.MsgTE.setPlainText(str(e))
# 图像显示的颜色控制
def SetGray(self):
if self.GrayImgCkB.isChecked():
self.RedColorSld.setEnabled(False)
self.RedColorSpB.setEnabled(False)
self.GreenColorSld.setEnabled(False)
self.GreenColorSpB.setEnabled(False)
self.BlueColorSld.setEnabled(False)
self.BlueColorSpB.setEnabled(False)
else:
self.RedColorSld.setEnabled(True)
self.RedColorSpB.setEnabled(True)
self.GreenColorSld.setEnabled(True)
self.GreenColorSpB.setEnabled(True)
self.BlueColorSld.setEnabled(True)
self.BlueColorSpB.setEnabled(True)
# 摄像头的读取和显示
# 按键“开始”
def StartCamera(self):
self.ShowBt.setEnabled(False)
self.StopBt.setEnabled(True)
self.RecordBt.setEnabled(True)
self.GrayImgCkB.setEnabled(True)
if self.GrayImgCkB.isChecked() == 0:
self.RedColorSld.setEnabled(True)
self.RedColorSpB.setEnabled(True)
self.GreenColorSld.setEnabled(True)
self.GreenColorSpB.setEnabled(True)
self.BlueColorSld.setEnabled(True)
self.BlueColorSpB.setEnabled(True)
self.ExpTimeSld.setEnabled(True)
self.ExpTimeSpB.setEnabled(True)
self.GainSld.setEnabled(True)
self.GainSpB.setEnabled(True)
self.BrightSld.setEnabled(True)
self.BrightSpB.setEnabled(True)
self.ContrastSld.setEnabled(True)
self.ContrastSpB.setEnabled(True)
self.RecordBt.setText('录像')
self.Timer.start(1) # self.Timer.start(1)用来启动计时器,计时周期为1ms,即每隔1ms程序会自动调用一次TimerOutFun
self.timelb = time.clock()
# 保存图片或视频
def SetFilePath(self):
dirname = QFileDialog.getExistingDirectory(self, "浏览", '.')
if dirname:
self.FilePathLE.setText(dirname)
self.RecordPath = dirname+'/'
# 人脸表情识别+情绪分类主要部分
def TimerOutFun(self):
success, frame = self.camera.read()
if success:
self.Image = self.ColorAdjust(frame) # 调用图像色彩调节函数
self.DispImg() # 调用函数
self.Image_num += 1
if self.RecordFlag:
self.video_writer.write(frame)
if self.Image_num % 10 == 9: # 计算帧频
frame_rate = 10/(time.clock()-self.timelb)
self.FmRateLCD.display(frame_rate)
self.timelb = time.clock()
self.ImgWidthLCD.display(self.camera.get(3))
self.ImgHeightLCD.display(self.camera.get(4))
if len(sys.argv) != 1:
print("Usage:%s camera_id\r\n" % (sys.argv[0]))
sys.exit(0)
# 人脸识别分类器本地存储路径
cascade_path = root_path + "haarcascade_frontalface_alt.xml" # 哈尔级联器
# 图像灰化,降低计算复杂度
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 使用人脸识别分类器,读入分类器
cascade = cv2.CascadeClassifier(cascade_path)
# 利用分类器识别出哪个区域为人脸
faceRects = cascade.detectMultiScale(frame_gray, scaleFactor=1.1, minNeighbors=1, minSize=(120, 120))
if len(faceRects) > 0:
for faceRect in faceRects:
x, y, w, h = faceRect
images = []
rs_sum = np.array([0.0] * num_class)
# 截取脸部图像提交给模型识别表情
image = frame_gray[y: y + h, x: x + w] # 灰度处理
# cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]])
# src:[必需]原图像
# dsize:[必需]输出图像所需大小
# fx:[必需]沿水平轴的比例因子
# fy:[必需]沿垂直轴的比例因子
# interpolation:[必需]插值方式
image = cv2.resize(image, (img_size, img_size)) # 将灰度处理后的图像按比例调整为像素大小48*48
image = image * (1. / 255)
images.append(image) # 在末尾添加新的图片
images.append(cv2.flip(image, 1))
images.append(cv2.resize(image[2:45, :], (img_size, img_size)))
for img in images:
image = img.reshape(1, img_size, img_size, 1)
"""
predict_proba()函数:
模型预测输入样本属于每种类别的概率,概率和为1,每个位置的概率分别对应classes_中对应位置的类别标签。
predict_proba输出概率最大值索引位置对应的classes_元素就是样本所属的类别。
"""
list_of_list = model.predict_proba(image, batch_size=32, verbose=1) # predict
result = [prob for lst in list_of_list for prob in lst]
rs_sum += np.array(result)
print(rs_sum)
label = np.argmax(rs_sum)
emo = emo_labels[label]
print('Emotion : ', emo) # 输出情绪分类结果
# cv2.putText(frame, "Emotion: %s" % emo, (400, 400), cv2.FONT_HERSHEY_COMPLEX, 1, (255, 255, 0), 3)
# rectangle()框选识别到的人脸
# cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, thickness=2)
# font = cv2.FONT_HERSHEY_SIMPLEX
# 文字显示该表情分类结果
# cv2.putText(frame, '%s' % emo, (x + 30, y + 30), font, 1, (255, 0, 255), 4)
else:
self.MsgTE.clear()
self.MsgTE.setPlainText('Image obtaining failed.')
def ColorAdjust(self, img):
try:
B = img[:, :, 0]
G = img[:, :, 1]
R = img[:, :, 2]
B = B*self.B
G = G*self.G
R = R*self.R
img1 = img
img1[:, :, 0] = B
img1[:, :, 1] = G
img1[:, :, 2] = R
return img1
except Exception as e:
self.MsgTE.setPlainText(str(e))
# 检测人脸、色彩空间及格式转换
def DispImg(self):
if self.GrayImgCkB.isChecked():
img = cv2.cvtColor(self.Image, cv2.COLOR_BGR2GRAY)
else:
img = cv2.cvtColor(self.Image, cv2.COLOR_BGR2RGB)
qimg = qimage2ndarray.array2qimage(img)
self.DispLb.setPixmap(QPixmap(qimg))
self.DispLb.show()
# 按键“暂停”
def StopCamera(self):
if self.StopBt.text() == '暂停':
self.StopBt.setText('继续')
self.RecordBt.setText('保存')
self.Timer.stop()
elif self.StopBt.text() == '继续':
self.StopBt.setText('暂停')
self.RecordBt.setText('录像')
self.Timer.start(1)
# 按键“录像”
def RecordCamera(self):
tag = self.RecordBt.text()
if tag == '保存':
try:
image_name = self.RecordPath+'image'+time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))+'.jpg'
print(image_name)
cv2.imwrite(image_name, self.Image)
self.MsgTE.clear()
self.MsgTE.setPlainText('Image saved.')
except Exception as e:
self.MsgTE.clear()
self.MsgTE.setPlainText(str(e))
elif tag == '录像':
self.RecordBt.setText('停止')
video_name = self.RecordPath + 'video' + time.strftime('%Y%m%d%H%M%S', time.localtime(time.time())) + '.avi'
fps = self.FmRateLCD.value()
size = (self.Image.shape[1],self.Image.shape[0])
fourcc = cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')
self.video_writer = cv2.VideoWriter(video_name, fourcc, self.camera.get(5), size)
self.RecordFlag=1
self.MsgTE.setPlainText('Video recording...')
self.StopBt.setEnabled(False)
self.ExitBt.setEnabled(False)
elif tag == '停止':
self.RecordBt.setText('录像')
self.video_writer.release()
self.RecordFlag = 0
self.MsgTE.setPlainText('Video saved.')
self.StopBt.setEnabled(True)
self.ExitBt.setEnabled(True)
# 退出程序
def ExitApp(self):
self.Timer.Stop()
self.camera.release()
self.MsgTE.setPlainText('Exiting the application..')
QCoreApplication.quit()
if __name__ == '__main__':
app = QApplication(sys.argv)
ui = CamShow()
ui.show()
sys.exit(app.exec_())