AI Avatar – 4. Animate the Avatar

이번시간에는 지난시간에 만든 아바타를 가지고 움직이게 만들어 볼거에요. 지난 시간에 만든 코드에 덧붙여서 만들거에요. 반드시 지난 시간 코드를 알고 계셔야합니다.

코드의 맨 마지막에 아래 코드를 추가합니다. 아래 코드는 기존에 이미지와 동영상프레임 이미지들에서 획득한 얼굴이미지들을 하나씩 돌면서 RGB형식으로 만들어진 이미지를 BGR로 변환합니다.

opencv_images = [cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) for img in faces]

그리고 아바타에 움직을 추가할 함수, animate_avatar()를 선언합니다. 인자로는 얼굴 이미지들을 넘겨받고, 이미지들을 빠르게 돌려서 움직이는 것 처럼 보이게 만듭니다.

def animate_avatar(images):
    cv2.namedWindow('Avatar Animation', cv2.WINDOW_NORMAL)
    for i in range(30):  # Number of animation cycles
        for image in images:
            cv2.imshow('Avatar Animation', image)
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
    cv2.destroyAllWindows()

# Animate the avatar
animate_avatar(opencv_images)

아래는 완성된 코드입니다.

import os
import cv2
import dlib
import numpy as np
from PIL import Image

def get_files_with_extensions(directory, extensions):
    # get multiple files
    files = os.listdir(directory)
    if len(extensions) > 0:
        return [f for f in files if os.path.splitext(f)[1].lower() in extensions]
    else:
        return files

# dlib 얼굴 검출기 로드
detector = dlib.get_frontal_face_detector()

# 이미지에서 얼굴을 추출하는 함수
def extract_face(image_path):
    image = cv2.imread(image_path)
    faces = detector(image)
    if  len(faces) > 0:
        face = faces[0]   # 발견된 첫 번째 얼굴 추출
        x, y, w, h = face.left(), face.top(), face.width(), face.height()
        face_image = image[y:y+h, x:x+w]
        return cv2.cvtColor(face_image, cv2.COLOR_BGR2RGB)
    else :
        return  None

# 여러 얼굴을 추출하여 반환
def extract_faces_from_arr(image_paths):
    faces = []
    for path in image_paths:
        face = extract_face(path)
        if face is not None :
            faces.append(face)
    return faces

# 얼굴과 비디오 프레임을 합성 아바타로 통합하는 함수
def extract_faces_from_folder(frame_dir):
    # 프레임에서 얼굴 추출하여 배열에 저장
    frame_faces = []
    for frame_file in get_files_with_extensions(frame_dir, ['.jpg', '.png']):
        frame_path = os.path.join(frame_dir, frame_file)
        frame_image = extract_face(frame_path)   # extract_face 함수 재사용
        if frame_image is not None :
            frame_faces.append(frame_image)
    return frame_faces
    
def merge_faces(image_faces, frame_faces):
    return image_faces + frame_faces

def animate_avatar(images):
    cv2.namedWindow('Avatar Animation', cv2.WINDOW_NORMAL)
    for i in range(30):  # Number of animation cycles
        for image in images:
            cv2.imshow('Avatar Animation', image)
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
    cv2.destroyAllWindows()

# 얼굴을 추출할 이미지 경로
image_paths = ['image1.png', 'image2.png', 'image3.png']
image_faces = extract_faces_from_arr(image_paths)

# 동영상 추출 프레임 이미지 폴더 경로
folder_path = 'frames'
frame_faces = extract_faces_from_folder(folder_path)

all_faces = merge_faces(image_faces, frame_faces)

# Convert the PIL images to OpenCV format for animation
opencv_images = [cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) for img in all_faces]

# Animate the avatar
animate_avatar(opencv_images)

실행을 해보도록 하겠습니다. 본 파일을 실행전에 얼굴에 사용할 이미지 3개와 동영상에서 캡쳐한 프레임이미지들이 frames에 들어있는지 확인한 뒤 실행합니다.

python animate_avatar.py

실행을 하면 팝업이 하나 뜨는데 그 안에서 지금까지 생성한 얼굴들이 빠르게 돌아갑니다. 중간에 멈추고 싶을때는 Ctrl+C를 누르면 종료합니다.

아직까지는 그닥 아바타 같아보이지가 않아서, 상용화가 가능하도록 다듬는 부분에 있어서는 아무래도 따로 리서치를 해서 추가로 블로깅을 해야할것 같습니다. 생각보다 실망스러운데 일단 다음시간에 하는 더빙까지 붙여 보도록 하겠습니다.

References