Les images du dataset sont issues de diverses sources:
1: Google
2: https://github.com/X-zhangyang/Real-World-Masked-Face-Dataset
3: https://www.kaggle.com/abhikjha/utk-face-cropped
Pour constituer notre dataset, nous avons réalisé ces étapes successives :
1: Collecte de l'image
2: Detection du visage avec OpenCV
3: Extraction du visage
4: Sauvegarde de l'image et mise aux dimensions 160x160
Une fois ces étapes réalisées, nous avons constitué un jeu d'apprentissage et un jeu de validation en prenant soins de faire en sorte que dans le jeu d'apprentissage les données soient équilibrées, c'est à dire qu'il y ait autant de visages avec masque et sans masque.
Pour y parvenir, nous avons utilisé la génération d'images à l'aide de Keras.
Une fois constitué, le dataset comporte :
Notre dataset et notre modèle sont disponibles par simple échange de mail
Pour la classification nous avons fait le choix d'utiliser le modèle près entrainé VGG16.
cheminTrain = "dataset/train/"
cheminValidation = "dataset/validation/";
nbImagesTrain = len([f for f in os.listdir(cheminTrain)if os.path.isfile(os.path.join(cheminTrain, f))])
print(" > Nombres d'images d'apprentissage : "+str(nbImagesTrain))
nbImagesValidation = len([f for f in os.listdir(cheminValidation)if os.path.isfile(os.path.join(cheminValidation, f))])
print(" > Nombres d'images de validation : "+str(nbImagesValidation))
data = []
labels = []
#Le label de l'image (masque 1 ou sans masque 0) est contenu dans le nom de l'image
for file in os.listdir(cheminTrain):
file_name, file_extension = os.path.splitext(file)
type = file_name.split("_")[1]
#Label de l'image
labels.append(int(type))
#Chargement de l'image
image = cv2.imread(cheminTrain+file_name+file_extension)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
image = cv2.resize(image, (224, 224))
#Sauvegarde de l'image
data.append(image)
# Conversion de image en tableau Numpy er egalisation des intensités de l'image
data = np.array(data) / 255.0
labels = np.array(labels)
# One Hot encoding des labels
# [1,0] = La personne ne porte pas de masque
# [0,1] = La personne porte un masque
lb = LabelBinarizer()
labels = lb.fit_transform(labels)
labels = to_categorical(labels)
# Découpage des données en jeu d'apprentissage (80%) et jeu de test (20%)
(trainX, testX, trainY, testY) = train_test_split(data, labels,test_size=0.20, stratify=labels, random_state=42)
# Chargement du model VGG16
baseModel = VGG16(weights="imagenet", include_top=False,input_tensor=Input(shape=(224, 224, 3)))
# On modifie la couche de sortie de VGG
sortieVGG = baseModel.output
sortieVGG = Flatten(name="flatten")(sortieVGG)
sortieVGG = Dense(512, activation="relu")(sortieVGG)
sortieVGG = Dropout(0.5)(sortieVGG)
sortieVGG = Dense(2, activation="softmax")(sortieVGG)
model = Model(inputs=baseModel.input, outputs=sortieVGG)
#Comparaison des modèles avant/après
baseModel.summary()
model.summary()
#On n'oublie pas de geler les couches du réseau VGG car on ne souhaite pas qu'elles perdent d'informations
#lors de la phase de rétro-propagation dû à notre modification de la sortie de VGG.
for layer in baseModel.layers:
layer.trainable = False
#Compilation du modèle
opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)
model.compile(loss="binary_crossentropy", optimizer=opt,metrics=["accuracy"])
# Generation de nouvelles images à l'aide de Keras lors de l'apprentissage
augmentation = ImageDataGenerator(rotation_range=15,fill_mode="nearest")
#Nombre d'iterations
EPOCHS = 1
#Taille des batchs
BS = 8
# Apprentissage
print("Apprentissage...")
H = model.fit_generator(
augmentation.flow(trainX, trainY, batch_size=BS),
steps_per_epoch=len(trainX) // BS,
validation_data=(testX, testY),
validation_steps=len(testX) // BS,
epochs=EPOCHS)
# Sauvegarde du modèle
model.save("QSTOMIT-MASQUE.model", save_format="h5")
import cv2
import os
import numpy as np
import tensorflow as tf
# Detection de visages à l'aide du model Cafee Model Zoo
# http://caffe.berkeleyvision.org/model_zoo.html
prototxt_path = os.path.join('model_data/deploy.prototxt')
caffemodel_path = os.path.join('model_data/weights.caffemodel')
model = cv2.dnn.readNetFromCaffe(prototxt_path, caffemodel_path)
#Chargement du modèle permettant de détecter le port du masque
modelMasque = tf.keras.models.load_model("QSTOMIT-MASQUE.model")
#Capture de la caméra (idCamera)
cap = cv2.VideoCapture(0)
while True:
_, image = cap.read()
blob = cv2.dnn.blobFromImage(cv2.resize(image, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0))
model.setInput(blob)
detections = model.forward()
h = image.shape[0]
w = image.shape[1]
for i in range(0, detections.shape[2]):
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
(startX, startY, endX, endY) = box.astype("int")
confidence = detections[0, 0, i, 2]
# If confidence > 0.5, save it as a separate file
if (confidence > 0.5):
frame = image[startY:endY, startX:endX]
#Appel du modèle appris pour la detection de masque
capture = cv2.resize(frame, (224, 224))
capture = capture.reshape((1, capture.shape[0], capture.shape[1], capture.shape[2]))
predict = modelMasque.predict(capture)
pasDeMasque = predict[0][0]
avecMasque = predict[0][1]
#Interpretation de la prediction
if (pasDeMasque > avecMasque):
cv2.rectangle(image, (startX, startY), (endX, endY),(0, 0, 255), 2)
cv2.putText(image, "PAS DE MASQUE", (startX, startY-10),cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 255), 2)
else:
cv2.rectangle(image, (startX, startY), (endX, endY),(0, 255, 0), 2)
cv2.putText(image, "OK", (startX, startY), cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 255, 255), 2)
# Affichage de l'image
cv2.imshow('img', image)
k = cv2.waitKey(30) & 0xff
if k==27:
break
cap.release()