Hi guys! In this article, I’m going to tell you how to create your own face mask detector model. I’ll be using a Face Mask dataset created by Prajna Bhandary. This dataset consists of 1,376 images belonging to two classes, with mask and without mask.
The main focus of this model is to detect whether a person is wearing a mask or not.
Firstly, we get the image with the face and run it through a cascade classifier. The classifier will give the region of interest of the face (height and width).
Secondly, we will resize the region of interest and pass it to a pre-trained CNN, it will give us the probability as an output.
To train a deep learning model to classify whether a person is wearing a mask or not, we need to find a good dataset with a fair amount of images for both classes:
- wearing a mask
- not wearing a mask
I have used the face mask dataset created by Prajna Bhandary. You can access the dataset here.
Building Face Mask Detector model
For building the model, I am going to use Keras and TensorFlow to train a classifier to automatically detect whether a person is wearing a mask or not.
I’ll be fine-tuning the MobileNet V2 architecture with pre-trained ImageNet weights, a highly efficient architecture that can be applied to embedded devices with limited computational capacity.
Import necessary libraries
from tensorflow.keras.preprocessing.image import ImageDataGenerator, img_to_array, load_img from tensorflow.keras.applications import MobileNetV2 from tensorflow.keras.layers import AveragePooling2D, Dropout, Flatten, Dense, Input from tensorflow.keras.models import Model from tensorflow.keras.optimizers import Adam from tensorflow.keras.applications.mobilenet_v2 import preprocess_input from tensorflow.keras.utils import to_categorical from sklearn.preprocessing import LabelBinarizer from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report from imutils import paths import numpy as np import os
I’ll be using:
- tensorflow.keras for data augmentation, loading the MobilNetV2 classifier, building a new fully-connected (FC) head, pre-processing and loading image data
- scikit-learn (sklearn) for binarizing class labels, segmenting our dataset, and printing a classification report.
- imutils paths implementation will help us to find and list images in our dataset.
- matplotlib to plot our training curves
Define path to Dataset
Now, provide a path to the dataset which contains with masks and without masks images.
dataset_path = "dataset"
Next, I’ll define parameters which will hold the initial learning rate, number of epochs and batch size.
INIT_LR = 1e-4 EPOCHS = 20 BS = 32
Load and pre-process data
Now, I’ll be loading and pre-processing the data.
imagePaths = list(paths.list_images(dataset_path)) images =  labels =  for img_path in imagePaths: # extract the class label from the filename label = img_path.split(os.path.sep)[-2] # load the input image (224x224) and preprocess it image = load_img(img_path, target_size=(224, 224)) image = img_to_array(image) image = preprocess_input(image) # update the data and labels lists, respectively images.append(image) labels.append(label)
Firstly, I have grabbed paths of all the images in imagePaths variable. Then, I have initialized images and labels list. Next, I’ll loop over imagePaths and extract the lable from the image path which will later be appended to labels list. Images will be pre-processed and then they are appended to images list.
Now, I’ll convert images and labels into numpy array and the result will be stored in data and labels variable.
data = np.array(images, dtype="float32") labels = np.array(labels)
Next, I’ll perform one-hot encoding on our class labels.
# perform one-hot encoding on the labels labelbinarizer = LabelBinarizer() labels = labelbinarizer.fit_transform(labels) labels = to_categorical(labels)
Next, I’ll be segmenting our data into training and testing part using scikit’s learn train_test_split. I’ll use 80% data for training and rest 20% for testing.
(trainX, testX, trainY, testY) = train_test_split(data, labels, test_size=0.20, stratify=labels, random_state=42)
To improve generalization, I’ll perform data augmentation where the random rotation, zoom, shear, shift, and flip parameters are established.
# construct the training image generator for data augmentation datagen = ImageDataGenerator( rotation_range=25, zoom_range=0.2, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, horizontal_flip=True, fill_mode="nearest")
Next, I’ll be preparing MobileNetV2 classifier for fine-tuning.
bModel = MobileNetV2(weights="imagenet", include_top=False, input_tensor=Input(shape=(224, 224, 3))) # construct the head of the model that will be placed on top of the the base model hModel = bModel.output hModel = AveragePooling2D(pool_size=(5, 5))(hModel) hModel = Flatten(name="flatten")(hModel) hModel = Dense(128, activation="relu")(hModel) hModel = Dropout(0.5)(hModel) hModel = Dense(2, activation="softmax")(hModel) model = Model(inputs=bModel.input, outputs=hModel) for layer in bModel.layers: layer.trainable = False
To perform fine-tuning, firstly I’ll load MobileNet with pre-trained ImageNet weights, leaving off head of the network. Then, I’ll construct a new Fully connected head and replace in with the old head in base (line 11). Next, I’ll freeze the base layers of the network. The weights of these base layers will not be updated during the process of backpropagation, whereas the head layer weights will be tuned.
Training and Compiling the model
Now, as our data have been prepared and model architecture is ready, we are now ready to compile and train our face mask detector model.
# compile our model opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS) model.compile(loss="binary_crossentropy", optimizer=opt, metrics=["accuracy"]) # train the head H = model.fit( datagen.flow(trainX, trainY, batch_size=BS), steps_per_epoch=len(trainX) // BS, validation_data=(testX, testY), validation_steps=len(testX) // BS, epochs=EPOCHS)
I have used Adam optimizer and binary cross-entropy to compile the model.
Saving the model
Lastly, I’ll be saving the model so that it can be used in future.
#save the model print("saving the mask detector model...") model.save("model_mask.h5") print('MODEL SAVED')
Our face mask detector is accurate, and since we used the MobileNetV2 architecture, it’s also computationally efficient. We can deploy this model to embedded systems as well. If we deployed it correctly, we can help ensure the safety of others. You can access the entire code here.
That’s it for the article. I hope it’ll be useful for someone.
If you enjoyed this article, share it with your friends and colleagues! Thank you and stay safe!