🔗 일부 입증 자료 링크가 정상 작동하지 않아 한 페이지에 정리하였습니다. 크롬 환경에서 정상조회가 가능합니다. [입증 자료 링크 목록보기] 🔗
🔗 일부 입증 자료 링크가 정상 작동하지 않아 한 페이지에 정리하였습니다. 크롬 환경에서 정상조회가 가능합니다. [입증 자료 링크 목록보기] 🔗
노인/지체장애인을 위한 아이트래킹 시스템
WIPO National Award for Inventors
Kiyo4i Gold Prize
개발 내용
연구동기
장애인이나 노인이 몸을 움직이기 힘들어 키보드 마우스 사용이 어려워 전자기기 사용이 어렵고 이로 인해 정보격차가 발생하고 있다는 기사를 봄. 어떻게 하면 소외되는 이 없이 정보균형을 이룰 수 있을지 생각하다 본 발명을 고안하게 됨.
개발 내용
본 작품을 통해 고가의 아이트래킹 장비를 구매하지 않고도 내장된 웹캠으로 사용자의 시선을 추적하여 마우스를움직이고, 눈 깜빡임으로 클릭, 드래그등을 수행할 수 있는 작품이다. MediaPipe와 OpenCV를 활용하여 아이트래킹 기술을 구현 하였으며 이에 맞는 전용 사이트와 함께 사용하면 취약계층의 위급상황을 보다 안전하게 대비할 수 있을 것이다.
발전단계
페이스 트래킹
분류기를 통해 사용자의 코 위치를 파악하고 이를 통해 얼굴의 위치를 파악하는 시스템으로 코 주변의 음영에 따라 포인트가 튀는 현상이 발생함. 또한 고개를 상하좌우로 심하게 흔들어야 하는 단점이 있었음.
이를 해결하기 위해 2. AI 아이트래킹을 구축함.
*아래는 1차 시연 영상
import cv2
import numpy as np
from pynput.mouse import Controller
# 마우스 컨트롤러 생성
mouse = Controller()
# 웹캠을 통해 영상을 받아옴
webcam = cv2.VideoCapture(1)
# 얼굴을 검출하기 위한 분류기 로드
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
while True:
# 웹캠에서 프레임 읽기
ret, frame = webcam.read()
if ret:
# 화면 반전
frame = cv2.flip(frame, 1)
# 그레이 스케일로 변환
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 얼굴 검출
faces = face_cascade.detectMultiScale(gray, 1.1, 4)
# 검출된 얼굴에 대해 반복
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
# 얼굴 영역의 중심 좌표 계산
face_center_x = x + w // 2
face_center_y = y + h // 2
# 마우스 커서 이동
mouse.position = (face_center_x, face_center_y)
# 화면에 출력
cv2.imshow('Face Tracking', frame)
# 'q'를 누르면 종료
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 웹캠 해제 및 창 닫기
webcam.release()
cv2.destroyAllWindows()
2. AI 아이트래킹
1.의 문제점을 해결하기 위해 MediaPipe, OpenCV를 활용하여 얼굴의 눈, 코 입의 다양한 부분을 인식하고, 이들의 좌표값의 상호작용을 계산하여 트래킹을 할 수 있도록 함. 또한 여러 포인트를 인식하기 때문에 눈의 복수 포인트의 좌표를 계산하여 깜박임을 감지하고 클릭 기능을 추가함.
import cv2
import mediapipe as mp
import pyautogui
import time
from PIL import Image, ImageDraw, ImageFont
import numpy as np
# 한글 출력을 위한 함수
def draw_korean_text(image, text, position, font_path, font_size, color):
image_pil = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(image_pil)
font = ImageFont.truetype(font_path, font_size)
text_bbox = draw.textbbox((0, 0), text, font=font)
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]
text_x = position[0] - text_width // 2
text_y = position[1] - text_height // 2
draw.text((text_x, text_y), text, font=font, fill=color)
return cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR)
# mediapipe 초기 설정
mp_face = mp.solutions.face_mesh
face_mesh = mp_face.FaceMesh(min_detection_confidence=0.5, min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utils
cap = cv2.VideoCapture(1)
screen_width, screen_height = pyautogui.size()
prev_x, prev_y = 0, 0
# 눈 감지 관련 변수
eyes_closed = False
blink_count = 0
time_first_blink = 0
font_path = "C:/Windows/Fonts/malgun.ttf"
# 눈, 코, 입 부분 나타내는(강조하는)코드
landmark_indices = {
"left_eye": [33, 133],
"right_eye": [362, 263],
"nose": [1],
"mouth": [13, 14]
}
try:
while cap.isOpened():
success, image = cap.read()
if not success:
print("Ignoring empty camera frame.")
continue
image = cv2.flip(image, 1) # 웹캠 반전
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = face_mesh.process(image_rgb)
if results.multi_face_landmarks:
for landmarks in results.multi_face_landmarks:
left_eye_top = (landmarks.landmark[159].x, landmarks.landmark[159].y)
left_eye_bottom = (landmarks.landmark[145].x, landmarks.landmark[145].y)
right_eye_top = (landmarks.landmark[386].x, landmarks.landmark[386].y)
right_eye_bottom = (landmarks.landmark[374].x, landmarks.landmark[374].y)
left_eye_distance = abs(left_eye_top[1] - left_eye_bottom[1])
right_eye_distance = abs(right_eye_top[1] - right_eye_bottom[1])
if left_eye_distance < 0.02 and right_eye_distance < 0.02:
if not eyes_closed:
eyes_closed = True
time_eyes_closed = time.time()
blink_count += 1
if blink_count == 1:
time_first_blink = time.time()
else:
eyes_closed = False
if blink_count == 2 and time.time() - time_first_blink < 1.0: # 두 번 깜빡임
pyautogui.doubleClick()
image = draw_korean_text(image, "연속 눈 깜빡임이 감지되었습니다 [클릭]", (image.shape[1] // 2, image.shape[0] - 50), font_path, 30, (0, 255, 0))
blink_count = 0
elif time.time() - time_first_blink >= 1.0:
blink_count = 0
x = int(landmarks.landmark[4].x * image.shape[1])
y = int(landmarks.landmark[4].y * image.shape[0])
if prev_x and prev_y:
diff_x = x - prev_x
diff_y = y - prev_y
pyautogui.move(diff_x * 4.5, diff_y * 4.5) # 마우스 민감도 증가
image = draw_korean_text(image, "AI-Angel EyeTracking 작동중...", (image.shape[1] // 2, image.shape[0] - 100), font_path, 30, (0, 0, 255))
prev_x, prev_y = x, y
# 얼굴의 특정 부분만 표시
for part, indices in landmark_indices.items():
for idx in indices:
lm = landmarks.landmark[idx]
point = (int(lm.x * image.shape[1]), int(lm.y * image.shape[0]))
cv2.circle(image, point, 2, (0, 255, 0), -1)
cv2.imshow('AI Face Mouse Control', image)
if cv2.waitKey(5) & 0xFF == 27:
break
finally:
cap.release()
cv2.destroyAllWindows()
3. AI 트래킹 + 기능추가
2. 기능애 고개를 위 아래로 저으면 스크롤을 할 수 있는 기능을 추가하였다. 이를 통해 전자기기를 손을 쓰지 않고 트래킹 기능을 통해 완벽하게 수행할 수 있게 되었다.
import cv2
import mediapipe as mp
import pyautogui
import time
from PIL import Image, ImageDraw, ImageFont
import numpy as np
# 한글 출력을 위한 함수
def draw_korean_text(image, text, position, font_path, font_size, color):
image_pil = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(image_pil)
font = ImageFont.truetype(font_path, font_size)
text_bbox = draw.textbbox((0, 0), text, font=font)
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]
text_x = position[0] - text_width // 2
text_y = position[1] - text_height // 2
draw.text((text_x, text_y), text, font=font, fill=color)
return cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR)
# mediapipe 초기 설정
mp_face = mp.solutions.face_mesh
face_mesh = mp_face.FaceMesh(min_detection_confidence=0.5, min_tracking_confidence=0.5)
mp_drawing = mp.solutions.drawing_utils
cap = cv2.VideoCapture(1)
screen_width, screen_height = pyautogui.size()
prev_x, prev_y = 0, 0
# 눈 감지 관련 변수
eyes_closed = False
blink_count = 0
time_first_blink = 0
time_eyes_closed = 0
scrolling = False
font_path = "C:/Windows/Fonts/malgun.ttf"
# 눈, 코, 입 부분 나타내는(강조하는)코드
landmark_indices = {
"left_eye": [33, 133],
"right_eye": [362, 263],
"nose": [1],
"mouth": [13, 14]
}
try:
while cap.isOpened():
success, image = cap.read()
if not success:
print("Ignoring empty camera frame.")
continue
image = cv2.flip(image, 1) # 웹캠 반전
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = face_mesh.process(image_rgb)
if results.multi_face_landmarks:
for landmarks in results.multi_face_landmarks:
left_eye_top = (landmarks.landmark[159].x, landmarks.landmark[159].y)
left_eye_bottom = (landmarks.landmark[145].x, landmarks.landmark[145].y)
right_eye_top = (landmarks.landmark[386].x, landmarks.landmark[386].y)
right_eye_bottom = (landmarks.landmark[374].x, landmarks.landmark[374].y)
left_eye_distance = abs(left_eye_top[1] - left_eye_bottom[1])
right_eye_distance = abs(right_eye_top[1] - right_eye_bottom[1])
if left_eye_distance < 0.02 and right_eye_distance < 0.02:
if not eyes_closed:
eyes_closed = True
time_eyes_closed = time.time()
blink_count += 1
if blink_count == 1:
time_first_blink = time.time()
else:
scrolling = True
else:
eyes_closed = False
scrolling = False
if blink_count == 2 and time.time() - time_first_blink < 1.0: # 두 번 깜빡임
pyautogui.doubleClick()
image = draw_korean_text(image, "연속 눈 깜빡임이 감지되었습니다 [클릭]", (image.shape[1] // 2, image.shape[0] - 50), font_path, 30, (0, 255, 0))
blink_count = 0
elif time.time() - time_first_blink >= 1.0:
blink_count = 0
x = int(landmarks.landmark[4].x * image.shape[1])
y = int(landmarks.landmark[4].y * image.shape[0])
if prev_x and prev_y:
diff_x = x - prev_x
diff_y = y - prev_y
if scrolling:
pyautogui.scroll(diff_y * 2) # 스크롤 민감도 조정
image = draw_korean_text(image, "AI 얼굴 방향 스크롤 중...", (image.shape[1] // 2, image.shape[0] - 100), font_path, 30, (0, 0, 255))
else:
pyautogui.move(diff_x * 4.5, diff_y * 4.5) # 마우스 민감도 증가
image = draw_korean_text(image, "AI-Angel EyeTracking 작동중...", (image.shape[1] // 2, image.shape[0] - 100), font_path, 30, (0, 0, 255))
prev_x, prev_y = x, y
# 얼굴의 특정 부분만 표시
for part, indices in landmark_indices.items():
for idx in indices:
lm = landmarks.landmark[idx]
point = (int(lm.x * image.shape[1]), int(lm.y * image.shape[0]))
cv2.circle(image, point, 2, (0, 255, 0), -1)
cv2.imshow('AI Face Mouse Control', image)
if cv2.waitKey(5) & 0xFF == 27:
break
finally:
cap.release()
cv2.destroyAllWindows()
*보다 자세한 내용은 아래 report를 참고하시기 바랍니다.