DETECTION DU PORT DU MASQUE - COVID 19

1: Constitution du dataset

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 :

  • 4220 images
  • 2954 images pour l'apprentissage
  • 1266 images pour la validation

Notre dataset et notre modèle sont disponibles par simple échange de mail

2: Utilisation de VGG16 pour la classification

Pour la classification nous avons fait le choix d'utiliser le modèle près entrainé VGG16.

Préparation des données

In [ ]:
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 et modification du modèle VGG16

In [ ]:
# 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"])

Apprentissage

In [ ]:
# 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")

3: Detection du port du masque en direct

In [ ]:
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()