마스크를 썼는지 안썼는지 실시간으로 확인하는인공지능을 구현해보자.

 

목차

  1. Load dataset
  2. Training model
    1. Labeling
    2. Construct model
    3. Training
  3. Use pre-trained model
  4. Result

Load Dataset

마스크 쓴 사람과 마스크 안쓴 사람의 데이터로 학습이 끝난 마스크 classifier를 활용한 마스크 detector로 진행이 됩니다.

 

 

아래와 같이 마스크 안 쓴사람의 이미지를 구해서, Face detection을 한번 하고, 잘라낸 face 에서 랜드마크 detection을 합니다.

그 다음에 배경이 투명한 마스크 이미지를 구해서 랜드마크를 기준으로 마스크를 씌워줍니다.

요런 식으로 해서 마스크를 안쓴 이미지에서 마스크를   이미지를 만들었습니다.

 

No mask image

 

Face detection

 

Landmark detection

 

Transparent mask image

 

 

Mask on face

 

 

690장의 마스크 쓴 사람의 이미지가 만들어졌습니다.


Training model 

학습 모델은 다른 사람이 만들어놓은 모델(MobileNetV2)을 전이학습을 해서 모델을 이용합니다.

 

컴퓨터 비전에서 말하는 전이학습은 주로 사전 학습이 된 모델을 이용하는 것을 뜻하고,

여기서 사전학습 모델이란, 내가 풀고자 하는 문제와 비슷하면서 사이즈가 데이터로 이미 학습이 되어 있는 모델을 말합니다.

 


Labeling

데이터에 대해서 마스크를 썼으면 (1,0) 안썼으면 (0,1)으로 레이블을 원핫인코딩을 합니다.

그리고 train data test 데이터를 분류해줍니다.

# perform one-hot encoding on the labels
lb = LabelBinarizer()
labels = lb.fit_transform(labels)
labels = to_categorical(labels)

# partition the data into training and testing splits using 80% of
# the data for training and the remaining 20% for testing
(trainX, testX, trainY, testY) = train_test_split(data, labels,
	test_size=0.20, stratify=labels, random_state=42)

# construct the training image generator for data augmentation
aug = ImageDataGenerator(
	rotation_range=20,
	zoom_range=0.15,
	width_shift_range=0.2,
	height_shift_range=0.2,
	shear_range=0.15,
	horizontal_flip=True,
	fill_mode="nearest")

 


Construct Model

 

그리고 나서 MobileNet으로 모델을 생성합니다.

 

첫번째 레이어는 대표적인 히든 레이어의 활성화 함수인 relu로

두번째 레이어는 역시나 Multi classification의 최종 활성화 함수로 잘 쓰이는 Softmax로 구성해주고

Classification 용이한 Lossfunction CrossEntropy 사용해줍니다.

# load the MobileNetV2 network, ensuring the head FC layer sets are
# left off
baseModel = 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
headModel = baseModel.output
headModel = AveragePooling2D(pool_size=(7, 7))(headModel)
headModel = Flatten(name="flatten")(headModel)
headModel = Dense(128, activation="relu")(headModel)
headModel = Dropout(0.5)(headModel)
headModel = Dense(2, activation="softmax")(headModel)

# place the head FC model on top of the base model (this will become
# the actual model we will train)
model = Model(inputs=baseModel.input, outputs=headModel)

# loop over all layers in the base model and freeze them so they will
# *not* be updated during the first training process
for layer in baseModel.layers:
	layer.trainable = False
    
# compile our model
print("[INFO] compiling model...")
opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)
model.compile(loss="binary_crossentropy", optimizer=opt,
	metrics=["accuracy"])

 


Training

# train the head of the network
print("[INFO] training head...")
H = model.fit(
	aug.flow(trainX, trainY, batch_size=BS),
	steps_per_epoch=len(trainX) // BS,
	validation_data=(testX, testY),
	validation_steps=len(testX) // BS,
	epochs=EPOCHS)

 


Use model

 

from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
from tensorflow.keras.models import load_model
import numpy as np
import cv2
import matplotlib.pyplot as plt
import os
import argparse

# Face detection model : opencv dnn model
facenet = cv2.dnn.readNet('models/deploy.prototxt', 'models/res10_300x300_ssd_iter_140000.caffemodel')

