人脸检测实战进阶:使用 OpenCV 进行活体检测
这篇教程详细介绍了如何利用Python和OpenCV进行视频分析,结合预训练的深度学习模型进行活性检测。内容涵盖了以下几个关键部分:
视频流处理和面部检测:
- 解析视频参数并加载视频流。
- 使用OpenCV进行面部检测,提取面部区域并生成输出文件。
- 实现跳帧技术以减少处理时间。
LivenessNet模型:- 介绍LivenessNet模型,这是一个浅层卷积神经网络,用于区分真实和欺骗的面部图像。
- 提供模型构建代码,包括数据预处理、标签编码、数据分割以及模型训练和评估。
活性检测流程:- 使用检测到的面部图像输入到训练好的LivenessNet模型中进行分类。
- 生成分类报告并输出训练图以供分析。
教程结构清晰,从数据预处理到模型训练再到模型应用,涵盖了从头到尾的流程。代码示例详细,适合实际应用中处理不同视频和检测器路径的需求。
help=“# of frames to skip before applying face detection”)
args = vars(ap.parse_args())
导入需要的包。 除了内置的 Python 模块,这个脚本只需要 OpenCV 和 NumPy。
解析命令行参数:
–input :我们输入视频文件的路径。
–output :将存储每个裁剪面的输出目录的路径。
facial detector 的路径为: 我们计划采用 OpenCV 的深度学习 人脸识别技术。 为便利起见,该 Caffe 模型已包含在今天的下载包中。
–confidence : 过滤弱人脸检测的最小概率。 默认情况下,此值为 50%。
由于相邻帧具有高度相似性,因此我们无需对每个图像进行检测和存储。相反地,我们会跳过 N 帧,两次检测之间。您可以通过调整此参数来修改默认值为16。
让我们继续加载人脸检测器并初始化我们的视频流:
load our serialized face detector from disk
print(“[INFO] loading face detector…”)
protoPath = os.path.sep.join([args[“detector”], “deploy.prototxt”])
modelPath = os.path.sep.join([args[“detector”],
“res10_300x300_ssd_iter_140000.caffemodel”])
net = cv2.dnn.readNetFromCaffe(protoPath, modelPath)
open a pointer to the video file stream and initialize the total
number of frames read and saved thus far
images = os.listdir(args[“input”])
read = 0
saved = 0
加载 OpenCV 的深度学习人脸检测器。
收集全部图像。 我们还初始化了两个变量,分别用于记录读取的帧数和循环处理时的帧数。 接下来,我们将开始一个循环来处理每一帧。
for filename in images:
filepath = os.path.join(args[“input”], filename)
img = cv2.imread(filepath)
循环读取图片。
继续检测人脸:
(h, w) = img.shape[:2]
blob = cv2.dnn.blobFromImage(cv2.resize(img, (300, 300)), 1.0,
(300, 300), (104.0, 177.0, 123.0))
pass the blob through the network and obtain the detections and
predictions
net.setInput(blob)
detections = net.forward()
ensure at least one face was found
if len(detections) > 0:
we’re making the assumption that each image has only ONE
face, so find the bounding box with the largest probability
i = np.argmax(detections[0, 0, :, 2])
confidence = detections[0, 0, i, 2]
为了执行人脸检测,我们需要从图像中创建一个 blob。
该 blob 具有 300×300 像素的分辨率,为了适应我们的 Caffe 人脸检测器,随后将需要调整边界框尺寸,获取相应的框架尺寸。
该脚本对深度学习的人脸检测器执行前向传播操作。该脚本假设每帧视频仅包含一张脸,这有助于减少误报的发生。对于处理包含多个面孔的视频,建议对算法进行相应的优化。因此,该脚本选择具有最高置信度的人脸检测结果。使用索引提取检测的置信度。
让我们过滤弱检测并将人脸 ROI 写入磁盘:
ensure that the detection with the largest probability also
means our minimum probability test (thus helping filter out
weak detections)
if confidence > args[“confidence”]:
compute the (x, y)-coordinates of the bounding box for
the face and extract the face ROI
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
(startX, startY, endX, endY) = box.astype(“int”)
face = img[startY:endY, startX:endX]
write the frame to disk
p = os.path.sep.join([args[“output”],
“{}.png”.format(saved)])
cv2.imwrite(p, face)
saved += 1
print(“[INFO] saved {} to disk”.format§)
cv2.destroyAllWindows()
为了减少误报,确保我们的脸...达到最小阈值;在该处,我们获取人脸 ROI 边界框坐标和人脸 ROI 本身;通过该方式,我们创建路径 + 文件名,并将其存储在磁盘上;此时可以增加存储的人脸数量;处理完毕后,我们完成清理。
=========================================================================

