DMS

2024. 4. 22. 20:35파이썬

  • python = 3.8.8
  • pip install torch==1.7.1+cu110 torchvision==0.8.2+cu110 -f https://download.pytorch.org/whl/torch_stable.html
  • pip install opencv-python==4.2.0.30
  • pip install tensorboardX==1.8
  • pip install scipy

 

1. 데이터셋 전처리(파일명에서 한글 삭제)

import os
import shutil

# 카테고리 및 해당 인덱스 정의
light = ['무광원', '정면광원', '좌측광원', '우측광원', '후면광원']  # 3
location = ['계기판', '네비게이션', '룸미러', '썬바이저', '정면', '좌사이드미러', '우사이드미러'] # 4
status = ['정상주시', '졸음재현', '하품재현', '통화재현']

# 원본 파일이 있는 루트 폴더와 복사할 폴더 정의
root_folder = "C:/Users/user/Desktop/DMS/045_G1"
change_folder = "C:/Users/user/Desktop/DMS/project_dms/testimg"

# 복사할 폴더가 없으면 생성
if not os.path.exists(change_folder):
    os.makedirs(change_folder)

# 루트 폴더의 파일 목록 가져오기
file_list = os.listdir(root_folder)

# 루트 폴더의 각 파일에 대해 반복
for file in file_list:
    # 파일 이름과 확장자 분리
    name, ext = file.split('.')
    # 파일 이름을 밑줄을 기준으로 구성 요소로 분할
    p = name.split("_")
    # 카테고리를 해당 인덱스로 변환
    p[3] = str(light.index(p[3]))
    p[4] = str(location.index(p[4]))
    p[5] = str(status.index(p[5]))
    # 구성 요소를 다시 결합하고 확장자를 추가
    new_name = "_".join(p) + '.' + ext
    # 파일을 루트 폴더에서 대상 폴더로 새 이름으로 복사
    shutil.copyfile(os.path.join(root_folder, file), os.path.join(change_folder, new_name))

 

2. 얼굴 비율 함수 정의

from scipy.spatial import distance as dist

def ear(pts):
    # 눈의 가로 길이를 측정하여 EAR(Eye Aspect Ratio) 계산
    A = dist.euclidean(pts[1],pts[7])  # 왼쪽 눈 가로 길이
    B = dist.euclidean(pts[3],pts[5])  # 오른쪽 눈 가로 길이
    C = dist.euclidean(pts[0],pts[4])  # 눈 세로 길이
    return (A+B) / (2.0*C)
   
def head_rate(pts):
    # 머리 비율 계산
    A = dist.euclidean(pts[51],pts[54])  # 눈과 귀 사이 거리
    B = dist.euclidean(pts[54],pts[16])  # 머리의 너비
    return A/B
    
def stir_rate(pts):
    # 스트링 비율 계산
    A = dist.euclidean(pts[4],pts[54])  # 눈과 입 사이 거리
    B = dist.euclidean(pts[28],pts[54])  # 코와 입 사이 거리
    return A/B
    
def face_metric(pts):
    # 얼굴 메트릭 계산
    left_EAR = ear(pts[60:68])  # 왼쪽 눈 EAR
    right_EAR = ear(pts[68:76])  # 오른쪽 눈 EAR
    EAR_avg = (left_EAR + right_EAR)/2  # 두 눈의 평균 EAR
    HR = head_rate(pts)  # 머리 비율
    SR = stir_rate(pts)  # 스트링 비율
    return EAR_avg, HR, SR

 

2.1 테스트

import cv2
import sys
import matplotlib.pyplot as plt
sys.path.append('fd')
sys.path.append('fl')

import face_detection1
import face_landmark_detection 
landmark_detection_weight = "C:/Users/user/Desktop/DMS/project_dms/fl/checkpoint_epoch_313.pth.tar"
face_detection_weight = "C:/Users/user/Desktop/DMS/project_dms/fd/weights/mobilenet0.25_epoch_240.pth"
test_img_path = "C:/Users/user/Desktop/DMS/123.jpg"
test_img = cv2.imread(test_img_path)
dets = face_detection1.detection(test_img, face_detection_weight)
points = face_landmark_detection.landmark_detection(test_img, dets[0], landmark_detection_weight)
EAR, HR, SR = face_metric(points)
print(f"EAR : {EAR:.3f}, HR : {HR:.3f}, SR :  {SR:.3f}")
plt.imshow(cv2.cvtColor(test_img, cv2.COLOR_BGR2RGB))
plt.show()

(512, 567, 3)

Model Loaded!

EAR : 0.284, HR : 0.554, SR : 0.982

 

3. Inference

