221020 중간발표 AI엔진파트 - Marvic1130/BlinkingRecognitionProject GitHub Wiki

2022 2학기 캡스톤 3조 중간발표 AI엔진 파트

프로젝트 개요

수업집중도 데이터를 통해 강의평가 신뢰도를 높이기 위한 강의평가 웹 서비스

수업 집중도는 수업시간중 눈 깜빡임 횟수를 이용하여 도출한다.


CreateData.py

코드 개요

눈 깜빡임을 검출하기 위해 눈을 뜨고있는 데이터와 눈을 감고있는 데이터를 수집하기위한 코드

이 코드의 큰 틀은 오픈소스 를 활용했다.

마스크와 모자착용 관계없이 얼굴을 인식하여 데이터를 저장

얼굴을 인식하면 하관을 제외한 부분을 저장하여 눈 부분에 초점을 맞출 수 있도록 하였다.(이 부분은 이후 학습 진행상황에 따라 변경 가능)

코드 설명

얼굴인식 모델 불러오기

훈련된 가중치와 네트워크 구성을 가지고 있는 파일을 읽어와서 facenet 이라는 객체 생성

8LBMI2.h5라는 모델을 로드하여 model에 저장

facenet = cv2.dnn.readNet('models/deploy.prototxt', 'models/res10_300x300_ssd_iter_140000.caffemodel')
model = load_model('8LBMI2.h5')

웹캠에서 영상 가져오기

cap = cv2.VideoCapture(0)

만약 웹캠이 정상적으로 동작한다면 아래 코드를 실행시킨다

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

추론을 진행하기위해 입력 영상을 블롭객체로 생성후 입력 설정 및 추론

    blob = cv2.dnn.blobFromImage(img, scalefactor=1., size=(405, 405), mean=(104., 177., 123.))
    facenet.setInput(blob)
    dets = facenet.forward()

이후 코드는 영상출력파트

스크린샷 2022-10-18 오후 9 10 42

코드

import os
import random
import time

import cv2
import numpy as np
from keras.models import load_model


def rename_file(dist_lable: str):
    count = 0
    file_list = os.listdir("croppedData")
    for i in range(file_list.__len__()):
        if file_list[i].endswith(".jpg"):

            src = "croppedData/" + file_list[i]
            dst = "croppedData/" + dist_lable + count.__str__() + ".jpg"
            os.rename(src, dst)
            print(src + " rename to " + dst)
            count += 1

rename_file('temp')

facenet = cv2.dnn.readNet('models/deploy.prototxt', 'models/res10_300x300_ssd_iter_140000.caffemodel')
model = load_model('8LBMI2.h5')

cap = cv2.VideoCapture(0)
i = 0

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

    h, w = img.shape[:2]

    blob = cv2.dnn.blobFromImage(img, scalefactor=1., size=(405, 405), mean=(104., 177., 123.))
    facenet.setInput(blob)
    dets = facenet.forward()

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

        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 = img[y1:y2, x1:x2]
        face = face/256

        if (x2 >= w or y2 >= h):
            continue
        if (x1<=0 or y1<=0):
            continue

        face_input = cv2.resize(face,(200, 200))
        face_input = np.expand_dims(face_input, axis=0)
        face_input = np.array(face_input)

        modelpredict = model.predict(face_input)
        mask=modelpredict[0][0]
        nomask=modelpredict[0][1]
        color = (255, 255, 255)

        file_list = os.listdir("croppedData")

        cropped_data_path = "croppedData/temp" + random.randrange(0, 999999).__str__() + ".jpg"
        height_dist = (y2-y1)//2
        crop = img[y1: y2-height_dist, x1: x2]
        try:
            cv2.imwrite(cropped_data_path, crop)
        except Exception as e:
            print(e)

        cv2.rectangle(img, pt1=(x1, y1), pt2=(x2, y2), thickness=2, color=color, lineType=cv2.LINE_AA)

    cv2.imshow('masktest',img)

    key = cv2.waitKey(1)

rename_file('crop')

Labelling.py

코드 개요

CreateData.py에서 생성한 이미지를 분류하기 위한 프로그램

Tkinter를 이용하여 GUI로, 버튼을 이용하여 분류할 수 있다.

스크린샷 2022-10-18 오후 8 30 51

코드 설명

openCV를 이용하여 이미지를 받아온 후 Tkinter에 출력하기