# keras model
model = load_model('models/mask_detector.model') 

#cap = cv2.VideoCapture('imgs/01.mp4')
parser = argparse.ArgumentParser() 
parser.add_argument('--video', help='Input video path') 
args = parser.parse_args() 

cap = cv2.VideoCapture(args.video if args.video else 0)
ret, img = cap.read()

fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
out = cv2.VideoWriter('output.mp4', fourcc, cap.get(cv2.CAP_PROP_FPS), (img.shape[1], img.shape[0]))# 저장

while cap.isOpened():
    ret, img = cap.read()
    if not ret:
        break

    h, w = img.shape[:2] # height and width

    # Preprocess Image for Face Detection
    blob = cv2.dnn.blobFromImage(img, scalefactor=1., size=(300, 300), mean=(104., 177., 123.))
    facenet.setInput(blob)
    dets = facenet.forward() 

    result_img = img.copy()

    for i in range(dets.shape[2]):
        confidence = dets[0, 0, i, 2]
        if confidence < 0.5:
            continue

        # x, y bounding box
        x1 = int(dets[0, 0, i, 3] * w)
        y1 = int(dets[0, 0, i, 4] * h)
        x2 = int(dets[0, 0, i, 5] * w)
        y2 = int(dets[0, 0, i, 6] * h)
        
        # face detection
        face = img[y1:y2, x1:x2]

        # mask detection and prediction
        face_input = cv2.resize(face, dsize=(224, 224))
        face_input = cv2.cvtColor(face_input, cv2.COLOR_BGR2RGB)
        face_input = preprocess_input(face_input)
        face_input = np.expand_dims(face_input, axis=0)
        
        # mask, nomask percentage
        mask, nomask = model.predict(face_input).squeeze()

        if mask > nomask:
            color = (0, 255, 0)
            label = 'Mask %d%%' % (mask * 100)
        else:
            color = (0, 0, 255)
            label = 'No Mask %d%%' % (nomask * 100)

        cv2.rectangle(result_img, pt1=(x1, y1), pt2=(x2, y2), thickness=2, color=color, lineType=cv2.LINE_AA)
        cv2.putText(result_img, text=label, org=(x1, y1 - 10), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=0.8, color=color, thickness=2, lineType=cv2.LINE_AA)

    out.write(result_img)
    cv2.imshow('result', result_img)
    if cv2.waitKey(1) == ord('q'):
        break

out.release()
cap.release()

그리고 나서 pretrained model 가져다가 코드를 짰습니다.

 

먼저 좀 다르게 전처리를 해주기 위해서 opencv의 dnn 모델인 Facedetection model과 Mask dectector 모델을 사용합니다.

그리고 예전에 배웠던 Aurgument parser와 비디오 캡쳐를 이용해서 웹캠을 이용하였고

facenet을 이용해서 Face detection을 진행합니다.

 

그리고 dets 변수에 저장을 해주고

for문을 돌면서 바운딩 박스 안에 들어있는 face detection 해주는데 이부분은 조금더 공부를 해봐야   같습니다.

 

Face detection이 끝나고  마스크 모델을 적용하기 위해서

먼저 detection된 face를 모델에 맞춰주기 위해서 전처리를 해주고,

또 이미지를 모델에 맞게끔 RGB로도 바꿔주고,

그리고 역시나 또 모델에 맞게끔 preprocess_input을 해주고,

shape 맞춰주기 위해서 차원을 하나 추가해 줍니다.

 

그리고 나서 predict를 해서 

내가 마스크를 썼는지, 안썼는지 

초록색 네모칸으로 마스크를 쓴 확률과 빨간색 네모칸으로 마스크를 안쓴 확률을 보여줍니다.

 


Result

 

그래서 나온 결과로 아래와 같은 결과를 확인할 수 있었는데 움직일 때마다 No mask로 나오는 경우가 있더라구요.

이게 연산이 느려서 그런건지 어떤건지는 잘 모르겠고

Face detection 거친 후에 끝부분과 입의 랜드마크가 모두 인식되지 않는다면 마스크를 썼다고 분류하는 같습니다

정면
측면
지갑

 

손 스크

 

 

 

 

복사했습니다!