深度学习中的卷积神经网络(CNN)在人脸识别领域有显著应用。该过程包括人脸图像采集、处理、训练与识别。
1 人脸图像采集
在数据采集的过程中,利用OpenCV从视频流(如摄像头)中捕捉人脸图像,并将这些图像保存到指定的文件夹中。首先通过OpenCV库连接到默认或指定的摄像头。接着加载Haar特征分类器用于人脸识别。在视频流处理过程中,每一帧都被转换为灰度图以加速处理,并应用分类器来检测人脸。
cv2.namedWindow(window_name)
# 视频来源,可以来自一段已存好的视频,也可以直接来自USB摄像头
cap = cv2.VideoCapture(camera_idx,cv2.CAP_DSHOW)
# 告诉OpenCV使用人脸识别分类器
classfier = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
# 识别出人脸后要画的边框的颜色,RGB格式
color = (0, 255, 0)
num = 0
while cap.isOpened():
ok, frame = cap.read() # 读取一帧数据
print(type(frame))
if not ok:
break
grey = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 将当前桢图像转换成灰度图像
每当检测到人脸时,就会在人脸周围绘制一个矩形框,并将包含该人脸的矩形区域裁剪下来,保存为JPEG图像文件到指定的文件夹中。同时,在视频窗口中实时显示捕捉到的帧和已检测到的人脸数量。
# 人脸检测,1.2和2分别为图片缩放比例和需要检测的有效点数
faceRects = classfier.detectMultiScale(grey, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))
if len(faceRects) > 0: # 大于0则检测到人脸
for faceRect in faceRects: # 单独框出每一张人脸
x, y, w, h = faceRect
# 将当前帧保存为图片
img_name = '%s/%d.jpg ' % (path_name, num)
image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
cv2.imwrite(img_name, image)
num += 1
if num > (catch_pic_num): # 如果超过指定最大保存数量退出循环
break
# 画出矩形框
cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), color, 2)
# 显示当前捕捉到了多少人脸图片
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(frame, 'num:%d' % (num), (x + 30, y + 30), font, 1, (255, 0, 255), 4)
# 超过指定最大保存数量结束程序
if num > (catch_pic_num): break
# 显示图像
cv2.imshow(window_name, frame)
c = cv2.waitKey(10)
if c & 0xFF == ord('q'):
break
# 释放摄像头并销毁所有窗口
程序会持续运行,直到达到指定的最大保存图片数量或用户按下'q'键退出。最后,释放摄像头资源并关闭所有OpenCV创建的窗口。
2 图像处理
处理和加载图像数据集,以便进行机器学习或深度学习训练。
首先定义了存储训练集、验证集和测试集图像及其标签的变量,并允许指定数据集加载的路径。
class Dataset:
def __init__(self, path_name):
# 训练集
self.train_images = None
self.train_labels = None
# 验证集
self.valid_images = None
self.valid_labels = None
# 测试集
self.test_images = None
self.test_labels = None
# 数据集加载路径
self.path_name = path_name
# 当前库采用的维度顺序
self.input_shape = None
self.nb_classes = None
在load
方法中,该类加载了数据集,使用load_dataset
函数(该函数未在代码段中定义,可能是自定义或来自其他库)来读取图像和对应的标签,以及人脸类别的总数。然后,它使用train_test_split
函数(来自sklearn.model_selection
或类似库)将数据划分为训练集和验证集,并进一步从剩余数据中划分出测试集。
# 加载数据集并按照交叉验证的原则划分数据集并进行相关预处理工作
def load(self, img_rows=IMAGE_SIZE, img_cols=IMAGE_SIZE,
img_channels=3):
# 加载数据集到内存
images, labels, face_num = load_dataset(self.path_name)
self.nb_classes = face_num
train_images, valid_images, train_labels, valid_labels = train_test_split(images, labels, test_size=0.3,
random_state=random.randint(0, 100))
_, test_images, _, test_labels = train_test_split(images, labels, test_size=0.5,
random_state=random.randint(0, 100))
if K.image_data_format() == 'channels_first':
self.train_images = train_images.reshape(train_images.shape[0], img_channels, img_rows, img_cols)
valid_images = valid_images.reshape(valid_images.shape[0], img_channels, img_rows, img_cols)
test_images = test_images.reshape(test_images.shape[0], img_channels, img_rows, img_cols)
self.input_shape = (img_channels, img_rows, img_cols)
else:
train_images = train_images.reshape(train_images.shape[0], img_rows, img_cols, img_channels)
valid_images = valid_images.reshape(valid_images.shape[0], img_rows, img_cols, img_channels)
test_images = test_images.reshape(test_images.shape[0], img_rows, img_cols, img_channels)
self.input_shape = (img_rows, img_cols, img_channels)
根据Keras库的图像数据格式要求,代码调整了图像数据的维度顺序。接下来,它打印了每个数据集的样本数,并将标签转换为one-hot编码格式,这是多分类问题中常用的标签表示方式。
train_labels = np_utils.to_categorical(train_labels, self.nb_classes)
valid_labels = np_utils.to_categorical(valid_labels, self.nb_classes)
test_labels = np_utils.to_categorical(test_labels, self.nb_classes)
# 像素数据浮点化以便归一化
train_images = train_images.astype('float32')
valid_images = valid_images.astype('float32')
test_images = test_images.astype('float32')
# 将其归一化,图像的各像素值归一化到0~1区间
train_images /= 255
valid_images /= 255
test_images /= 255
self.train_images = train_images
self.valid_images = valid_images
self.test_images = test_images
self.train_labels = train_labels
self.valid_labels = valid_labels
self.test_labels = test_labels
此外,为了优化训练过程,图像数据被转换为浮点数类型并归一化到0到1的范围内。这种预处理有助于加快模型的收敛速度,并可能提高模型的性能。最后,处理后的图像数据和标签被分别存储到类的相应属性中,供后续训练或评估模型时使用。
3 模型训练
在模型训练过程中,使用了一个典型的卷积神经网络(CNN)训练,用于人脸图像分类任务。
3.1 模型构建
- 初始化模型:使用
Sequential()
创建一个序贯模型,这是最简单的模型类型,由多个网络层线性堆叠。 - 添加卷积层:通过
Convolution2D
(注意:在较新版本的Keras中,这已被Conv2D
替代)添加多个卷积层,用于提取图像中的特征。每个卷积层后都跟着一个ReLU激活层,以增加非线性。 - 池化层:使用
MaxPooling2D
添加池化层,用于降低特征图的空间尺寸,减少计算量,同时保留重要特征。 - Dropout层:添加Dropout层以减少过拟合,通过随机丢弃一部分神经元的输出来实现。
- Flatten层:将多维的输入一维化,以便可以连接到全连接层。
- 全连接层:使用
Dense
添加全连接层,将学到的特征表示映射到样本标记空间。 - 输出层:使用softmax激活函数的全连接层作为输出层,输出每个类别的预测概率。
def build_model(self, dataset, nb_classes=4):
# 构建一个空的网络模型,它是一个线性堆叠模型,各神经网络层会被顺序添加,专业名称为序贯模型或线性堆叠模型
self.model = Sequential()
# 以下代码将顺序添加CNN网络需要的各层,一个add就是一个网络层
self.model.add(Convolution2D(32, 3, 3, padding='same',
input_shape=dataset.input_shape)) # 1 2维卷积层 卷积核 个数,大小,步长
self.model.add(Activation('relu')) # 2 激活函数层
self.model.add(Convolution2D(32, 3, 3,padding='same')) # 3 2维卷积层
self.model.add(Activation('relu')) # 4 激活函数层
self.model.add(MaxPooling2D(pool_size=(2, 2),padding='same')) # 5 池化层
self.model.add(Dropout(0.25)) # 6 Dropout层
self.model.add(Convolution2D(64, 3, 3,padding='same')) # 7 2维卷积层
self.model.add(Activation('relu')) # 8 激活函数层
self.model.add(Convolution2D(64, 3, 3,padding='same')) # 9 2维卷积层
self.model.add(Activation('relu')) # 10 激活函数层
self.model.add(MaxPooling2D(pool_size=(2, 2), padding='same')) # 11 池化层
self.model.add(Dropout(0.25)) # 12 Dropout层
self.model.add(Flatten()) # 13 Flatten层
self.model.add(Dense(512)) # 14 Dense层,又被称作全连接层
self.model.add(Activation('relu')) # 15 激活函数层
self.model.add(Dropout(0.5)) # 16 Dropout层
self.model.add(Dense(nb_classes)) # 17 Dense层
self.model.add(Activation('softmax')) # 18 分类层,输出最终结果
# 输出模型概况
self.model.summary()
3.2 模型训练
- 选择优化器:这里使用了SGD(随机梯度下降)优化器,并设置了学习率、衰减率、动量等参数。
- 配置损失函数和评估指标:使用
categorical_crossentropy
作为损失函数,适用于多分类问题,并使用accuracy
(准确率)作为评估指标。 - 数据准备:根据是否使用数据增强,准备训练数据。数据增强通过
ImageDataGenerator
实现,可以随机旋转、平移、翻转图像等,以增加训练数据的多样性。 - 训练模型:使用
fit
或fit_generator
方法训练模型。如果不使用数据增强,则直接使用fit
方法;如果使用数据增强,则使用fit_generator
方法,该方法接受一个生成器,该生成器在训练过程中实时生成增强后的数据。 - 验证:在训练过程中,使用验证集(validation set)来评估模型的性能,以便在训练过程中进行监控和调整。
# 训练模型
def train(self, dataset, batch_size=4, nb_epoch=10, data_augmentation=True):
sgd = SGD(lr=0.0007, decay=1e-6,
momentum=0.9, nesterov=True) # 采用SGD+momentum的优化器进行训练,首先生成一个优化器对象
self.model.compile(loss='categorical_crossentropy',
optimizer=sgd,
metrics=['accuracy']) # 完成实际的模型配置工作
# 不使用数据提升,所谓的提升就是从我们提供的训练数据中利用旋转、翻转、加噪声等方法创造新的
# 训练数据,有意识的提升训练数据规模,增加模型训练量
if not data_augmentation:
self.model.fit(dataset.train_images,
dataset.train_labels,
batch_size=batch_size,
nb_epoch=nb_epoch,
validation_data=(dataset.valid_images, dataset.valid_labels),
shuffle=True)
# 使用实时数据提升
else:
# 定义数据生成器用于数据提升,其返回一个生成器对象datagen,datagen每被调用一
# 次其生成一组数据(顺序生成),节省内存,其实就是python的数据生成器
datagen = ImageDataGenerator(
featurewise_center=False, # 是否使输入数据去中心化(均值为0),
samplewise_center=False, # 是否使输入数据的每个样本均值为0
featurewise_std_normalization=False, # 是否数据标准化(输入数据除以数据集的标准差)
samplewise_std_normalization=False, # 是否将每个样本数据除以自身的标准差
zca_whitening=False, # 是否对输入数据施以ZCA白化
rotation_range=20, # 数据提升时图片随机转动的角度(范围为0~180)
width_shift_range=0.2, # 数据提升时图片水平偏移的幅度(单位为图片宽度的占比,0~1之间的浮点数)
height_shift_range=0.2, # 同上,只不过这里是垂直
horizontal_flip=True, # 是否进行随机水平翻转
vertical_flip=False) # 是否进行随机垂直翻转
# 计算整个训练样本集的数量以用于特征值归一化、ZCA白化等处理
datagen.fit(dataset.train_images)
# 利用生成器开始训练模型
self.model.fit_generator(datagen.flow(dataset.train_images, dataset.train_labels,
batch_size=batch_size),
steps_per_epoch=(dataset.train_images.shape[0])/batch_size,
epochs=nb_epoch,
validation_data=(dataset.valid_images, dataset.valid_labels))
3.3 模型保存
def save_model(self, file_path=MODEL_PATH):
self.model.save('./model/fei_model.h5')
def load_model(self, file_path):
self.model = load_model(file_path)
3.4 模型评估
模型评估过程通过混淆矩阵直观展示多分类模型性能。首先,使用训练好的模型对验证集进行预测,将预测结果和真实标签转换为类别形式。接着,计算混淆矩阵,该矩阵展示了真实类别与预测类别之间的关系。然后,利用plot_confusion_matrix
函数绘制混淆矩阵图,图中颜色深浅表示各类别间的分类情况,同时计算并显示模型的准确率和误分类率。通过混淆矩阵,可以快速识别模型在哪些类别上表现良好,哪些需要改进。这一过程为评估和优化模型性能提供了重要依据,是机器学习项目中不可或缺的一环。
def plot_confusion_matrix(cm,
target_names,
title='Confusion matrix',
cmap=plt.cm.Greens, # 这个地方设置混淆矩阵的颜色主题,这个主题看着就干净~
normalize=True):
accuracy = np.trace(cm) / float(np.sum(cm))
misclass = 1 - accuracy
if cmap is None:
cmap = plt.get_cmap('Blues')
plt.figure(figsize=(10, 6))
plt.imshow(cm, interpolation='nearest', cmap=cmap)
plt.title(title)
plt.colorbar()
if target_names is not None:
tick_marks = np.arange(len(target_names))
plt.xticks(tick_marks, target_names, rotation=45)
plt.yticks(tick_marks, target_names)
if normalize:
cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
thresh = cm.max() / 1.5 if normalize else cm.max() / 2
for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
if normalize:
plt.text(j, i, "{:0.4f}".format(cm[i, j]),
horizontalalignment="center",
color="white" if cm[i, j] > thresh else "black")
else:
plt.text(j, i, "{:,}".format(cm[i, j]),
horizontalalignment="center",
color="white" if cm[i, j] > thresh else "black")
plt.tight_layout()
plt.ylabel('True label')
plt.xlabel('Predicted label\naccuracy={:0.4f}; misclass={:0.4f}'.format(accuracy, misclass))
# 这里这个savefig是保存图片,如果想把图存在什么地方就改一下下面的路径,然后dpi设一下分辨率即可。
# plt.savefig('/content/drive/My Drive/Colab Notebooks/confusionmatrix32.png',dpi=350)
plt.show()
# 显示混淆矩阵
def plot_confuse(model, x_val, y_val):
predictions = model.predict(x_val, batch_size=4)
y_pred = (predictions == np.max(predictions, axis=1, keepdims=True)).view('i1')
y_pred = y_pred.argmax(axis=1)
y_val = y_val.argmax(axis=1)
conf_mat = confusion_matrix(y_true=y_val, y_pred=y_pred)
plt.figure()
plot_confusion_matrix(conf_mat, normalize=False, target_names=['黎明','刘德华','张学友','郭富城'], title='Confusion Matrix')
4 人脸识别
Face_recognition
类集成了人脸检测与识别功能,利用OpenCV进行人脸检测,并通过一个预训练的深度学习模型(封装在Model
类中)进行人脸识别。初始化时,该类加载人脸对比表、模型权重、设置人脸框颜色,并启动摄像头。在人脸识别流程中,程序不断从摄像头捕获视频帧,并转换为灰度图以加速处理。随后,使用Haar特征分类器检测帧中的人脸,得到人脸区域的坐标。对于每个检测到的人脸,程序从原图中裁剪出人脸区域,输入到深度学习模型中进行识别,获取识别结果(如人名编号)。通过查询对比表,将编号转换为实际人名,并在原视频帧上绘制人脸框和人名标签。整个识别过程在实时视频流中持续进行,直到用户按下'q'键结束。程序还负责在退出时释放摄像头资源,并关闭所有OpenCV窗口,确保资源得到妥善管理。
class Face_recognition():
def __init__(self):
with open('contrast_table', 'r') as f:
self.contrast_table = json.loads(f.read())
self.model = Model()
self.model.load_model(file_path='./model/fei_model.h5')
# 框住人脸的矩形边框颜色
self.color = (0, 255, 0)
# 捕获指定摄像头的实时视频流
self.cap = cv2.VideoCapture(0)
# 人脸识别分类器本地存储路径
self.cascade_path = "haarcascade_frontalface_default.xml"
def recongition(self):
while True:
ret, frame = self.cap.read() # 读取一帧视频
if ret is True:
# 图像灰化,降低计算复杂度
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
else:
continue
# 使用人脸识别分类器,读入分类器
cascade = cv2.CascadeClassifier(self.cascade_path)
# 利用分类器识别出哪个区域为人脸
faceRects = cascade.detectMultiScale(frame_gray, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))
if len(faceRects) > 0:
for faceRect in faceRects:
x, y, w, h = faceRect
# 截取脸部图像提交给模型识别这是谁
image = frame[y - 10: y + h + 10, x - 10: x + w + 10]
probability, name_number = self.model.face_predict(image)
print(name_number)
name = self.contrast_table[str(name_number)]
# print('name_number:', name_number)
cv2.rectangle(frame, (x - 10, y - 10), (x + w + 10, y + h + 10), self.color, thickness=2)
# 文字提示是谁
cv2.putText(frame, name, (x + 30, y + 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 255), 2)
cv2.imshow("face_recognition", frame)
# 等待10毫秒看是否有按键输入
k = cv2.waitKey(10)
# 如果输入q则退出循环
if k & 0xFF == ord('q'):
break
# 释放摄像头并销毁所有窗口
self.cap.release()
cv2.destroyAllWindows()
识别效果: