들어가며
미니프로젝트 발표가 이뤄지는 날이다. 주제선정에 많은 시간을 투자해서 시간이 부족했는데 그만큼 조원들 모두 치열하게 준비한 것 같다. 우여곡절 끝에 완성한 프로젝트가 어떤 모습인지 자세하게 알아보자.
졸음 운전 방지 알림 서비스
이미지 창을 띄우는 함수이다. 학습 이미지와 훈련 이미지들이 어떻게 구성이 되어 있는지 확인하는 용도로 사용된다.
import cv2
import numpy as np
from matplotlib import pyplot as plt
import keras
# 이미지 그래프 그리는 함수
def plot_images(n_row:int, n_col:int, image:list[np.array]) -> None:
fig = plt.figure()
(fig, ax) = plt.subplots(n_row, n_col, figsize=(n_col, n_row))
for i in range(n_row):
for j in range(n_col):
if n_row <= 1:
axis = ax[j]
else:
axis = ax[i,j]
axis.get_xaxis().set_visible(False)
axis.get_yaxis().set_visible(False)
axis.imshow(image[i * n_col + j])
plt.show()
return None
눈이 감긴 이미지 1932개를 불러온다. 이미지의 크기를 `resize`를 통해 재정의 해주고, open-cv가 이미지를 읽을 때 BGR로 읽기 때문에 `cvtColor`를 사용하여 다시 RGB로 변환해준다. 그리고 변환이 끝난 이미지를 `sleep_images`리스트에 추가해준다. 샘플 15개의 이미지를 확인해보면 다음과 같다.
# 졸음 이미지 파일 가져오기
path1 = './ImprovementSet/Closed/'
sleep_images = list()
for image in os.listdir(path1):
img = cv2.imread(path1+image)
img = cv2.resize(img, (64, 64))
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGR을 RGB로 변환
sleep_images.append(img)
# 졸음 이미지 그래프 그리기
plot_images(n_row=3, n_col=5, image=sleep_images)
이번엔 눈이 떠져있는 이미지 2171개를 불러온다. 마찬가지로 이미지 사이즈 통일과 RGB변환을 해주고, `normal_images`리스트에 추가해준다. 불러온 이미지의 샘플 15개를 확인해보면 다음과 같다.
# 정상 이미지 불러오기
path2 = './ImprovementSet/Opened/'
normal_images = []
for image in os.listdir(path2):
img = cv2.imread(path2 + image)
img = cv2.resize(img, (64, 64))
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGR을 RGB로 변환
normal_images.append(img)
# 정상 이미지 그래프 그리기
plot_images(n_row=3, n_col=5, image=normal_images)
학습 시킬 데이터 `X`에는 졸음이미지와 정상이미지 데이터 모두를 할당해주고, 정답 데이터에는 졸음이미지와 정상이미지를 각각 1과 0으로 변환하여 저장해준다.
# 학습 시킬 데이터와 정답 데이터 만들기
X = sleep_images + normal_images # 졸음이미지+정상이미지 병합
y = [[1,0]] * len(sleep_images) + [[0,1]] * len(normal_images) # 원핫 인코딩 방식처럼
# numpy로 변환
X = np.array(X)
y = np.array(y)
print(y)
print(y.shape)
이미지들을 학습시킨 모델을 생성해준다. 모델은 CNN(Convolution Neural Network)를 사용한다. 은닉층에는 이미지 처리에 용이한 `Conv2D`와 `MaxPooling2D`를 사용하고, 3차원의 배열로 표현되는 이미지 데이터들을 1차원으로 변환해주기 위해 `Flatten`을 사용했다.
모델 컴파일 시 손실함수를 `categorical_crossentropy`를 사용함으로써 실제 클래스와 예측 확률 분포 간의 차이를 계산한다. 모델 평가지표 `metrics`는 `[accuracy]`로 1에 가까울 수록 높은 정확도를 표시해주고, 학습 횟수는 100번으로 지정했다. 학습이 끝난 모델은 `SLEEP_DETECTOR.keras`로 저장해주었다.
# CNN모델 만들기
model = keras.models.Sequential([
keras.Input(shape=(64, 64, 3)), # color이미지(64 x 64)
keras.layers.Conv2D(filters=32, kernel_size=(3, 3)),
keras.layers.MaxPooling2D(pool_size=(2, 2), strides=2),
keras.layers.Conv2D(filters=32, kernel_size=(3, 3)),
keras.layers.MaxPooling2D(pool_size=(2, 2), strides=2),
keras.layers.Conv2D(filters=32, kernel_size=(3, 3)),
keras.layers.MaxPooling2D(pool_size=(2, 2), strides=2),
keras.layers.Conv2D(filters=32, kernel_size=(3, 3)),
keras.layers.MaxPooling2D(pool_size=(2, 2), strides=2),
keras.layers.Flatten(),
keras.layers.Dense(units=64, activation='relu', name='LAYER1'),
keras.layers.Dense(units=32, activation='relu', name='LAYER2'),
keras.layers.Dense(units=2, activation='softmax', name='OUTPUT')
], name='FACE_CNN')
# 모델 학습이 끝났을 경우 주석 처리
model.summary()
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
# 1000번 학습된 CNN모델 생성
history = model.fit(x=X, y=y, epochs=100)
model.save('SLEEP_DETECTOR.keras')
이제 테스트로 넣을 이미지들을 불러온다. 이미지들은 10개를 사용했고 그 중 5개는 눈을 뜬 이미지, 나머지 5개는 눈을 감은 이미지이다.
# 성능 테스트 : 예제 파일을 읽어와서 이미지 테스트 하기
example_images = list()
path3 = './testSet/examples/'
for image in os.listdir(path3):
img = cv2.imread(path3 + image)
img = cv2.resize(img, (64, 64))
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGR을 RGB로 변환
example_images.append(img)
# example_images python 파일
example_images = np.array(example_images)
plot_images(n_row=2, n_col=5, image=example_images)
학습을 완료한 모델 `SLEEP_DETECTOR.keras`로 테스트 이미지들 중 졸음 이미지들만 추출할 차례이다. 예측 값이 0.5보다 높으면 즉, 1에 가깝다는 뜻이므로 졸음 이미지로 분류를 하고, 동시에 경고 메시지도 함께 출력하도록 했다. 아무래도 학습 이미지의 양이 많기도 하고, 비슷한 이미지들이 많기 때문에 매우 높은 정확도로 분류를 할 수 있었다.
# 학습완료한 모델 가져오기
model_2 = keras.models.load_model('SLEEP_DETECTOR.keras')
predict_images = model_2.predict(x=example_images)
print(np.round(predict_images))
# 2 x 5 이미지 그래프 그리기
(fig, ax) = plt.subplots(2, 5, figsize=(10, 4))
for i in range(2):
for j in range(5):
axis = ax[i, j]
axis.get_xaxis().set_visible(False)
axis.get_yaxis().set_visible(False)
if predict_images[i * 5 + j][0] > 0.5:
axis.imshow(example_images[i * 5 + j])
axis.text(0.5, 1.05, "warning!",
fontsize=10, color='red',
ha='center', va='bottom',
transform=axis.transAxes)
plt.show()
loss, accuracy = model_2.evaluate(X, y, verbose=0)
# Print the model's performance
print(f'Test Loss: {loss:.4f}')
print(f'Test Accuracy: {accuracy*100:.2f}%')
한계점 및 마무리
이미지들이 운전자 얼굴의 전반적인 모습은 아니어서 보다 다양한 이미지들까지 커버하기는 어렵다는 점이 아쉬웠다. 운전대를 잡은 사람들의 모습이 담긴 이미지들을 준비했더라면, 실제 상용화되고 있던 졸음운전 방지 시스템과 그래도 조금은 유사하게 구현할 수 있지 않았을까 싶다.
또한 직접 프로젝트를 진행하는 것이 이론수업을 통한 학습보다 뭔가 더 빠르게 흡수하는 느낌이 들기도 했다. 상용화된 기술을 간단하게나마 직접 구현을 해봄으로써, 더 피부에 와닿은 것 같다. 이를 계기로 사회에 도움이 될만한 아이디어들을 배운 기술로 유연하게 적용할 수 있도록 최종 프로젝트때 최선을 다해서 준비해야겠다.
'ABC부트캠프 테크노트' 카테고리의 다른 글
[28일차] ABC부트캠프 : NVIDIA 트랜스포머 기반 NLP 애플리케이션 구축 (0) | 2024.08.12 |
---|---|
[27일차] ABC부트캠프 : NVIDIA 딥러닝의 기초 (0) | 2024.08.09 |
[25일차] ABC부트캠프 : 미니 프로젝트 준비 (0) | 2024.08.07 |
[24일차] ABC부트캠프 : 딥러닝3 (0) | 2024.08.06 |
[23일차] ABC부트캠프 : 딥러닝2 (0) | 2024.08.06 |