赞
踩
在这篇博客中,我将分享如何结合深度学习和OpenCV来创建一个能够实时识别人脸情感的系统。
本文主要为我个人的学习心得与总结,鉴于网络上知识信息的零散性,我特意进行了整合与阐述。若在整理过程中存在疏漏或错误之处,恳请各位不吝赐教,给予指正。需要强调的是,本文纯粹为学习交流之用,并不产生任何经济收益。若在文中出现任何涉及禁止转载或侵权的图片、文字内容,敬请及时与我取得联系,我将立即进行修正。
目录
情感检测在计算机科学领域中扮演着重要的角色,尤其在人机交互和情感智能应用中。本项目旨在利用深度学习技术,特别是卷积神经网络(CNN),来实现实时的情感检测。此外,OpenCV作为一个开源的计算机视觉库,为我们提供了处理图像和视频流的强大工具。
我们需要导入用到的Python库,包括数据处理库Pandas、科学计算库NumPy、深度学习框架Keras、以及计算机视觉库OpenCV
- import pandas as pd
- import numpy as np
- from keras.models import Sequential
- from keras.layers import Conv2D,MaxPooling2D,Dense,Dropout,Flatten
- from keras.losses import categorical_crossentropy
- from keras.optimizers import Adam
- from keras.utils import to_categorical
在能够进行表情识别之前,我们需要加载并预处理数据。这一过程包括读取图像数据,将数据分割为训练和测试集,对数据进行标准化处理,以及将标签转换为one-hot编码格式。以下是数据预处理的代码段:
- def load_and_preprocess_data(file_path):
- Catch_bat = pd.read_csv(file_path)
-
- X_train, Y_train, X_test, Y_test = [], [], [], []
-
- for index, row in Catch_bat.iterrows():
- val = row['pixels'].split(' ')
- try:
- if 'Training' in row['Usage']:
- X_train.append(np.array(val, 'float32'))
- Y_train.append(row['emotion'])
- elif 'PublicTest' in row['Usage']:
- X_test.append(np.array(val, 'float32'))
- Y_test.append(row['emotion'])
- except:
- pass
-
- X_train = np.array(X_train, 'float32')
- Y_train = np.array(Y_train, 'float32')
- X_test = np.array(X_test, 'float32')
- Y_test = np.array(Y_test, 'float32')
-
- X_train = (X_train - np.mean(X_train, axis=0)) / np.std(X_train, axis=0)
- X_test = (X_test - np.mean(X_test, axis=0)) / np.std(X_test, axis=0)
-
- labels = 7
- Y_train = to_categorical(Y_train, num_classes=labels)
- Y_test = to_categorical(Y_test, num_classes=labels)
-
- width, height = 48, 48
- X_train = X_train.reshape(X_train.shape[0], width, height, 1)
- X_test = X_test.reshape(X_test.shape[0], width, height, 1)
-
- return X_train, Y_train, X_test, Y_test
这段代码首先使用pandas
读取CSV文件,然后根据“Usage”列将数据分割为训练集和测试集。接着,它将图像数据标准化,并将标签转换为one-hot编码格式。
此函数的目的是构建一个卷积神经网络模型,用于面部表情的分类。以下是数据预处理的代码段:
- def build_model(input_shape, num_classes):
- model = Sequential()
- model.add(Conv2D(64, kernel_size=(3, 3), activation='relu', input_shape=input_shape))
- model.add(Conv2D(64, kernel_size=(3, 3), activation='relu'))
- model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
- model.add(Dropout(0.5))
- model.add(Conv2D(64, kernel_size=(3, 3), activation='relu'))
- model.add(Conv2D(64, kernel_size=(3, 3), activation='relu'))
- model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
- model.add(Dropout(0.5))
- model.add(Conv2D(128, kernel_size=(3, 3), activation='relu'))
- model.add(Conv2D(128, kernel_size=(3, 3), activation='relu'))
- model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
- model.add(Flatten())
- model.add(Dense(512, activation='relu'))
- model.add(Dropout(0.2))
- model.add(Dense(num_classes, activation='softmax'))
- return model
- def train_model(model, X_train, Y_train, X_test, Y_test, batch_size=64, epochs=1):
- model.compile(loss=categorical_crossentropy, optimizer=Adam(), metrics=['accuracy'])
- model.fit(X_train, Y_train, batch_size=batch_size, epochs=epochs, verbose=1, validation_data=(X_test, Y_test), shuffle=True)
这个函数用于训练上述定义的卷积神经网络模型。主要步骤包括:
- def save_model(model, model_file, weights_file):
- model_json = model.to_json()
- with open(model_file, 'w') as json_file:
- json_file.write(model_json)
- model.save_weights(weights_file)
最后,这个函数用于保存训练好的模型,以便将来可以重新加载并使用,而无需重新训练。
在我们的项目中,我们使用了fer2013.csv
数据集,它包含数千个标记了七种基本情感(愤怒、厌恶、恐惧、快乐、悲伤、惊讶和中性)的面部表情图像
- # 加载和预处理数据
- X_train, Y_train, X_test, Y_test = load_and_preprocess_data('fer2013.csv')
-
- # 构建模型
- input_shape = X_train.shape[1:]
- num_classes = 7
- model = build_model(input_shape, num_classes)
-
- # 训练模型
- train_model(model, X_train, Y_train, X_test, Y_test)
-
- # 保存模型
- save_model(model, 'file_name.json', 'file_name.h5')
- import cv2
- import numpy as np
- from keras.models import model_from_json
-
- with open('file_name.json', 'r') as json_file:
- loaded_model_json = json_file.read()
- model = model_from_json(loaded_model_json)
- model.load_weights('file_name.h5')
-
- face_prototxt = 'deploy.prototxt.txt'
- face_model = 'res10_300x300_ssd_iter_140000.caffemodel'
- face_net = cv2.dnn.readNetFromCaffe(face_prototxt, face_model)
-
- emotions = ('angry', 'disgust', 'fear', 'happy', 'sad', 'surprise', 'neutral')
- emotion_colors = {
- 'angry': (0, 0, 255),
- 'disgust': (0, 255, 0),
- 'fear': (255, 0, 0),
- 'happy': (255, 255, 0),
- 'sad': (255, 0, 255),
- 'surprise': (0, 255, 255),
- 'neutral': (255, 255, 255)
- }
-
- font = cv2.FONT_HERSHEY_SIMPLEX
-
- def process_image(img):
- gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
- blob = cv2.dnn.blobFromImage(cv2.resize(img, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0))
- face_net.setInput(blob)
- faces = face_net.forward()
- for i in range(faces.shape[2]):
- confidence = faces[0, 0, i, 2]
- if confidence > 0.5:
- box = faces[0, 0, i, 3:7] * np.array([img.shape[1], img.shape[0], img.shape[1], img.shape[0]])
- (startX, startY, endX, endY) = box.astype("int")
- face = gray[startY:endY, startX:endX]
- face = cv2.resize(face, (48, 48))
- pixels = np.expand_dims(face, axis=0)
- pixels = pixels / 255.0
- pred = model.predict(pixels)
- max_index = np.argmax(pred[0])
- pred_emotion = emotions[max_index]
- color = emotion_colors.get(pred_emotion, (255, 255, 255))
- cv2.rectangle(img, (startX, startY), (endX, endY), color, 3)
- cv2.putText(img, pred_emotion, (int(startX), int(startY)), font, 1, color, 2)
- return img
-
- def process_image_file(file_path):
- img = cv2.imread(file_path)
- processed_img = process_image(img)
- cv2.namedWindow('image', 0)
- cv2.imshow('image', processed_img)
- cv2.waitKey(0)
- cv2.destroyAllWindows()
-
- process_image_file('you_image.jpg')
在这段代码中,我们加载了面部表情识别模型和人脸检测模型,然后定义了一个处理图像的函数。该函数首先将图像转换为灰度图,并使用人脸检测模型检测人脸。然后,对每个检测到的人脸进行表情识别,并根据预测结果在图像上绘制矩形和文本。最后,我们调用了处理单张图像文件的函数,并传入了一个示例图像文件的路径。
除了能够处理静态图像,我们还可以让我们的系统实时处理视频流,并对其中的人脸进行情绪识别。下面是添加处理视频功能的代码:
- def process_video(video_path, output_path):
- cap = cv2.VideoCapture(video_path)
- fps = int(cap.get(cv2.CAP_PROP_FPS))
- frame_size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
-
- fourcc = cv2.VideoWriter_fourcc(*'XVID')
- out = cv2.VideoWriter(output_path, fourcc, fps, frame_size)
-
- while True:
- ret, frame = cap.read()
- if not ret:
- break
- processed_frame = process_image(frame)
- resize_frame = cv2.resize(processed_frame, frame_size)
- cv2.imshow('video', resize_frame)
-
- out.write(resize_frame)
-
- key = cv2.waitKey(1)
- if key == ord('q') or cv2.getWindowProperty('video', cv2.WND_PROP_VISIBLE) < 1:
- break
-
- cap.release()
- out.release()
- cv2.destroyAllWindows()
在上述代码中,我们添加了一个名为 process_video的函数,它接受输入视频文件的路径和输出视频文件的路径。这个函数会打开输入视频文件,逐帧读取视频流,并对每一帧进行情绪识别处理。然后,将处理后的帧写入输出视频文件。最后,释放资源并关闭所有窗口。
为了让我们的情绪识别系统更加实用和互动,我们进一步扩展了其功能,使之能够实时处理来自摄像头的视频流。以下是实现此功能的详细代码:
- def process_camera():
- cap = cv2.VideoCapture(0)
- while True:
- ret, frame = cap.read()
- if not ret:
- break
- processed_frame = process_image(frame)
- resize_frame = cv2.resize(processed_frame, (1000, 700))
- cv2.imshow('frame', resize_frame)
- key = cv2.waitKey(1)
- if key == ord('q') or cv2.getWindowProperty('frame', cv2.WND_PROP_VISIBLE) < 1:
- break
- cap.release()
- cv2.destroyAllWindows()
在这段代码中,我们首先通过 cv2.VideoCapture(0) 打开系统默认的摄像头。接下来,程序进入一个循环,在这个循环中持续从摄像头读取帧。对于每一帧,我们调用 process_image 函数进行所需的图像处理。为了更好的显示效果,我们还将处理后的帧大小调整为 1000x700 像素。如果用户按下 'q' 键或关闭显示窗口,循环将终止,释放摄像头资源并关闭所有开启的窗口。
为了方便大家更好地理解和使用我在本文中提到的技术和方法,我已经将相关代码上传至我的GitHub仓库。
您可以通过以下链接访问我GitHub仓库,并下载或查看这些代码:
GitHub - maxuan777/Face-Emotion-Recognition-: 基于opencv与深度学习的人脸情绪识别
在仓库中,您可以找到与本文内容相对应的代码文件和项目结构。这些代码已经经过我的测试与验证,应该可以正常运行。当然,如果您在使用过程中遇到任何问题或建议,欢迎在下方留言,我会尽快回复并帮助您解决。
感谢您的阅读与支持!如果您对我的博客或代码有任何意见或建议,欢迎在下方留言或私信我。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。