已开启
new: 新建文件 1班18.py #103
new: 新建文件 1班18.py #103
已开启
totm创建于 2025年12月29日
共 1 个文件变更+421-0
@@ -0,0 +1,421 @@
1+import os
2+import numpy as np
3+import matplotlib.pyplot as plt
4+import tensorflow as tf
5+from tensorflow.keras import layers, models, optimizers, regularizers
6+from tensorflow.keras.preprocessing.image import ImageDataGenerator
7+from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau
8+from sklearn.model_selection import train_test_split
9+from sklearn.preprocessing import LabelEncoder
10+from sklearn.neighbors import KNeighborsClassifier
11+from sklearn.svm import SVC
12+from sklearn.metrics import classification_report, confusion_matrix
13+import seaborn as sns
14+import cv2
15+import mtcnn
16+from tqdm import tqdm
17+ 
18+# 设置随机种子保证可重复性
19+np.random.seed(42)
20+tf.random.set_seed(42)
21+ 
22+# 1. 数据准备和预处理
23+def load_data(data_dir, target_size=(160, 160)):
24+ """
25+ 加载人脸数据集并进行预处理
26+ """
27+ images = []
28+ labels = []
29+ label_dict = {}
30+ current_label = 0
31+
32+ # 使用MTCNN进行人脸检测和对齐
33+ detector = mtcnn.MTCNN()
34+
35+ for person_name in tqdm(os.listdir(data_dir), desc="Loading data"):
36+ person_dir = os.path.join(data_dir, person_name)
37+ if not os.path.isdir(person_dir):
38+ continue
39+
40+ label_dict[current_label] = person_name
41+
42+ for image_name in os.listdir(person_dir):
43+ image_path = os.path.join(person_dir, image_name)
44+ try:
45+ # 读取图像
46+ img = cv2.imread(image_path)
47+ img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
48+
49+ # 使用MTCNN检测人脸
50+ results = detector.detect_faces(img)
51+ if len(results) == 0:
52+ continue
53+
54+ # 获取第一个检测到的人脸
55+ x1, y1, width, height = results[0]['box']
56+ x2, y2 = x1 + width, y1 + height
57+
58+ # 提取人脸区域
59+ face = img[y1:y2, x1:x2]
60+
61+ # 调整大小和预处理
62+ face = cv2.resize(face, target_size)
63+ face = face.astype('float32') / 255.0
64+ face = np.expand_dims(face, axis=0)
65+
66+ # 数据增强
67+ datagen = ImageDataGenerator(
68+ rotation_range=10,
69+ width_shift_range=0.1,
70+ height_shift_range=0.1,
71+ shear_range=0.1,
72+ zoom_range=0.1,
73+ horizontal_flip=True,
74+ fill_mode='nearest'
75+ )
76+
77+ # 应用数据增强并收集样本
78+ aug_iter = datagen.flow(face, batch_size=1)
79+ for _ in range(5): # 每个原始图像生成5个增强样本
80+ aug_img = next(aug_iter)[0].numpy()
81+ images.append(aug_img)
82+ labels.append(current_label)
83+
84+ except Exception as e:
85+ print(f"Error processing {image_path}: {e}")
86+ continue
87+
88+ current_label += 1
89+
90+ images = np.array(images)
91+ labels = np.array(labels)
92+
93+ return images, labels, label_dict
94+ 
95+# 2. 构建FaceNet风格的模型
96+def build_facenet_model(input_shape=(160, 160, 3), embedding_size=128):
97+ """
98+ 构建一个简化版的FaceNet模型
99+ """
100+ def inception_block(x, filters):
101+ """Inception模块"""
102+ path1 = layers.Conv2D(filters[0], 1, activation='relu')(x)
103+
104+ path2 = layers.Conv2D(filters[1], 1, activation='relu')(x)
105+ path2 = layers.Conv2D(filters[1], 3, padding='same', activation='relu')(path2)
106+
107+ path3 = layers.Conv2D(filters[2], 1, activation='relu')(x)
108+ path3 = layers.Conv2D(filters[2], 5, padding='same', activation='relu')(path3)
109+
110+ path4 = layers.MaxPooling2D(3, 1, padding='same')(x)
111+ path4 = layers.Conv2D(filters[3], 1, activation='relu')(path4)
112+
113+ return layers.Concatenate()([path1, path2, path3, path4])
114+
115+ # 输入层
116+ inputs = layers.Input(shape=input_shape)
117+ x = layers.Lambda(lambda x: tf.image.per_image_standardization(x))(inputs)
118+
119+ # 初始卷积层
120+ x = layers.Conv2D(64, 7, strides=2, padding='same', activation='relu')(x)
121+ x = layers.MaxPooling2D(3, strides=2, padding='same')(x)
122+
123+ # Inception模块堆叠
124+ x = inception_block(x, [32, 32, 32, 32])
125+ x = inception_block(x, [32, 32, 32, 32])
126+ x = layers.MaxPooling2D(3, strides=2, padding='same')(x)
127+
128+ x = inception_block(x, [64, 64, 64, 64])
129+ x = inception_block(x, [64, 64, 64, 64])
130+ x = layers.MaxPooling2D(3, strides=2, padding='same')(x)
131+
132+ x = inception_block(x, [128, 128, 128, 128])
133+ x = inception_block(x, [128, 128, 128, 128])
134+ x = inception_block(x, [128, 128, 128, 128])
135+ x = inception_block(x, [128, 128, 128, 128])
136+ x = layers.MaxPooling2D(3, strides=2, padding='same')(x)
137+
138+ # 展平和全连接层
139+ x = layers.Flatten()(x)
140+ x = layers.Dense(256, activation='relu')(x)
141+ x = layers.Dropout(0.5)(x)
142+
143+ # L2正则化嵌入层
144+ embeddings = layers.Dense(embedding_size, activation=None,
145+ kernel_regularizer=regularizers.l2(0.01))(x)
146+ embeddings = tf.math.l2_normalize(embeddings, axis=1)
147+
148+ # 构建模型
149+ model = models.Model(inputs, embeddings, name='FaceNet')
150+
151+ return model
152+ 
153+# 3. 三元组损失函数
154+def triplet_loss(y_true, y_pred, alpha=0.2):
155+ """
156+ 三元组损失函数实现
157+ """
158+ anchor, positive, negative = y_pred[:, :128], y_pred[:, 128:256], y_pred[:, 256:]
159+
160+ # 计算距离
161+ pos_dist = tf.reduce_sum(tf.square(anchor - positive), axis=-1)
162+ neg_dist = tf.reduce_sum(tf.square(anchor - negative), axis=-1)
163+
164+ # 计算基本损失
165+ basic_loss = pos_dist - neg_dist + alpha
166+ loss = tf.reduce_mean(tf.maximum(basic_loss, 0.0))
167+
168+ return loss
169+ 
170+# 4. 数据生成器
171+class TripletGenerator:
172+ """
173+ 生成三元组(anchor, positive, negative)的数据生成器
174+ """
175+ def __init__(self, images, labels, batch_size=32):
176+ self.images = images
177+ self.labels = labels
178+ self.batch_size = batch_size
179+ self.label_to_indices = {label: np.where(labels == label)[0] for label in np.unique(labels)}
180+
181+ def __len__(self):
182+ return len(self.images) // self.batch_size
183+
184+ def __getitem__(self, index):
185+ batch_indices = np.random.choice(len(self.images), self.batch_size * 3, replace=False)
186+ anchors, positives, negatives = [], [], []
187+
188+ for i in range(self.batch_size):
189+ # 随机选择一个anchor
190+ anchor_idx = batch_indices[i]
191+ anchor_label = self.labels[anchor_idx]
192+
193+ # 选择同类的positive样本
194+ pos_idx = np.random.choice(self.label_to_indices[anchor_label])
195+ while pos_idx == anchor_idx:
196+ pos_idx = np.random.choice(self.label_to_indices[anchor_label])
197+
198+ # 选择不同类的negative样本
199+ neg_label = np.random.choice([l for l in self.label_to_indices if l != anchor_label])
200+ neg_idx = np.random.choice(self.label_to_indices[neg_label])
201+
202+ anchors.append(self.images[anchor_idx])
203+ positives.append(self.images[pos_idx])
204+ negatives.append(self.images[neg_idx])
205+
206+ anchors = np.array(anchors)
207+ positives = np.array(positives)
208+ negatives = np.array(negatives)
209+
210+ # 组合成三元组
211+ triplets = np.concatenate([anchors, positives, negatives], axis=-1)
212+
213+ # 虚拟标签(三元组损失不需要真实标签)
214+ dummy_labels = np.zeros((self.batch_size, 1))
215+
216+ return triplets, dummy_labels
217+ 
218+# 5. 训练模型
219+def train_model(data_dir, model_save_path='facenet_model.h5'):
220+ # 加载和预处理数据
221+ print("Loading and preprocessing data...")
222+ images, labels, label_dict = load_data(data_dir)
223+
224+ # 划分训练集和验证集
225+ X_train, X_val, y_train, y_val = train_test_split(
226+ images, labels, test_size=0.2, random_state=42, stratify=labels
227+ )
228+
229+ # 构建模型
230+ print("Building model...")
231+ model = build_facenet_model()
232+ model.summary()
233+
234+ # 编译模型(使用三元组损失)
235+ model.compile(optimizer=optimizers.Adam(learning_rate=0.001), loss=triplet_loss)
236+
237+ # 回调函数
238+ callbacks = [
239+ ModelCheckpoint(model_save_path, monitor='val_loss', save_best_only=True),
240+ EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True),
241+ ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, min_lr=1e-6)
242+ ]
243+
244+ # 数据生成器
245+ train_generator = TripletGenerator(X_train, y_train, batch_size=32)
246+ val_generator = TripletGenerator(X_val, y_val, batch_size=32)
247+
248+ # 训练模型
249+ print("Training model...")
250+ history = model.fit(
251+ train_generator,
252+ validation_data=val_generator,
253+ epochs=50,
254+ callbacks=callbacks,
255+ verbose=1
256+ )
257+
258+ # 绘制训练曲线
259+ plt.figure(figsize=(12, 5))
260+ plt.subplot(1, 2, 1)
261+ plt.plot(history.history['loss'], label='Train Loss')
262+ plt.plot(history.history['val_loss'], label='Validation Loss')
263+ plt.title('Model Loss')
264+ plt.xlabel('Epoch')
265+ plt.ylabel('Loss')
266+ plt.legend()
267+
268+ plt.subplot(1, 2, 2)
269+ plt.plot(history.history['lr'], label='Learning Rate')
270+ plt.title('Learning Rate Schedule')
271+ plt.xlabel('Epoch')
272+ plt.ylabel('Learning Rate')
273+ plt.legend()
274+
275+ plt.tight_layout()
276+ plt.savefig('training_curves.png')
277+ plt.show()
278+
279+ return model, label_dict
280+ 
281+# 6. 评估模型
282+def evaluate_model(model, images, labels, label_dict):
283+ """
284+ 评估模型性能
285+ """
286+ # 提取嵌入向量
287+ print("Extracting embeddings...")
288+ embeddings = model.predict(images, batch_size=32, verbose=1)
289+
290+ # 编码标签
291+ le = LabelEncoder()
292+ y_true = le.fit_transform(labels)
293+ num_classes = len(le.classes_)
294+
295+ # 训练分类器(SVM和KNN)
296+ print("Training classifiers...")
297+ X_train, X_test, y_train, y_test = train_test_split(
298+ embeddings, y_true, test_size=0.2, random_state=42, stratify=y_true
299+ )
300+
301+ # SVM分类器
302+ svm = SVC(kernel='rbf', probability=True)
303+ svm.fit(X_train, y_train)
304+ y_pred_svm = svm.predict(X_test)
305+
306+ # KNN分类器
307+ knn = KNeighborsClassifier(n_neighbors=5)
308+ knn.fit(X_train, y_train)
309+ y_pred_knn = knn.predict(X_test)
310+
311+ # 评估结果
312+ print("\nSVM Classification Report:")
313+ print(classification_report(y_test, y_pred_svm, target_names=le.classes_))
314+
315+ print("\nKNN Classification Report:")
316+ print(classification_report(y_test, y_pred_knn, target_names=le.classes_))
317+
318+ # 混淆矩阵
319+ plt.figure(figsize=(12, 6))
320+ plt.subplot(1, 2, 1)
321+ cm = confusion_matrix(y_test, y_pred_svm)
322+ sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
323+ xticklabels=le.classes_, yticklabels=le.classes_)
324+ plt.title('SVM Confusion Matrix')
325+
326+ plt.subplot(1, 2, 2)
327+ cm = confusion_matrix(y_test, y_pred_knn)
328+ sns.heatmap(cm, annot=True, fmt='d', cmap='Greens',
329+ xticklabels=le.classes_, yticklabels=le.classes_)
330+ plt.title('KNN Confusion Matrix')
331+
332+ plt.tight_layout()
333+ plt.savefig('confusion_matrices.png')
334+ plt.show()
335+
336+ return svm, knn, le
337+ 
338+# 7. 实时人脸识别
339+def real_time_recognition(model, classifier, le, label_dict, threshold=0.7):
340+ """
341+ 实时人脸识别演示
342+ """
343+ # 加载检测器
344+ detector = mtcnn.MTCNN()
345+
346+ # 打开摄像头
347+ cap = cv2.VideoCapture(0)
348+
349+ while True:
350+ ret, frame = cap.read()
351+ if not ret:
352+ break
353+
354+ # 检测人脸
355+ rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
356+ results = detector.detect_faces(rgb_frame)
357+
358+ for result in results:
359+ x1, y1, width, height = result['box']
360+ x2, y2 = x1 + width, y1 + height
361+
362+ # 提取人脸并预处理
363+ face = rgb_frame[y1:y2, x1:x2]
364+ face = cv2.resize(face, (160, 160))
365+ face = face.astype('float32') / 255.0
366+ face = np.expand_dims(face, axis=0)
367+
368+ # 预测嵌入向量
369+ embedding = model.predict(face)[0]
370+
371+ # 预测类别
372+ if isinstance(classifier, SVC):
373+ probas = classifier.predict_proba([embedding])[0]
374+ max_prob = np.max(probas)
375+ pred_idx = np.argmax(probas)
376+ else: # KNN
377+ distances, indices = classifier.kneighbors([embedding])
378+ max_prob = 1 / (distances[0][0] + 1e-6) # 转换为类似概率的值
379+ pred_idx = indices[0][0]
380+
381+ # 获取预测标签
382+ pred_label = le.inverse_transform([pred_idx])[0]
383+ person_name = label_dict.get(pred_label, "Unknown")
384+
385+ # 绘制边界框和标签
386+ color = (0, 255, 0) if max_prob >= threshold else (0, 0, 255)
387+ label = f"{person_name} ({max_prob:.2f})"
388+
389+ cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
390+ cv2.putText(frame, label, (x1 y1 - 10),
391+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
392+
393+ cv2.imshow('Real-time Face Recognition', frame)
394+
395+ if cv2.waitKey(1) & 0xFF == ord('q'):
396+ break
397+
398+ cap.release()
399+ cv2.destroyAllWindows()
400+ 
401+# 主函数
402+def main():
403+ # 数据目录(应包含按人物命名的子目录,每个子目录包含该人物的多张照片)
404+ data_dir = 'lfw_dataset' # 替换为你的数据集路径
405+
406+ # 1. 训练模型
407+ print("Starting Face Recognition System Training...")
408+ model, label_dict = train_model(data_dir)
409+
410+ # 2. 加载最佳模型并评估
411+ print("\nEvaluating Model...")
412+ images, labels, _ = load_data(data_dir) # 重新加载数据
413+ svm, knn, le = evaluate_model(model, images, labels, label_dict)
414+
415+ # 3. 实时识别演示
416+ print("\nStarting Real-time Recognition...")
417+ print("Press 'q' to quit.")
418+ real_time_recognition(model, svm, le, label_dict)
419+ 
420+if __name__ == '__main__':
421+ main()