###############################################################################
#                            Face Mask Detector                               #
###############################################################################

# * --------------------------------------------------------------------------*
# * AUTHOR: Rahul Bordoloi <mail@rahulbordoloi.me>                            *
# * --------------------------------------------------------------------------*
# * DATE CREATED: 11th July, 2020                                             *
# * ************************************************************************* *

# Importing Libraries
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.models import load_model
from imutils.video import VideoStream
import numpy as np
import argparse
import imutils
import time
import cv2
import os

# Hide Warnings
import warnings
warnings.filterwarnings("ignore")

def predMusk(frame, faceNet, maskNet):

    (h, w) = frame.shape[:2]     # Contructing a Blob from the Dimensions of the webcam frame
    blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), (104.0, 177.0, 123.0))

    faceNet.setInput(blob)       # Passing the blob through the network and obtaining face detections
    detections = faceNet.forward()

    faces = []   # List of faces
    locs = []    # Location of faces
    preds = []   # Predictions from fask mask model

    for i in range(0, detections.shape[2]):
        confidence = detections[0, 0, i, 2]

        if confidence > args["confidence"]:

            box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) # compute the coordinates for bounding box
            (startX, startY, endX, endY) = box.astype("int")

            # ensuring bounding boxes falls within the frame
            (startX, startY) = (max(0, startX), max(0, startY))
            (endX, endY) = (min(w - 1,endX), min(h - 1,endY))

            face = frame[startY:endY, startX:endX]      # extract face ROI
            face = cv2.cvtColor(face, cv2.COLOR_BGR2RGB)
            face = cv2.resize(face, (224,224))
            face = img_to_array(face)
            face = np.expand_dims(face, axis = 0)

            faces.append(face)
            locs.append((startX, startY, endX, endY))

    if len(faces) > 0:
        # faces = np.array(faces, dtype = "float64")
        preds = maskNet.predict(faces, batch_size = 32)
    return (locs, preds)

# Parsing Arguments passed through Command Line
ap = argparse.ArgumentParser()
ap.add_argument("-f", "--face", type = str, default = "face_detector")
ap.add_argument("-m", "--model", type = str, default = "maskDetector.model") 
ap.add_argument("-c", "--confidence", type = float, default = 0.5)
args = vars(ap.parse_args())

print("***** Loading FaceDetector *****")
prototxtPath = os.path.sep.join([args["face"], "deploy.prototxt"]) 
weightsPath = os.path.sep.join([args["face"], "res10_300x300_ssd_iter_140000.caffemodel"])    
faceNet = cv2.dnn.readNet(prototxtPath, weightsPath)

print("***** Loading FaceMask Detector *****")

maskNet = load_model(args["model"])

print("\n")
print("------------- Starting Video Stream -------------")

vs = VideoStream(0).start()
time.sleep(2.0)

# Loop over the Frames
while True:
    frame = vs.read()
    frame = imutils.resize(frame, width = 400)

    locs, preds = predMusk(frame, faceNet, maskNet)  # Calling Predictor Function

    # loop over the detected face locations and their corresponding & locations
    for (box, pred) in zip(locs, preds):
        (startX, startY, endX, endY) = box
        (mask, withoutMask) = pred

        # Bounding Box Design
        label = "Wearning Mask!" if mask > withoutMask else "Not Wearing Mask!"
        color = (0, 255, 0) if label == "Wearning Mask!" else (0, 0, 255)

        label = "{}: {:.2f}%".format(label, max(mask, withoutMask)* 100 )  # including the probability

        cv2.putText(frame, label, (startX, startY - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.45, color, 2)
        cv2.rectangle(frame, (startY, startY), (endX, endY), color, 2)
    
    # Output Frame
    cv2.imshow("Mask Detection", frame)
    if cv2.waitKey(1) & 0xFF == ord("e"):      # 'e' to exit the window
        break

print("------------- Video Stream Stopped -------------")
cv2.destroyAllWindows()
vs.stop()
