티스토리 뷰

안녕하세요. 이번에는 제가 고등학교 생기부를 만들고, 특기자 전형을 준비하면서 직접 작성한 보고서입니다. 해당 내용을 참고하시든 사용하시든 (이미지 제외) 상관없으니 자유롭게 사용해 주세요.

 

개발 동기

'StudyHelper.py'는 실시간으로 웹캠에 비친 사용자의 얼굴에서 홍채를 인식해 집중도 측정, 졸음 방지 기능을 제공하여 학습에 도움을 주는 프로그램입니다. 모 대학 교수님의 뇌파를 이용한 집중도 향상 AI 시스템의 연구성과를 보고 AI 기술이 학습에 도움을 준다는 점이 인상 깊었습니다. "뇌파가 아닌 다른 방법으로 집중도를 측정할 수 없을까?" 하는 호기심에 탐구하였고, 집중 여부에 따라 홍채의 움직임에 변화를 준다는 사실을 알게 되었습니다. 이러한 방법을 이용해 학습에 도움을 주는 프로그램을 개발했습니다.

 

개발과정

얼굴 인식 (+ 인식률을 높이는 방법)

얼굴을 인식하는 방법에 대해 여러 가지 고민했고, 고민 끝에 눈까지 인식하여 눈 주변 좌표를 가져오고 조작하기에 적합한 학습모델인 'dlib 안면 랜드 마킹 라이브러리'를 이용했습니다. 학습모델을 통해 성공적으로 얼굴에 특징점을 나타낼 수 있었지만, 얼굴이 옆으로 기울어지게 되면 인식을 거의 하지 못하는 문제를 발견했습니다. 이를 해결하고자 강구하던 끝에 얼굴의 움직임에 맞추어 화면을 회전하자는 생각을 떠올렸고, 이를 실행시켰습니다. 

 

rot_mat = []
theta = 0

# while True:
ret, frame = cap.read() # 캠의 이미지를 받는 부분
if rot_mat != []: # 최초 실행의 경우 회전을 하지 않도록 설정
    frame = cv2.warpAffine(frame, rot_mat, frame.shape[1::-1], flags=cv2.INTER_LINEAR,borderValue=(255,255,255)) # 지난 사진에서 받아온 어파인 변환 행렬을 받아와 이미지를 회전한다.

# 얼굴 특이점 인식 부분(생략됨)
tan_theta = (land_list[30][0]-land_list[27][0])/(land_list[30][1]-land_list[27][1])
theta += np.arctan(tan_theta) # 이전에 기울어진 정도의 각도 + 현재 구한 각도
angle =-(theta *180)/math.pi # 호도법을 일반각으로 변환시키는 작업

image_center = tuple(np.array(deframe.shape[1::-1]) /2) 
rot_mat = cv2.getRotationMatrix2D(image_center, angle, 1.0) 
# 회전 중심좌표(image_center)와 회전각도(angle)를 받아와 2x3의 어파인 변환 행렬을 출력
# 어파인 변환 행렬은 이후 warpAffine() 함수에서 인자로 넣어 이미지를 회전시키기 위해 구하는 것

 

현재 이미지에서 얼굴의 기울어진 정도만큼 회전하는 것이 아닌, 왜 지난 이미지에서 구한 각도만큼 회전하는 것일지 의문을 가질 수도 있습니다. 얼굴의 인식률을 높이기 위해 이미지를 회전시키는 것이기 때문에 얼굴을 인식하기 전 이미지를 회전해야만 합니다. 하지만 회전하기 위해 기울어진 정도를 구하기 위해서는 얼굴을 인식해야 하는 모순이 발생하므로, 얼굴을 두 번 인식하는 것은 비효율적이라고 판단하고 매우 작은 오차가 생길지언정 지난 이미지에서 가져온 어파인 변환 행렬을 통해 이미지를 회전시켰습니다.

 

홍채인식

홍채를 정확히 인식하는 방법을 고민했고, 홍채가 주변보다 어둡다는 특성을 이용해 thresholding을 통해 검출한 외곽선이 눈의 중심과 가장 가까운 영역을 가져오자는 생각을 했습니다. 그래서 저는 먼저 이미지의 얼굴 영역만을 잘라서 가져왔습니다. 가져온 얼굴 이미지를 thresholding을 하기 위해 먼저 cvtColor() 함수를 이용해 GrayScale로 변환하고, 노이즈를 줄이고자 Gaussian 필터를 씌웁니다. 작업을 마친 이미지를 thresholding을 적용하고, findConours를 통해 외곽선을 찾은 다음 labda x:-contourArea(x)를 통해 넓이가 큰 영역부터 내림차순으로 정렬합니다. 이 배열을 반복문을 돌려 눈의 중심과 가장 가까운 외곽선을 검출하여 홍채를 인식합니다.

 

눈 감는 횟수, 졸음 감지

눈을 감는 것을 측정하기 위해 저는 눈 주변에 새긴 랜드마크를 이용했습니다. 실시간으로 눈의 위쪽과 아래쪽 마크의 좌표를 받아온 후 두 마크 간의 거리가 가까워질 때 눈을 감는 것으로 간주했습니다. 그리고 눈을 다시 뜨기 전에는 눈을 감는 횟수가 카운트되지 않도록 변수를 조작했습니다. 또한, 조작한 변수를 이용해 일정 시간 이상 동안 다시 눈을 뜨지 않으면 졸고 있다고 판단하고 알람 소리를 울리게 했습니다.

 

눈의 움직임을 통한 집중도 측정

홍채의 움직임을 분석하기에 적당한 지표를 찾는 과정에서 확률과 통계 시간에 배운 표준편차를 이용하면 눈이 좌우로 얼마나 움직였는지 나타내기에 적당하다고 생각했습니다. 표준편차를 구하고자, 먼저 오른쪽 눈을 기준 잡고 눈의 중심좌표를 가져옵니다. 그 후 1초마다 홍채의 위치와 눈의 중심좌표까지의 거리를 벡터값으로 나타냅니다(오른쪽 +, 왼쪽 -). 이 값을 넘파이 배열에 넣어 std() 함수를 통해 표준편차를 구합니다. 실제로 집중하고 있을 때와 집중하고 있지 않을 때 홍채의 움직임에 대한 표준편차의 값을 알아보고자 학생 100명을 대상으로 집중집단과 비집중집단으로 나눠 프로그램을 배포했고, 초 단위로 표준편차가 어떻게 변화하는지 데이터를 쌓을 수 있었습니다. 이 데이터를 바탕으로 집중도를 구할 수 있었습니다.

 

개발을 마치고…

완성한 프로그램을 교내 공모전에 실어 가장 많은 인기를 받을 수 있었고, 파이썬을 공부하고 있는 친구들에게 도움을 주고자 소스 전문에 주석처리를 하여 오픈소스 형식으로 배포하였습니다. 이번 활동은 dlib 라이브러리를 이용했기 때문에 원하는 위치에 랜드마크를 새길 수 없다는 한계가 있었습니다. Human skeleton detection처럼 원하는 위치에 랜드 마킹이 가능한 모델이나 YOLO와 같은 객체 탐지 모델을 응용하면 좋은 결과물을 만들 수 있다고 생각했지만, 더욱 최적화된 모델을 가지고 싶다는 생각에 모델을 직접 구축하고 커스텀하는 방식을 주제로 공부할 예정입니다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/06   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함