├── README.md ├── add.py ├── avi_to_jpg.py ├── data_preprocess.py ├── detect_gui.py ├── train_CNN.py └── train_MLP.py /README.md: -------------------------------------------------------------------------------- 1 | ### 数据集及模型下载 2 | 链接:[https://pan.baidu.com/s/1mu__hCJKm52PnB1PhyauJw?pwd=p9vu](https://pan.baidu.com/s/1mu__hCJKm52PnB1PhyauJw?pwd=p9vu) 3 | 提取码:p9vu 4 | 5 | ### 原数据集enterface database下载 6 | 链接:[https://pan.baidu.com/s/1AXb31ov3kJhg5_Bo4C-ElA?pwd=5kxk](https://pan.baidu.com/s/1AXb31ov3kJhg5_Bo4C-ElA?pwd=5kxk) 7 | 提取码:5kxk 8 | 9 | -------------------------------------------------------------------------------- /add.py: -------------------------------------------------------------------------------- 1 | from tensorflow.keras.models import load_model 2 | from tensorflow.keras.utils import to_categorical 3 | from sklearn.preprocessing import LabelEncoder 4 | from sklearn.model_selection import train_test_split 5 | from sklearn.metrics import classification_report 6 | import pandas as pd 7 | import numpy as np 8 | 9 | # 读取数据 10 | df = pd.read_csv('preprocessed_data.csv') 11 | 12 | # 提取标签并进行编码 13 | labels = df['label'].values 14 | le = LabelEncoder() 15 | labels = le.fit_transform(labels) 16 | labels = to_categorical(labels) 17 | 18 | # 提取并恢复图像形状 19 | images = df.drop(columns='label').values 20 | images = images.reshape(len(images), 48, 48, 1) 21 | 22 | # 划分数据集 23 | X_train, X_test, y_train, y_test = train_test_split(images, labels, test_size=0.2, random_state=42) 24 | 25 | # 读取模型 26 | model = load_model('emotion_model.h5') 27 | 28 | # 预测测试集 29 | y_pred = model.predict(X_test) 30 | 31 | # 转换预测结果为标签形式 32 | y_pred = np.argmax(y_pred, axis=1) 33 | 34 | # 转换测试集标签为标签形状 35 | y_true = np.argmax(y_test, axis=1) 36 | 37 | # 打印各类别的分类报告 38 | print(classification_report(y_true, y_pred, target_names=le.classes_)) 39 | -------------------------------------------------------------------------------- /avi_to_jpg.py: -------------------------------------------------------------------------------- 1 | import os 2 | import cv2 3 | 4 | # 遍历所有subject的文件夹 5 | for subject in range(1, 45): # 44个subject 6 | subject_path = f"E:/project2_database/enterface database/subject {subject}" 7 | print(f"Checking {subject_path}...") 8 | 9 | # 情绪的缩写字典 10 | emotion_dict = {'anger': 'an', 'disgust': 'di', 'fear': 'fe', 'happiness': 'ha', 'sadness': 'sa', 'surprise': 'su'} 11 | 12 | # 遍历每个subject文件夹中的每个情绪 13 | for emotion, emotion_abbr in emotion_dict.items(): 14 | emotion_path = os.path.join(subject_path, emotion) 15 | print(f" Checking {emotion_path}...") 16 | 17 | # 检查情绪文件夹是否存在 18 | if os.path.exists(emotion_path): 19 | # 遍历每个情绪文件夹中的每个sentence 20 | for sentence in range(1, 6): # 5个sentence 21 | sentence_path = os.path.join(emotion_path, f"sentence {sentence}") 22 | print(f" Checking {sentence_path}...") 23 | 24 | # 检查sentence文件夹是否存在 25 | if os.path.exists(sentence_path): 26 | # 视频文件的路径 27 | video_path = os.path.join(sentence_path, f's{subject}_{emotion_abbr}_{sentence}.avi') 28 | print(f" Checking {video_path}...") 29 | 30 | # 检查视频文件是否存在 31 | if os.path.exists(video_path): 32 | # 使用cv2读取视频文件 33 | vidcap = cv2.VideoCapture(video_path) 34 | 35 | if vidcap.isOpened(): 36 | # 初始化帧计数器 37 | count = 0 38 | 39 | # 循环读取视频的每一帧 40 | while True: 41 | # 读取一帧 42 | success, image = vidcap.read() 43 | 44 | # 如果读取成功,保存该帧为图像文件 45 | if success: 46 | # 图像文件的路径 47 | frame_path = os.path.join(sentence_path, f'frame{count}.jpg') 48 | 49 | # 保存图像文件 50 | write_success = cv2.imwrite(frame_path, image) 51 | if write_success: 52 | print(f" Saved frame {count} to {frame_path}") 53 | else: 54 | print(f" Failed to save frame {count} to {frame_path}") 55 | 56 | # 帧计数器加1 57 | count += 1 58 | else: 59 | # 如果读取不成功(例如已经到了视频的末尾),则跳出循环 60 | print(f" Finished reading frames from {video_path}") 61 | break 62 | else: 63 | print(f" Failed to open {video_path}") 64 | else: 65 | print(f" {video_path} does not exist") 66 | else: 67 | print(f" {sentence_path} does not exist") 68 | else: 69 | print(f" {emotion_path} does not exist") 70 | -------------------------------------------------------------------------------- /data_preprocess.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | import pandas as pd 4 | import os 5 | 6 | def load_and_preprocess_image(image_path): 7 | # 加载图片 8 | img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) # 灰度化 9 | img = cv2.resize(img, (48, 48)) # 调整大小 10 | img = img / 255.0 # 归一化 11 | return img 12 | 13 | # 遍历所有图片并进行预处理 14 | images = [] 15 | labels = [] 16 | for subject in range(1, 45): # 44个subject 17 | subject_path = f"E:/project2_database/enterface database/subject {subject}" 18 | for emotion in ['anger', 'disgust', 'fear', 'happiness', 'sadness', 'surprise']: 19 | emotion_path = os.path.join(subject_path, emotion) 20 | if os.path.exists(emotion_path): 21 | for sentence in range(1, 6): # 5个sentence 22 | sentence_path = os.path.join(emotion_path, f"sentence {sentence}") 23 | if os.path.exists(sentence_path): 24 | for frame in os.listdir(sentence_path): 25 | if frame.endswith('.jpg'): 26 | frame_path = os.path.join(sentence_path, frame) 27 | img = load_and_preprocess_image(frame_path) 28 | images.append(img) 29 | labels.append(emotion) 30 | 31 | # 转换为numpy数组 32 | images = np.array(images) 33 | labels = np.array(labels) 34 | 35 | # 将二维的图像数据展平为一维 36 | flattened_images = images.reshape(len(images), -1) 37 | 38 | # 创建一个pandas DataFrame,然后保存到CSV文件 39 | df = pd.DataFrame(flattened_images) 40 | df['label'] = labels 41 | df.to_csv('preprocessed_data.csv', index=False) 42 | -------------------------------------------------------------------------------- /detect_gui.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import filedialog 3 | from tensorflow.keras.models import load_model 4 | import cv2 5 | import numpy as np 6 | from PIL import Image, ImageTk 7 | 8 | # 加载模型 9 | model = load_model('emotion_model.h5') 10 | 11 | def load_image(): 12 | file_path = filedialog.askopenfilename() 13 | if file_path: 14 | image = Image.open(file_path) 15 | image = image.resize((250, 250), Image.LANCZOS) 16 | image = ImageTk.PhotoImage(image) 17 | label_image.config(image=image) 18 | label_image.image = image 19 | label_image.file_path = file_path 20 | 21 | def predict_emotion(): 22 | if hasattr(label_image, 'file_path'): 23 | img = cv2.imread(label_image.file_path, cv2.IMREAD_GRAYSCALE) 24 | img = cv2.resize(img, (48, 48)) 25 | img = img / 255.0 26 | img = img.reshape(1, 48, 48, 1) 27 | 28 | prediction = model.predict(img) 29 | emotion = np.argmax(prediction) 30 | emotion_dict = {0: 'Anger', 1: 'Disgust', 2: 'Fear', 3: 'Happiness', 4: 'Sadness', 5: 'Surprise'} 31 | label_result.config(text=f"情感: {emotion_dict[emotion]}") 32 | 33 | root = tk.Tk() 34 | 35 | # 创建画布 36 | canvas = tk.Canvas(root, width=500, height=500) 37 | canvas.pack() 38 | 39 | # 创建标签用于显示图像和结果 40 | label_image = tk.Label(root) 41 | label_image.place(x=125, y=50) 42 | 43 | label_result = tk.Label(root, font=("Arial", 14)) 44 | label_result.place(x=180, y=320) 45 | 46 | # 创建按钮 47 | button_load = tk.Button(root, text="导入图像", command=load_image, width=20, height=2, font=("Arial", 14)) 48 | button_load.place(x=136, y=350) 49 | 50 | button_predict = tk.Button(root, text="检测情感", command=predict_emotion, width=20, height=2, font=("Arial", 14)) 51 | button_predict.place(x=136, y=420) 52 | 53 | root.mainloop() 54 | -------------------------------------------------------------------------------- /train_CNN.py: -------------------------------------------------------------------------------- 1 | from tensorflow.keras.models import Sequential 2 | from tensorflow.keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D 3 | from tensorflow.keras.optimizers import Adam 4 | from tensorflow.keras.utils import to_categorical 5 | from sklearn.preprocessing import LabelEncoder 6 | from sklearn.model_selection import train_test_split 7 | import pandas as pd 8 | import matplotlib.pyplot as plt 9 | from matplotlib.font_manager import FontProperties 10 | # 使用黑体字体 11 | font = FontProperties(fname=r"C:\Windows\Fonts\simhei.ttf", size=14) 12 | plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置全局字体 13 | plt.rcParams['axes.unicode_minus'] = False # 解决保存图像时负号'-'显示为方块的问题 14 | 15 | # 读取数据 16 | df = pd.read_csv('preprocessed_data.csv') 17 | 18 | # 提取标签并进行编码 19 | labels = df['label'].values 20 | le = LabelEncoder() 21 | labels = le.fit_transform(labels) 22 | labels = to_categorical(labels) 23 | 24 | # 提取并恢复图像形状 25 | images = df.drop(columns='label').values 26 | images = images.reshape(len(images), 48, 48, 1) 27 | 28 | # 划分数据集 29 | X_train, X_test, y_train, y_test = train_test_split(images, labels, test_size=0.2, random_state=42) 30 | 31 | # 创建模型 32 | model = Sequential() 33 | model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(48, 48, 1))) 34 | model.add(MaxPooling2D((2, 2))) 35 | model.add(Conv2D(64, (3, 3), activation='relu')) 36 | model.add(MaxPooling2D((2, 2))) 37 | model.add(Conv2D(64, (3, 3), activation='relu')) 38 | model.add(Flatten()) 39 | model.add(Dense(64, activation='relu')) 40 | model.add(Dropout(0.5)) 41 | model.add(Dense(6, activation='softmax')) 42 | 43 | # 编译模型 44 | model.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy']) 45 | 46 | # 训练模型并捕获history对象 47 | history = model.fit(X_train, y_train, epochs=10, batch_size=64, validation_data=(X_test, y_test)) 48 | 49 | # 保存模型 50 | model.save('emotion_model.h5') 51 | 52 | # 绘制训练过程中的准确率曲线 53 | plt.figure(figsize=(12, 6)) 54 | plt.subplot(1, 2, 1) 55 | plt.plot(history.history['accuracy']) 56 | plt.plot(history.history['val_accuracy']) 57 | plt.title('Model accuracy') 58 | plt.ylabel('Accuracy') 59 | plt.xlabel('Epoch') 60 | plt.legend(['Train', 'Test'], loc='upper left') 61 | 62 | # 绘制训练过程中的损失曲线 63 | plt.subplot(1, 2, 2) 64 | plt.plot(history.history['loss']) 65 | plt.plot(history.history['val_loss']) 66 | plt.title('Model loss') 67 | plt.ylabel('Loss') 68 | plt.xlabel('Epoch') 69 | plt.legend(['Train', 'Test'], loc='upper left') 70 | plt.show() 71 | -------------------------------------------------------------------------------- /train_MLP.py: -------------------------------------------------------------------------------- 1 | from tensorflow.keras.models import Sequential 2 | from tensorflow.keras.layers import Dense, Dropout, Flatten 3 | from tensorflow.keras.optimizers import Adam 4 | from tensorflow.keras.utils import to_categorical 5 | from sklearn.preprocessing import LabelEncoder 6 | from sklearn.model_selection import train_test_split 7 | import pandas as pd 8 | 9 | # 读取数据 10 | df = pd.read_csv('preprocessed_data.csv') 11 | 12 | # 提取标签并进行编码 13 | labels = df['label'].values 14 | le = LabelEncoder() 15 | labels = le.fit_transform(labels) 16 | labels = to_categorical(labels) 17 | 18 | # 提取并恢复图像形状 19 | images = df.drop(columns='label').values 20 | images = images.reshape(len(images), 48*48) # 对于MLP,我们不需要保留二维结构 21 | 22 | # 划分数据集 23 | X_train, X_test, y_train, y_test = train_test_split(images, labels, test_size=0.2, random_state=42) 24 | 25 | # 创建模型 26 | model = Sequential() 27 | model.add(Dense(512, activation='relu', input_shape=(48*48,))) 28 | model.add(Dropout(0.5)) 29 | model.add(Dense(256, activation='relu')) 30 | model.add(Dropout(0.5)) 31 | model.add(Dense(128, activation='relu')) 32 | model.add(Dropout(0.5)) 33 | model.add(Dense(6, activation='softmax')) 34 | 35 | # 编译模型 36 | model.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy']) 37 | 38 | # 训练模型 39 | model.fit(X_train, y_train, epochs=10, batch_size=64, validation_data=(X_test, y_test)) 40 | 41 | # 保存模型 42 | model.save('mlp_emotion_model.h5') 43 | --------------------------------------------------------------------------------