def showImage(src:str):
    try:
        img = cv2.imread(src)
        img = resizeImg(img)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = Image.fromarray(img)
        imgtk = ImageTk.PhotoImage(image=img)
        label = tk.Label(win, image=imgtk)
        label.image = imgtk
        label.grid(row=0, column=0, columnspan=3)
    except:
        for i in range(file_list.__len__()):
            if file_list[i] == src:
                del file_list[i]
                break
        showImage(file_list[0])

OnButton ClickListener on 버튼을 누르면 사진을 on 디렉토리로 이동

def onClick_onButton():
    global on_count
    global src
    os.rename(src, "on/on" + on_count.__str__() + ".jpg")
    on_count += 1
    try:
        for i in range(file_list.__len__()):
            if file_list[i].endswith(".jpg"):
                src = "croppedData/" + file_list[i]
                del file_list[i]
                break
    except IndexError:
        exit()
    showImage(src)

OffButton ClickListener off 버튼을 누르면 사진을 off 디렉토리로 이동시킨다.

def onClick_offButton():
    global off_count
    global src
    os.rename(src, "off/off" + off_count.__str__() + ".jpg")
    off_count += 1
    try:
        for i in range(file_list.__len__()):
            if file_list[i].endswith(".jpg"):
                src = "croppedData/" + file_list[i]
                del file_list[i]
                break
    except IndexError:
        exit()
    showImage(src)

DeleteButton ClickListener Delete 버튼을 누르면 해당 사진을 삭제한다.

def onClick_delete(): global src os.remove(src) try: for i in range(file_list.len()): if file_list[i].endswith(".jpg"): src = "croppedData/" + file_list[i] del file_list[i] break except IndexError: exit() showImage(src)

전체코드

import tkinter as tk
from PIL import ImageTk, Image
import cv2
import os

win = tk.Tk()
win.title("Labeler")
w = 250
win.geometry("")


def showImage(src:str):
    try:
        img = cv2.imread(src)
        img = resizeImg(img)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = Image.fromarray(img)
        imgtk = ImageTk.PhotoImage(image=img)
        label = tk.Label(win, image=imgtk)
        label.image = imgtk
        label.grid(row=0, column=0, columnspan=3)
    except:
        for i in range(file_list.__len__()):
            if file_list[i] == src:
                del file_list[i]
                break
        showImage(file_list[0])


def countJpgFiles(src:str):
    file_list = os.listdir(src)
    count = 0
    for i in range(file_list.__len__()):
        if file_list[i].endswith(".jpg"):
            src = "croppedData/" + file_list[i]
            count += 1
    return count


def resizeImg(src:str):
    try:
        h, w = src.shape[:2]
        dist = cv2.resize(src, (250, int(h*250/w)), cv2.INTER_AREA)
    except AttributeError:
        exit()
    return dist

file_list = os.listdir("croppedData")
file_list.sort()
file_list.sort(key=len)
countIndex = 0
on_count = countJpgFiles("on")
off_count = countJpgFiles("off")

for i in range(file_list.__len__()):
    if file_list[i].endswith(".jpg"):
        global src
        src = "croppedData/" + file_list[i]
        del file_list[i]
        break

    else:
        del file_list[i]

showImage(src)

def onClick_onButton():
    global on_count
    global src
    os.rename(src, "on/on" + on_count.__str__() + ".jpg")
    on_count += 1
    try:
        for i in range(file_list.__len__()):
            if file_list[i].endswith(".jpg"):
                src = "croppedData/" + file_list[i]
                del file_list[i]
                break
    except IndexError:
        exit()
    showImage(src)


def onClick_offButton():
    global off_count
    global src
    os.rename(src, "off/off" + off_count.__str__() + ".jpg")
    off_count += 1
    try:
        for i in range(file_list.__len__()):
            if file_list[i].endswith(".jpg"):
                src = "croppedData/" + file_list[i]
                del file_list[i]
                break
    except IndexError:
        exit()
    showImage(src)


def onClick_delete():
    global src
    os.remove(src)
    try:
        for i in range(file_list.__len__()):
            if file_list[i].endswith(".jpg"):
                src = "croppedData/" + file_list[i]
                del file_list[i]
                break
    except IndexError:
        exit()
    showImage(src)


on_button = tk.Button(win, text="ON", command=onClick_onButton)
on_button.config(width=5, height= 2)
on_button.grid(row=1, column=0)

off_button = tk.Button(win, text="OFF", command=onClick_offButton)
off_button.config(width=5, height= 2)
off_button.grid(row=1, column=1)

