前几天在谈到人脸识别有提到:大部分使用神经网路的模型都可以侦测与识别口罩下的人脸。
纳今天如果我们只是想要知道有没有戴口罩呢?
其实,我们只需要有一些训练资料,
跟简单的几个步骤,
就可以完成这个需求。
就让我们来实际训练一个简单的口罩辨识模型吧!
这里我们切成几个部分来看:
observations/experiements/data
整个目录复制到你的电脑上,里面应该包含
随意浏览with_mask图片你可以发现,实际上这些戴口罩的照片是"人工"合成的,但并不影响我们训练模型 (实际辨识效果也不差)
但要特别注意的是,with_mask与without_mask里面的照片不可以用同一个人,不然你的模型很难用在其他不在训练资料集的图片上,辨识的效果会很差。
- application
- mask_detector
- train.py
- detect_webcam.py
- dataset
- mask_person
- with_mask
- without_mask
目录结构与档案应该很好懂,我们继续下一部份
train.py
档案,撰写以下程序码 (说明在程序码内注解):
import argparse
import ntpath
import os
import matplotlib.pyplot as plt
import numpy as np
from imutils import paths
from sklearn.metrics import classification_report
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelBinarizer
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
from tensorflow.keras.layers import AveragePooling2D
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Input
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.preprocessing.image import load_img
from tensorflow.keras.utils import to_categorical
def main():
# 初始化Arguments
ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset", required=True, help="path to input dataset")
ap.add_argument("-p", "--plot", type=str, default="plot.png", help="path to output loss/accuracy plot")
ap.add_argument("-m", "--model", type=str, default="mask_detector.model",
help="path to output face mask detector model")
args = vars(ap.parse_args())
# 初始化训练用参数与Batch Size
INIT_LR = 1e-4
EPOCHS = 20
BS = 32
# 载入图片
print("[INFO] loading images...")
imagePaths = list(paths.list_images(args["dataset"]))
data = []
labels = []
# 将训练图片进行前处理与建立训练data
for imagePath in imagePaths:
label = ntpath.normpath(imagePath).split(os.path.sep)[-2]
# 注意这里将图片转成224 x 224,与MobileNetV2模型需要的Input一样大小
image = load_img(imagePath, target_size=(224, 224))
image = img_to_array(image)
image = preprocess_input(image)
data.append(image)
labels.append(label)
data = np.array(data, dtype="float32")
labels = np.array(labels)
# 将类别encoding成数值方便训练
lb = LabelBinarizer()
labels = lb.fit_transform(labels)
labels = to_categorical(labels)
# 切分训练资料与测试资料
(trainX, testX, trainY, testY) = train_test_split(data, labels, test_size=0.20, stratify=labels, random_state=9527)
# 做Data Argumentation,强化模型的辨识能力
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")
# 载入模型,去除模型最後一层 (等等要改为我们要辨识的"两种类别")
baseModel = MobileNetV2(weights="imagenet", include_top=False, input_tensor=Input(shape=(224, 224, 3)))
# 组合自定义的最後层
headModel = baseModel.output
headModel = AveragePooling2D(pool_size=(7, 7))(headModel)
headModel = Flatten(name="flatten")(headModel)
headModel = Dense(128, activation="relu")(headModel)
headModel = Dropout(0.5)(headModel)
headModel = Dense(2, activation="softmax")(headModel)
# 建立模型
model = Model(inputs=baseModel.input, outputs=headModel)
# 确认模型只有我们新增的最後层可以训练 (transfer learning)
for layer in baseModel.layers:
layer.trainable = False
# 编译模型
print("[INFO] compiling model...")
opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)
model.compile(loss="binary_crossentropy", optimizer=opt, metrics=["accuracy"])
# 开始训练
print("[INFO] training head...")
H = model.fit(
aug.flow(trainX, trainY, batch_size=BS),
steps_per_epoch=len(trainX) // BS,
validation_data=(testX, testY),
validation_steps=len(testX) // BS,
epochs=EPOCHS)
# 使用测试资料验证模型准确率
print("[INFO] evaluating network...")
predIdxs = model.predict(testX, batch_size=BS)
predIdxs = np.argmax(predIdxs, axis=1)
# 印出测试结果
print(classification_report(testY.argmax(axis=1), predIdxs, target_names=lb.classes_))
# 储存模型
print("[INFO] saving mask detector model...")
model.save(args["model"], save_format="h5")
# 划出训练结果
N = EPOCHS
plt.style.use("ggplot")
plt.figure()
plt.plot(np.arange(0, N), H.history["loss"], label="train_loss")
plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss")
plt.plot(np.arange(0, N), H.history["accuracy"], label="train_acc")
plt.plot(np.arange(0, N), H.history["val_accuracy"], label="val_acc")
plt.title("Training Loss and Accuracy")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend(loc="lower left")
plt.savefig(args["plot"])
if __name__ == '__main__':
main()
train.py
档案的目录下,执行python train.py -d {你的mask_person资料集目录}
模型的准确率,有99%
[INFO] evaluating network...
precision recall f1-score support
with_mask 0.99 0.99 0.99 138
without_mask 0.99 0.99 0.99 138
accuracy 0.99 276
macro avg 0.99 0.99 0.99 276
weighted avg 0.99 0.99 0.99 276
训练模型的accuracy与loss的趋势,可以看出模型真的有学习到东西,并且没有过度训练
最後在你当前的目录下会产生一个mask_detector.model
的档案,训练完成!
detect_webcam.py
,撰写以下程序码 (说明在程序码内注解):
import argparse
import time
import cv2
import imutils
import mtcnn
import numpy as np
from imutils.video import WebcamVideoStream
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing.image import img_to_array
# 初始化脸部侦测模型
detector = mtcnn.MTCNN()
# 辨识人脸与侦测是否有戴口罩
def detect_and_predict_mask(frame, mask_net):
faces = []
locs = []
preds = []
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img_h, img_w = rgb.shape[:2]
bboxes = detector.detect_faces(rgb)
for bbox in bboxes:
(x, y, w, h) = bbox['box']
padding = 35
(crop_x0, crop_x1) = (x - padding if x > padding else 0, x + w + padding if x + w + padding < img_w else img_w)
(crop_y0, crop_y1) = (y - padding if y > padding else 0, y + h + padding if y + h + padding < img_h else img_h)
face = rgb[crop_y0:crop_y1, crop_x0:crop_x1]
face = cv2.resize(face, (224, 224))
face = img_to_array(face)
face = preprocess_input(face)
faces.append(face)
locs.append((x, y, x + w, y + h))
if len(faces) > 0:
faces = np.array(faces, dtype="float32")
preds = mask_net.predict(faces, batch_size=32)
return (locs, preds)
def main():
# 初始化Arguments
ap = argparse.ArgumentParser()
ap.add_argument("-m", "--model", default="mask_detector.model", help="path to the trained mask model")
args = vars(ap.parse_args())
maskNet = load_model(args["model"])
# 启动WebCam
vs = WebcamVideoStream().start()
time.sleep(2.0)
while True:
frame = vs.read()
frame = imutils.resize(frame, width=400)
(locs, preds) = detect_and_predict_mask(frame, maskNet)
for (box, pred) in zip(locs, preds):
(startX, startY, endX, endY) = box
(mask, withoutMask) = pred
(label, color) = ("Mask", (0, 255, 0)) if mask > withoutMask else ("No Mask", (0, 0, 255))
label = "{}: {:.2f}%".format(label, max(mask, withoutMask) * 100)
cv2.putText(frame, label, (startX, startY - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.45, color, 2)
cv2.rectangle(frame, (startX, startY), (endX, endY), color, 2)
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
cv2.destroyAllWindows()
vs.stop()
if __name__ == '__main__':
main()
这个程序码如果理解前面系列文章,大部分的内容大同小异,几个重点说明一下:
最後执行的结果如下
希望你还喜欢今天的应用,明天见!
今日传送门
>>: [NestJS 带你飞!] DAY25 - Authorization & RBAC
目的 使用包覆(Wrapper)的方式,可以动态地给物件增添新的功能,或是重新定义既有的功能,达到扩...
1. Benchmark,是GO语言用来做函数的效能测试。 2. Go语言对效能测试函数的名称与函数...
早安安! 今天是Python基本介绍的最後一天了~ 6天真的太短了,有好多东西想讲但都讲不完 ಥ⌣ಥ...
团队目标的建立 提供对应的元素,催化IT团队与事业体相呼应的目标 身为产品经理,需要清楚知道公司目标...
说了好几天的概念,再不动手真的会睡着。让我们先来本机炸一炸,加深我们的学习动机。 安装前,先注意两大...