现在我们已经实现了 gather_examples.py 脚本,让我们开始工作。
打开一个终端并执行以下命令为我们的“假/欺骗”类提取人脸:
python script.py --input fakes --output dataset/fake --detector face_recog
同样,我们也可以对“真实”类做同样的事情:
python script.py --input_dir real --output_dir dataset/real --face_detector
因为实际视频文件比模拟视频文件长,所以我们将采用更高的帧率以平衡每个类别的人脸 ROI 数量。执行脚本后,您应获得以下图像计数:
Fake: 55images
Real: 84images
Total: 139images
=======================================================================================
下一步是实施“LivenessNet”,这是我们基于深度学习的活体检测器。
在本质上,LivenessNet采用了极其简单的架构设计。这一设计主要归因于两个关键因素:其一,保持这个网络的浅层架构和最少参数数量,可以有效减少在我们的小数据集上过度拟合的机会;其二,通过减少参数数量,可以确保我们的活体检测器能够快速运行,即使在资源受限的设备上,例如Raspberry Pi也能正常工作。
现在让我们实现 LivenessNet——打开 livenessnet.py 并插入以下代码:
import the necessary packages
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.layers import Conv2D
from tensorflow.keras.layers import MaxPooling2D
from tensorflow.keras.layers import Activation
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import Dense
from tensorflow.keras import backend as K
class LivenessNet:
@staticmethod
def build(width, height, depth, classes):
initialize the model along with the input shape to be
“channels last” and the channels dimension itself
model = Sequential()
inputShape = (height, width, depth)
chanDim = -1
if we are using “channels first”, update the input shape
and channels dimension
if K.image_data_format() == “channels_first”:
inputShape = (depth, height, width)
chanDim = 1
导入必要的库包。 为了全面掌握这些层和功能,建议深入学习使用 Python 实现计算机视觉深度学习。
实现LivenessNet类。该类包含一个静态方法build。build方法接受四个参数:
width :图像/体积的宽度。
height :图像有多高。
depth :图像的通道数(在本例中为 3,因为我们将使用 RGB 图像)。
classes:类别的数量。 我们总共有两个类:“real”和“fake”。
为模型进行初始化设置,首先需要指定输入通道的形状。在此过程中,需要特别注意通道的排序方式。接下来,我们将逐步添加各层结构,构建完整的CNN网络框架。
first CONV => RELU => CONV => RELU => POOL layer set
model.add(Conv2D(16, (3, 3), padding=“same”,
input_shape=inputShape))
model.add(Activation(“relu”))
model.add(BatchNormalization(axis=chanDim))
model.add(Conv2D(16, (3, 3), padding=“same”))
model.add(Activation(“relu”))
model.add(BatchNormalization(axis=chanDim))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
second CONV => RELU => CONV => RELU => POOL layer set
model.add(Conv2D(32, (3, 3), padding=“same”))
model.add(Activation(“relu”))
model.add(BatchNormalization(axis=chanDim))
model.add(Conv2D(32, (3, 3), padding=“same”))
model.add(Activation(“relu”))
model.add(BatchNormalization(axis=chanDim))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
CNN架构类似于VGG。 它简短,仅包含预定义的滤波器。 理论上,我们无需深度学习架构来区分真脸和假脸。
第一个卷积层,经过RELU激活,再连接另一个卷积层和RELU激活,最后进行池化。第二个卷积层,经过RELU激活,再连接另一个卷积层和RELU激活,最后进行池化。最后,我们将添加一个全连接层,经过RELU激活。
first (and only) set of FC => RELU layers
model.add(Flatten())
model.add(Dense(64))
model.add(Activation(“relu”))
model.add(BatchNormalization())
model.add(Dropout(0.5))
softmax classifier
model.add(Dense(classes))
model.add(Activation(“softmax”))
return the constructed network architecture
return model
全连接层和 ReLU 激活层组成,带有 softmax 分类器头。
模型返回。
======================================================================