del_button = tk.Button(win, text="Delete", command=onClick_delete)
del_button.config(width=5, height= 2)
del_button.grid(row=1, column=2)
win.mainloop()

CreateModel.py

코드 개요

CreateModel.py 에서는 앞에서 라벨링을 완료한 데이터를 학습하여 모델을 생성하는 코드

학습기법은 CNN을 사용하여 학습을 진행한다.

스크린샷 2022-10-18 오후 9 32 28 스크린샷 2022-10-18 오후 9 32 21

CNN(Convolutional Neural Networks)란?

CNN(Convolutional Neural Networks)이란 우리말로 번역하면 합성곱 신경망으로, 이름 그대로 Convolution(합성곱) 전처리를 이용하여 학습을 하는 머신러닝 기법이다.

CNN은 딥러닝에서 이미지나 영상 데이터를 처리할 때 쓰인다.


신경망은 기본적으로 Layer를 연결하는 방식으로 이루어지는데, CNN에서는 Convolution layer와 pooling layer가 사용된다.

img


Convolution layer는 이미지의 특징을 판단하는 layer이다. 컴퓨터는 이미지에서 물체를 판별할 때, 다른 위치에 있는 같은 물체를 다른 물체라고 판단하는데, 이러한 문제를 해결해 주는것이 Convolution layer이다.

R1280x0-2

R1280x0


pooling layer는 특징을 강화하는 layer이다.

pooling layer는 특정값을 제외하고 나머지 값들은 버린다고 생각하면 된다.

Pooling layer의 종류에는 max pooling, average pooling, overlapping Pooling이 있다.

1*8DRW7Uw6lHfAdPdXrHiY9w

코드 설명

이미지를 불러와 np.array에 저장하는 코드

for idex, categorie in enumerate(categories):
    label = [0 for i in range(num_classes)]
    label[idex] = 1
    image_dir = groups_folder_path+'/'+ categorie + '/'

    for top, dir, f in os.walk(image_dir):
        for filename in f:
            if filename.endswith('.jpg'):
                img = cv2.imread(image_dir + filename)
                img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
                img = cv2.resize(img, (192,128))
                ls_x.append(img / 256)
                ls_y.append(label)

X_train = np.array(ls_x)
Y_train = np.array(ls_y)

모델 구성

model = Sequential()
model.add(Conv2D(32,(3,3), activation='relu',
                        input_shape=(128, 192, 1)))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Convolution2D(32, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Convolution2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Convolution2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Convolution2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

#Classifier
model.add(Flatten())
model.add(Dense(200, activation = 'relu'))
model.add(Dense(2,activation = 'softmax'))

학습 시작 및 모델 저장

model.compile(loss='binary_crossentropy', optimizer='Adam', metrics=['accuracy'])
hist=model.fit(X_train, Y_train, batch_size=32, epochs=200)

model.save('ONOFFMODEL.h5')

전체 코드

from keras.models import Sequential
from keras.layers import Dropout, Activation, Dense
from keras.layers import Flatten, Convolution2D, MaxPooling2D, Conv2D
from keras.models import load_model
import os, re, glob
import cv2
import numpy as np

groups_folder_path = 'CreateTrainingData'
categories = ['on','off']

num_classes = categories.__len__()

ls_x = []
ls_y = []

for idex, categorie in enumerate(categories):
    label = [0 for i in range(num_classes)]
    label[idex] = 1
    image_dir = groups_folder_path+'/'+ categorie + '/'

    for top, dir, f in os.walk(image_dir):
        for filename in f:
            if filename.endswith('.jpg'):
                img = cv2.imread(image_dir + filename)
                img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
                img = cv2.resize(img, (192,128))
                ls_x.append(img / 256)
                ls_y.append(label)

X_train = np.array(ls_x)
Y_train = np.array(ls_y)

print(X_train.shape)
print(X_train.shape[1:])

model = Sequential()
model.add(Conv2D(32,(3,3), activation='relu',
                        input_shape=(128, 192, 1)))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Convolution2D(32, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Convolution2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Convolution2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Convolution2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

#Classifier
model.add(Flatten())
model.add(Dense(200, activation = 'relu'))
model.add(Dense(2,activation = 'softmax'))

model.summary()

model.compile(loss='binary_crossentropy', optimizer='Adam', metrics=['accuracy'])
hist=model.fit(X_train, Y_train, batch_size=32, epochs=200)

model.save('ONOFFMODEL.h5')