# FD를 위한 라이브러리
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
import time
import torch
import sys
from scipy.spatial import distance as dist
import os

sys.path.append('fd')
sys.path.append('fl')

import face_detection1
import face_landmark_detection

 

  • EAR 경계값은 : (open-close)^2 / 2 +close보다 작을시 문제
  • Head rate 경계값은 : 0.5 ~ 0.95 사이면 문제
  • 좌우 경계값은 : 0.5 ~ 2 사이면 정상
landmark_detection_weight = 'C:/Users/user/Desktop/DMS/project_dms/fl/checkpoint_epoch_313.pth.tar'
face_detection_weight = "C:/Users/user/Desktop/DMS/project_dms/fd/weights/mobilenet0.25_epoch_240.pth"
time_th = 0.25
vis_thresh = 0.6
EAR_Thresh = 0.2        # 이것보다 작으면 문제
HR_Thresh = (0.5, 0.95) # 이구간만 문제
SR_Thresh = (0.5, 2)    # 이사이만 정상
root_folder = 'C:/Users/user/Desktop/DMS/project_dms/testimg'
result_folder = 'result'

# 결과를 저장할 폴더 생성
if not os.path.exists(result_folder):
    os.makedirs(result_folder)

# 변수 초기화
ear_status = False
head_status = False
face_detection_n = False
ear_status_warning = False
head_status_warning = False
face_detection_n_warning = False
fd_time = 0
head_time = 0
ear_time = 0

file_list = os.listdir(root_folder)
for idx, file in enumerate(file_list):

    # test 이미지 읽어오기
    img = cv2.imread(os.path.join(root_folder, file))
    
    # 얼굴 감지
    dets = face_detection1.detection(img, face_detection_weight)
    cr = time.time()
    if len(dets) == 0 and dets[0][4] < vis_thresh: # 정면 미주시
        if face_detection_n == False:
            fd_time = cr
            face_detection_n = True
        if face_detection_n == True and cr - fd_time > time_th:
            face_detection_n_warning = True
            if face_detection_n_warning:
                cv2.rectangle(img, (0,0), (300, 100), (255,0,0), -1, cv2.LINE_AA)  # 채워진 사각형
                cv2.putText(img, '경고!', (5,70), cv2.FONT_HERSHEY_DUPLEX, 2, (255,255,255), thickness=3, lineType=cv2.LINE_AA)
        cv2.imwrite(f'result/m_{idx:04d}.png', cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
        continue

    face_detection_n = False
    face_detection_n_warning = False

    # 얼굴 특징점 감지 및 EAR, HR, SR 계산
    points = face_landmark_detection.landmark_detection(img, dets[0], landmark_detection_weight)
    EAR, HR, SR = face_metric(points)

    # EAR 이상 확인
    if EAR < EAR_Thresh:
        cr = time.time()
        if ear_status == False:
            ear_time = cr
            ear_status = True
        if ear_status == True and cr - ear_time > time_th:
            ear_status_warning = True
    else:
        ear_status = False
        ear_status_warning = False

    # 머리 이상 확인
    if (HR_Thresh[0] < HR and HR < HR_Thresh[1]) or (SR < SR_Thresh[0] and SR_Thresh[1] < SR):
        cr = time.time()
        if head_status == False:
            head_time = cr
            head_status = True
        if head_status == True and cr - head_time > time_th:
            head_status_warning = True
    else:
        head_status = False
        head_status_warning = False

    # 졸음이 감지되면 경고 출력
    if ear_status_warning or head_status_warning:
        cv2.rectangle(img, (0,0), (300, 100), (255,0,0), -1, cv2.LINE_AA)  # 채워진 사각형
        cv2.putText(img, '경고!', (5,70), cv2.FONT_HERSHEY_DUPLEX, 2, (255,255,255), thickness=3, lineType=cv2.LINE_AA)

    # 이미지에 EAR, SR, HR 값을 표시
    cv2.putText(img, f'EAR:{EAR:.2f} ', (600,100), cv2.FONT_HERSHEY_DUPLEX, 1, (0,0,255), thickness=3, lineType=cv2.LINE_AA)
    cv2.putText(img, f'SR:{SR:.2f}', (600,150), cv2.FONT_HERSHEY_DUPLEX, 1, (0,0,255), thickness=3, lineType=cv2.LINE_AA)
    cv2.putText(img, f'HR:{HR:.2f}', (600,200), cv2.FONT_HERSHEY_DUPLEX, 1, (0,0,255), thickness=3, lineType=cv2.LINE_AA)
    cv2.imwrite(f'result/m_{idx:04d}.png', cv2.cvtColor(img,cv2.COLOR_BGR2RGB))

 

4. 결과