基于我们收集的真实/欺骗图像数据集以及 LivenessNet 的实现,我们现在准备开始训练网络。
set the matplotlib backend so figures can be saved in the background
import matplotlib
matplotlib.use(“Agg”)
import the necessary packages
from pyimagesearch.livenessnet import LivenessNet
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.utils import to_categorical
from imutils import paths
import matplotlib.pyplot as plt
import numpy as np
import argparse
import pickle
import cv2
import os
construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument(“-d”, “–dataset”, required=True,
help=“path to input dataset”)
ap.add_argument(“-m”, “–model”, type=str, required=True,
help=“path to trained model”)
ap.add_argument(“-l”, “–le”, type=str, required=True,
help=“path to label encoder”)
ap.add_argument(“-p”, “–plot”, type=str, default=“plot.png”,
help=“path to output loss/accuracy plot”)
args = vars(ap.parse_args())
我们的面部活力训练脚本由许多导入(第 2-19 行)组成。现在让我们回顾一下:
matplotlib 用于生成训练图表。通过指定‘Agg’后端,我们能够方便地将绘图保存至磁盘的第3行。
LivenessNet :我们在上一节中定义的 liveness CNN。
train_test_split 由 scikit-learn 开发,负责构建数据分割,用于训练和测试。分类报告同样源自 scikit-learn,提供模型性能的简要统计报告。
ImageDataGenerator 用于执行数据增强操作,生成多样的图像样本,为模型训练提供多样化的图像数据。
Adam:为这个模型提供最佳优化选择。此外,替代方法还包括SGD、RMSprop等。路径设置:从我的imutils包中,该模块将帮助我们高效地收集磁盘上所有图像文件的路径。
pyplot :用于生成一个很好的训练图。
numpy :Python 的数值处理库。这也是 OpenCV 的要求。
argparse :用于处理命令行参数。
pickle :用于将我们的标签编码器序列化到磁盘。
cv2 :我们的 OpenCV 绑定。
os :这个模块可以做很多事情,但我们只是将它用作操作系统路径分隔符。
查看脚本的其余部分应该更简单。 此脚本接受四个命令行参数:
–dataset表示输入数据集的路径。在文章的前面部分,我们利用gather_examples.py脚本生成了数据集。
–model :我们的脚本将生成一个输出模型文件——在这里你提供它的路径。
–le :还需要提供输出序列化标签编码器文件的路径。
该脚本将生成一个绘图文件。 如果你希望覆盖默认的 plot.png 文件,建议在命令行参数中指定该文件名。
下一个代码块将执行一些初始化并构建我们的数据:
initialize the initial learning rate, batch size, and number of
epochs to train for
INIT_LR = 1e-4
BS = 8
EPOCHS = 50
grab the list of images in our dataset directory, then initialize
the list of data (i.e., images) and class images
print(“[INFO] loading images…”)
imagePaths = list(paths.list_images(args[“dataset”]))
data = []
labels = []
loop over all image paths
for imagePath in imagePaths:
extract the class label from the filename, load the image and
resize it to be a fixed 32x32 pixels, ignoring aspect ratio
label = imagePath.split(os.path.sep)[-2]
image = cv2.imread(imagePath)
image = cv2.resize(image, (32, 32))
update the data and labels lists, respectively
data.append(image)
labels.append(label)
convert the data into a NumPy array, then preprocess it by scaling
all pixel intensities to the range [0, 1]
data = np.array(data, dtype=“float”) / 255.0
设置训练参数,包括初始学习率、批量大小和EPOCHS。
在该位置,我们的imagePaths被获取。我们还创建了两个列表,用于存储我们的数据和类标签。通过循环构建数据和标签列表,数据由我们加载并调整为32×32像素的图像构成,每个图像都有一个对应的标签存储在标签列表中。
对像素强度进行归一化处理,并将列表转换为 NumPy 数组。接下来,我们对标签进行编码,并对数据进行分区。
encode the labels (which are currently strings) as integers and then
one-hot encode them
le = LabelEncoder()
labels = le.fit_transform(labels)
labels = to_categorical(labels, 2)
partition the data into training and testing splits using 75% of
the data for training and the remaining 25% for testing
(trainX, testX, trainY, testY) = train_test_split(data, labels,
test_size=0.25, random_state=42)
单热编码标签方案。首先,我们使用 scikit-learn 来划分数据集,其中 75% 用于训练,25% 保留作为测试集。随后,我们将初始化数据增强器并进行训练,以构建面部活力模型。
construct the training image generator for data augmentation
aug = ImageDataGenerator(rotation_range=20, zoom_range=0.15,
width_shift_range=0.2, height_shift_range=0.2, shear_range=0.15,
horizontal_flip=True, fill_mode=“nearest”)
initialize the optimizer and model
print(“[INFO] compiling model…”)
opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)
model = LivenessNet.build(width=32, height=32, depth=3,
classes=len(le.classes_))
model.compile(loss=“binary_crossentropy”, optimizer=opt,
metrics=[“accuracy”])
train the network
print(“[INFO] training network for {} epochs…”.format(EPOCHS))
H = model.fit(x=aug.flow(trainX, trainY, batch_size=BS),
validation_data=(testX, testY), steps_per_epoch=len(trainX) // BS,
epochs=EPOCHS)
创建数据增强模块,该模块能够支持随机旋转、缩放、平移、剪切以及翻转等图像处理操作。
搭建并优化LivenessNet模型。随后,我们投入了大量资源进行训练。鉴于我们采用的浅层网络架构和有限的数据量,整个训练过程所需的时间将较短。训练完成后,我们即可评估模型性能并生成训练曲线图。
evaluate the network
print(“[INFO] evaluating network…”)
predictions = model.predict(x=testX, batch_size=BS)
print(classification_report(testY.argmax(axis=1),
predictions.argmax(axis=1), target_names=le.classes_))
save the network to disk
print(“[INFO] serializing network to ‘{}’…”.format(args[“model”]))
model.save(args[“model”], save_format=“h5”)
save the label encoder to disk
f = open(args[“le”], “wb”)
f.write(pickle.dumps(le))
f.close()
plot the training loss and accuracy
plt.style.use(“ggplot”)
plt.figure()
plt.plot(np.arange(0, EPOCHS), H.history[“loss”], label=“train_loss”)
plt.plot(np.arange(0, EPOCHS), H.history[“val_loss”], label=“val_loss”)
plt.plot(np.arange(0, EPOCHS), H.history[“accuracy”], label=“train_acc”)
plt.plot(np.arange(0, EPOCHS), H.history[“val_accuracy”], label=“val_acc”)
plt.title(“Training Loss and Accuracy on Dataset”)
plt.xlabel(“Epoch #”)
plt.ylabel(“Loss/Accuracy”)
plt.legend(loc=“lower left”)
plt.savefig(args[“plot”])
基于测试集进行预测。随后生成一个分类报告并输出至终端。将 LivenessNet 模型与其标签编码器一并序列化至磁盘。
生成训练历史图以供以后检查。
========================================================================
python train.py --dataset dataset --model liveness.model --le le.pickle

===========================================================================
最后一步是组合所有部分:
我们将访问我们的网络摄像头/视频流
对每一帧应用人脸检测
对于检测到的每个人脸,应用我们的活体检测器模型
打开 liveness_demo.py 并插入以下代码:
import the necessary packages
from imutils.video import VideoStream
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.models import load_model
import numpy as np
import argparse
import imutils
import pickle
import time
import cv2
import os
construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument(“-m”, “–model”, type=str, required=True,
help=“path to trained model”)
ap.add_argument(“-l”, “–le”, type=str, required=True,
help=“path to label encoder”)
ap.add_argument(“-d”, “–detector”, type=str, required=True,
help=“path to OpenCV’s deep learning face detector”)
ap.add_argument(“-c”, “–confidence”, type=float, default=0.5,
help=“minimum probability to filter weak detections”)
args = vars(ap.parse_args())
最后
不知道你们使用什么开发环境,我通常使用Python 3.6版本和PyCharm作为开发工具。没有软件安装,或者缺乏相关资料支持,遇到问题无人解答,都可以免费获取相应的代码资源。过几天,我计划录制一个视频教程,有需要的小伙伴也可以关注获取。
给大家准备的学习资料包括但不限于:
Python 环境、pycharm编辑器/永久激活/翻译插件
python 零基础视频教程
Python 界面开发实战教程
Python 爬虫实战教程
Python 数据分析实战教程
python 游戏开发实战教程
Python 电子书100本
Python 学习路线规划

网上充斥着大量学习资料,但如果所学的知识体系不够完善,遇到问题时满足于浅尝辄止,缺乏深入研究的意愿,那么难以实现真正的技术突破。
有需要的系统化学习资料学习者,点击链接即可获取。
一个人可以单兵作战效率高,但团队协作更卓越!无论你是...还是...,我们都热忱欢迎你加入我们的技术交流圈子(技术交流、学习资源、大厂内推、职场吐槽、面试辅导),让我们一起学习成长!
