본문 바로가기

카테고리 없음

나도코딩, 이미지처리 openCV 강좌 마지막 프로젝트 수정 코드

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()