text: # -*- coding: utf-8 -*-
  """maskDetector.ipynb
  
  Automatically generated by Colaboratory.
  
  Original file is located at
      https://colab.research.google.com/drive/1qQ_FQXld1pwJqdLvEpdv3NeR0GALPd3a
  
  # References:
  * https://www.kaggle.com/general/74235
  * https://www.kaggle.com/andrewmvd/face-mask-detection
  
  # Dataset download from Kaggle to Google Colab
  Required the file kaggle.json dowloaded from Kaggle profile
  """
  
  """! mkdir ~/.kaggle/
  
  ! cp kaggle.json ~/.kaggle/
  
  ! chmod 600 ~/.kaggle/kaggle.json
  
  ! kaggle datasets download 'andrewmvd/face-mask-detection'
  
  ! mkdir faces
  
  ! unzip face-mask-detection.zip -d faces"""
  
  """# Required libraries to data processing"""
  """
  import cv2
  import pandas as pd
  from lxml import etree
  import os
  from google.colab.patches import cv2_imshow
  from sklearn.utils import shuffle
  from sklearn.model_selection import train_test_split
  import numpy as np
  
  import tensorflow as tf
  from tensorflow import keras
  from tensorflow.keras import Sequential, models
  from tensorflow.keras.layers import Flatten, Dense, Conv2D, MaxPool2D
  from keras.preprocessing.image import ImageDataGenerator
  from sklearn.metrics import classification_report, confusion_matrix"""
  
  """# Data pre-processing
  * Crop the faces presents in the image (folder faces/images) according to the bound-boxes presents in the dataset (folder faces/annotations)
  
  """
  
  """label_dict = {
      'with_mask': 0,
      'mask_weared_incorrect': 1,
      'without_mask': 2
  }
  
  df = pd.DataFrame(columns = ['image', 'name', 'index', 'label', 'target'])
  print(df)
  
  img_height = 50
  img_width = 50
  
  ! mkdir cropped_images
  
  for imageName in os.listdir('/content/faces/images'):
    annotationName = imageName.split('.')[0] + '.xml'
    annotation = etree.parse('/content/faces/annotations/' + annotationName)
    objectList = annotation.findall('object') 
  
    cont = 0
    for o in objectList:
      target = o.find('name').text
  
      bndbox = o.find('bndbox')
      xmin = int(bndbox.find('xmin').text)
      ymin = int(bndbox.find('ymin').text)
      xmax = int(bndbox.find('xmax').text)
      ymax = int(bndbox.find('ymax').text)
  
      img = cv2.imread('/content/faces/images/' + imageName)
      roi = img[ymin:ymax, xmin:xmax]
      resized_image = cv2.resize(roi, (img_height, img_width))
  
      cv2.imwrite('/content/cropped_images/' + str(cont) + '_' + imageName, resized_image)
      df = df.append({'image': cv2.cvtColor(resized_image, cv2.COLOR_BGR2GRAY).flatten(), 
                      'name': str(cont) + '_' + imageName, 
                      'index': cont, 
                      'label': label_dict[target], 
                      'target': target}, ignore_index = True)
      cont += 1
  
  df.head()
  
  #cv2_imshow(df.loc[3]['image'])
  
  df.shape
  
  df['label'].value_counts()
  
  df = shuffle(df)
  
  df.head()
  
  X = list(df['image'])
  y = list(df['label'])
  
  X[0:10]
  
  y[0:10]"""
  
  """#Building the model with neural retwork"""
  
  """df.head()
  
  df.shape
  
  from keras_preprocessing.image import ImageDataGenerator
  
  train_image_generator = ImageDataGenerator(rescale = 1. / 255., validation_split = 0.25)
  
  train_generator = train_image_generator.flow_from_dataframe(
      dataframe = df,
      directory = '/content/cropped_images',
      x_col = 'name',
      y_col = 'target',
      subset = 'training',
      batch_size = 32,
      seed = 42,
      shuffle = True,
      class_mode = 'categorical',
      target_size = (50, 50)
  )
  
  valid_generator = train_image_generator.flow_from_dataframe(
      dataframe = df,
      directory = '/content/cropped_images',
      x_col = 'name',
      y_col = 'target',
      subset = 'validation',
      batch_size = 32,
      seed = 42,
      shuffle = True,
      class_mode = 'categorical',
      target_size = (50, 50)
  )
  
  print(train_generator)
  print(valid_generator)
  
  input_shape = [img_width, img_height, 3]
  
  classes = list(df['label'].unique())
  classes
  
  model_1 = keras.models.Sequential([
      keras.layers.Conv2D(filters = 10, kernel_size = 3, activation = 'relu', 
                          input_shape = input_shape),
      keras.layers.Conv2D(filters = 10, kernel_size = 3, activation = 'relu'),
      keras.layers.MaxPool2D(pool_size = 2, padding = 'valid'),
      keras.layers.Conv2D(filters = 10, kernel_size = 3, activation = 'relu'),
      keras.layers.Conv2D(filters = 10, kernel_size = 3, activation = 'relu'),
      keras.layers.MaxPool2D(pool_size = 2, padding = 'valid'),
      keras.layers.Flatten(),
      keras.layers.Dense(units = len(classes), activation = 'softmax')
  ])
  
  model_1.compile(loss = 'categorical_crossentropy', 
                  optimizer = keras.optimizers.Adam(), 
                  metrics = ['accuracy', keras.metrics.Precision(), keras.metrics.Recall()])
  # 'categorical_crossentropy': used as a loss function for multi-class classification model where there are two or more output labels
  
  model_1.summary()
  
  history_1 = model_1.fit(train_generator, epochs = 10, steps_per_epoch = len(train_generator), 
                          validation_data = valid_generator, validation_steps = len(valid_generator))
  
  result_1 = pd.DataFrame(history_1.history)
  result_1"""
  
  """ About the loss metric:
  
   Loss: A scalar value that we attempt to minimize during our training of the model. The lower the loss, the closer our predictions are to the true labels. What you'd expect to see from running fit on your Keras model, is a decrease in loss over n number of epochs.
  
   References: 
  
   [1] - https://towardsdatascience.com/11-evaluation-metrics-data-scientists-should-be-familiar-with-lessons-from-a-high-rank-kagglers-8596f75e58a7
  
   [2]- https://stackoverflow.com/questions/34673396/what-does-the-standard-keras-model-output-mean-what-is-epoch-and-loss-in-keras
  
  """
  
  """result_1.plot()
  
  n = 20
  cont = 0
  
  for imageName in os.listdir('/content/cropped_images/'):
    if(cont >= n):
      break
    print('\n' + imageName)
  
    img = cv2.imread('/content/cropped_images/' + imageName)
    cv2_imshow(img)
    
    img1 = tf.keras.utils.load_img('/content/cropped_images/' + imageName, target_size=(img_height, img_width))
    img1_array = tf.keras.utils.img_to_array(img1)
    img1_array = tf.expand_dims(img1_array, 0)
  
    predictions = model_1.predict(img1_array)
    print(predictions)
  
    score = tf.nn.softmax(predictions[0])
    print(score)
    cont += 1
  
  label_dict"""
  
  """# Split the dataframe in train (80%) and test (20%) sets"""
  
  """X[0:10]
  
  y[0:10]
  
  X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
  
  X_train[0:10]
  
  len(X_train)
  
  y_train[0:10]
  
  len(y_train)
  
  X_test[0:10]
  
  len(X_test)
  
  y_test[0:10]
  
  len(y_test)"""
  
  """# Building the model with DT classifier"""
  
  """from sklearn.tree import DecisionTreeClassifier
  from sklearn import metrics"""
  
  """dt = DecisionTreeClassifier()
  
  dt.fit(X_train, y_train)
  
  y_pred = dt.predict(X_test)
  
  print("Score: ", dt.score(X_test, y_test))
  print("Accuracy:", metrics.accuracy_score(y_test, y_pred))
  print("Precision:", metrics.precision_score(y_test, y_pred, average=None)) #None, 'micro', 'macro', 'weighted' | If None, the scores for each class are returned.
  print("Recall:", metrics.recall_score(y_test, y_pred, average=None))
  print("F1-score:", metrics.f1_score(y_test, y_pred, average=None))
  
  n = 20
  cont = 0
  
  for imageName in os.listdir('/content/cropped_images/'):
    if(cont >= n):
      break
    print('\n' + imageName)
  
    img = cv2.imread('/content/cropped_images/' + imageName)
    cv2_imshow(img)
  
    img_array = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY).flatten().reshape(1, -1)
    print(img_array)
  
    pred = dt.predict(img_array)
    print(pred)
  
    cont += 1"""
  
  """# Building the model with KNN classifier"""
  
  """from sklearn.model_selection import GridSearchCV
  from sklearn.neighbors import KNeighborsClassifier"""
  
  """grid_params = {
    "n_neighbors": [2, 3, 5, 11, 19, 23, 29],
    "weights": ["uniform", "distance"],
    "metric": ["euclidean", "manhattam"]
  }
  
  knn_model = GridSearchCV(KNeighborsClassifier(), grid_params, refit=True)
  
  knn_model.fit(X_train, y_train)
  
  y_pred = knn_model.predict(X_test)
  
  print("Score: ", knn_model.score(X_test, y_test))
  print("Accuracy:", metrics.accuracy_score(y_test, y_pred))
  print("Precision:", metrics.precision_score(y_test, y_pred, average=None)) #None, 'micro', 'macro', 'weighted' | If None, the scores for each class are returned.
  print("Recall:", metrics.recall_score(y_test, y_pred, average=None))
  print("F1-score:", metrics.f1_score(y_test, y_pred, average=None))
  
  n = 20
  cont = 0
  
  for imageName in os.listdir('/content/cropped_images/'):
    if(cont >= n):
      break
    print('\n' + imageName)
  
    img = cv2.imread('/content/cropped_images/' + imageName)
    cv2_imshow(img)
  
    img_array = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY).flatten().reshape(1, -1)
    print(img_array)
  
    pred = dt.predict(img_array)
    print(pred)
  
    cont += 1"""
  
  """# Testing the three models in a frame captured by the camera"""
  
  def classify_image(frame, model_type, model):
    label = {
      0: "Com mascara",
      1: "Mascara vestida incorretamente",
      2: "Sem mascara"
    }
    
    img_height = 50
    img_width = 50
  
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  
    classifier = cv2.CascadeClassifier('utils/haarcascade_frontalface_alt2.xml')
    faces = classifier.detectMultiScale(gray)
    
    for x,y,w,h in faces:
      gray_face = gray[y:y+h, x:x+w]
      
      if model_type == 'c':
          gray_face = cv2.resize(gray_face, (img_height, img_width))
          pred = model.predict(gray_face.flatten().reshape(1, -1))[0]
      elif model_type == 'r':
          img1 = tf.keras.utils.load_img(gray_face, target_size=(img_height, img_width))
          img1_array = tf.keras.utils.img_to_array(img1)
          img1_array = tf.expand_dims(img1_array, 0)
          pred = model.predict(img1_array)
      else:
          print('Invalid model type')
          return
      
      #print(pred)
  
      classification = label[pred]
      
      if pred == 0:
        cv2.rectangle(frame, (x,y), (x+w, y+h), (0,255,0), 3)
      elif pred == 1:
        cv2.rectangle(frame, (x,y), (x+w, y+h), (0,255,255), 3)
      elif pred == 2:
        cv2.rectangle(frame, (x,y), (x+w, y+h), (0,0,255), 3)
  
  
  from imutils.video import VideoStream
  from picamera import PiCamera
  from time import sleep
  import cv2
  import pickle
  #from tensorflow import keras
  
  camera = VideoStream(usePiCamera=True).start()
  sleep(1.0)
  in_loop = True
  
  while in_loop:
      ret, frame = camera.read(), camera.read()
      
      model_type = 'c'   #c   r
      filename = 'models/dt_model.sav'  #dt_model.sav   knn_model.sav   nn_model
      loaded_model = pickle.load(open(filename, 'rb'))
      #loaded_model = keras.models.load_model(filename)
      
      classify_image(frame, model_type, loaded_model)
          
      cv2.imshow("Video", frame)
      
      key = cv2.waitKey(1) & 0xFF == ord('q')
      if key:
          in_loop = False
      
  cv2.destroyAllWindows()
  camera.stop()