https://youtu.be/XK3eU9egll8?si=lqYYb7m9OgW-HTku
웹캠 영상에 캐릭터를 입혀보았습니다. 얼굴이 화면 가장자리로 가면 프로그램 수행이 중단되는 버그가 있는데 그것을 해결해보았습니다.
# 웹캠 영상에서 얼굴 감지해서 캐릭터를 입히는 코드.
# 얼굴이 화면을 벗어나거나 끄트머리로 올 경우, 프로그램이 중단되는 버그를 해결함
# 버그 원인은 캐릭터를 그릴 위치가 화면을 벗어나게 되면 overlay 함수의 행렬 인덱싱에서 오버플로우가 발생해서 그럼.
# 화면을 벗어나거나 끄트머리로 오는 부위는 off를 시켜서 overlay 함수를 건너뛰게 함.
import cv2
import mediapipe as mp
def overlay(image, x, y, w, h, overlay_image): # 대상 이미지(3채널), x,y 좌표, width, height, 덮어씌울 이미지(4채널)
alpha = overlay_image[:, :, 3] # BGRA
mask = alpha / 255.0 # 0~255 -> 0~1사이의 값
for c in range(0, 3): # channel BGR
temp= image[y-h:y+h, x-w:x+w, c]
#print(temp.shape)
image[y-h:y+h, x-w:x+w, c] = overlay_image[:, :, c] * mask + image[y-h:y+h, x-w:x+w, c] * (1.0-mask)
mp_face_detection = mp.solutions.face_detection
mp_drawing = mp.solutions.drawing_utils
cap = cv2.VideoCapture(0)
cap.set(3, 1024)
cap.set(4, 768)
image_right_eye = cv2.imread('right_ear2.png', cv2.IMREAD_UNCHANGED)
image_left_eye = cv2.imread('left_ear2.png', cv2.IMREAD_UNCHANGED)
image_nose = cv2.imread('nose2.png', cv2.IMREAD_UNCHANGED)
face_detection = mp_face_detection.FaceDetection(model_selection=0, min_detection_confidence=50)
# 오른쪽 눈, 왼쪽 눈, 코끝 표현을 기본으로 on을 해서, 얼굴 감지되면 해당 캐릭터 그려지도록 함.
right_eye_on = 1
left_eye_on = 1
nose_tip_on = 1
while True:
ret, image = cap.read()
if not ret:
continue
image.flags.writeable = False
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
results = face_detection.process(image)
image.flags.writeable = True
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
if results.detections:
for detection in results.detections:
keypoints = detection.location_data.relative_keypoints
right_eye = keypoints[0] # 오른쪽 눈
left_eye = keypoints[1]
nose_tip = keypoints[2]
# 좌표가 범위를 벗어나면 오버레이를 하지 않고 넘어감.
# x값에 -50, +50으로 오버래핑 이미지를 입히고, y값에 -50, +50으로 입히게 되는데,
# 그렇게 입힌 캐릭터 좌표 범위가 화면을 벗어나 오버플로우가 나서 overlay 함수 수행 중 행렬 연산에서 오류가 남.
# 따라서 화면 전후좌우로 오버래핑 이미지의 반경에서 keypoints 좌표가 벗어나면 overylay 함수를 수행하지 않고 건너뛴다.
# 아래는 각 캐릭터 이미지의 너비와 폭의 절반을 구함.
right_eye_x_half = int(image_right_eye.shape[1]/2)
right_eye_y_half = int(image_right_eye.shape[0]/2)
left_eye_x_half = int(image_left_eye.shape[1]/2)
left_eye_y_half = int(image_left_eye.shape[0]/2)
nose_tip_x_half = int(image_nose.shape[1]/2)
nose_tip_y_half = int(image_nose.shape[0]/2)
h, w, _ = image.shape
right_eye = (int(right_eye.x * w) -20, int(right_eye.y*h)-100)
left_eye = (int(left_eye.x * w)+20, int(left_eye.y*h) - 100)
nose_tip = (int(nose_tip.x * w), int(nose_tip.y *h))
# 캐릭터 표현 좌표 범위를 벗어나는지 검사해서 벗어난다면 스위치를 off해서 overlay 함수를 건너뛰도록 함.
if (right_eye[0] < right_eye_x_half) or (right_eye[0] > (image.shape[1]-right_eye_x_half)):
right_eye_on = 0
if (right_eye[1] < right_eye_y_half) or (right_eye[1] > (image.shape[0]-right_eye_y_half)):
right_eye_on = 0
if (left_eye[0] < left_eye_x_half) or (left_eye[0] > (image.shape[1]-left_eye_x_half)):
left_eye_on = 0
if (left_eye[1] < left_eye_y_half) or (left_eye[1] > (image.shape[0]-left_eye_y_half)):
left_eye_on = 0
if (nose_tip[0] < nose_tip_x_half) or (nose_tip[0] > (image.shape[1]-nose_tip_x_half)):
nose_tip_on = 0
if (nose_tip[1]< nose_tip_y_half) or (nose_tip[1] > (image.shape[0]-nose_tip_y_half)):
nose_tip_on = 0
# 표현 스위치가 켜져 있으면 overlay 함수 호출
if right_eye_on:
overlay(image, *right_eye, right_eye_x_half, right_eye_y_half, image_right_eye)
if left_eye_on :
overlay(image, *left_eye, left_eye_x_half, left_eye_y_half, image_left_eye)
if nose_tip_on :
overlay(image, *nose_tip, nose_tip_x_half, nose_tip_y_half, image_nose)
# 표현 스위치 기본값은 on 임
right_eye_on = 1
left_eye_on = 1
nose_tip_on = 1
cv2.imshow('image', image)
if cv2.waitKey(33) == ord('q'):
break
cap.release()
cv2.destroyAllWindows()