본문 바로가기
개발 공부 기록하기/- Python

1:1 비율의 얼굴 중심 썸네일 생성하기

by soulduse 2023. 8. 21.
반응형

세로 또는 가로로 긴 이미지의 썸네일 문제점

이미지를 가져와서 무지성으로 상단을 기준으로 1:1 비율로 잘랐더니 위와 같은 결과물이 나왔습니다.

사진 업로드, 그리고 자동으로 생성된 썸네일. 편리하다고 생각했던 이 기능에도 숨겨진 고충이 있었습니다. "왜 얼굴이 짤렸을까?", "이 부분만 보여주면 좋았을텐데..." 자동 생성된 썸네일에 대한 아쉬움이 한두번이 아니었습니다.

얼굴 인식의 힘을 빌려 이 문제를 해결하려고 합니다. 이 포스트에서는 얼굴 인식 기술을 활용해 짤리지 않는, 사용자의 얼굴을 중심으로 한 완벽한 1:1 썸네일을 만드는 방법을 함께 알아보겠습니다.

 

먼저 작업의 완성된 결과물 부터 보겠습니다.

 원본 이미지 생성된 썸네일
 
 

주어진 이미지에서 얼굴 인식을 활용하여 이미지 상단에 얼굴이 위치하도록 1:1 비율의 썸네일을 만들어보겠습니다.

 

관련 코드 입니다.

import cv2
import os
import numpy as np

class ThumbnailCreator:
    def __init__(self):
        # OpenCV를 사용하여 얼굴 인식을 위한 준비
        self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
    
    def create_square_thumbnail(self, image, save_folder, image_name):
        """
        주어진 이미지를 바탕으로 1:1 비율의 썸네일 이미지를 생성하고 저장 후, 저장 경로를 반환합니다.
        얼굴 인식을 통해 얼굴이 이미지 상단에 위치하도록 조절합니다.
        :param image: PIL 이미지 객체
        :param save_folder: 저장할 폴더 경로
        :param image_name: 이미지 파일명
        :return: 저장된 썸네일 이미지의 경로
        """
        face_position = self._detect_face(image)
        thumbnail = self._create_thumbnail_based_on_face(image, face_position)
        return self._save_thumbnail(thumbnail, save_folder, image_name)
    
    def _detect_face(self, image):
        """
        이미지 내의 얼굴을 인식하는 함수
        :param image: PIL 이미지 객체
        :return: 얼굴의 위치와 크기 (x, y, w, h) 또는 None (얼굴 미검출시)
        """
        gray = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2GRAY)
        faces = self.face_cascade.detectMultiScale(gray, 1.3, 5)
        return faces[0] if len(faces) > 0 else None
        
    def _create_thumbnail_based_on_face(self, image, face_position):
        """
        얼굴의 위치를 바탕으로 이미지를 1:1 비율로 잘라내는 함수
        :param image: PIL 이미지 객체
        :param face_position: 얼굴의 위치와 크기 (x, y, w, h) 또는 None
        :return: 1:1 비율로 잘린 썸네일 이미지
        """
        width, height = image.size
        if face_position:
            x, y, w, h = face_position
            offset_y = h // 3
        else:
            x = width // 2
            y = height // 2
            offset_y = 0
        
        if width == height:
            return image
        elif width < height:
            return self._crop_vertical_image(image, y, offset_y)
        else:
            return self._crop_horizontal_image(image, x, w)
    
    def _crop_vertical_image(self, image, y, offset_y):
        """
        세로 이미지를 적절한 위치에서 1:1 비율로 잘라내는 함수
        :param image: PIL 이미지 객체
        :param y: 얼굴의 y 좌표
        :param offset_y: 얼굴을 상단으로 올리기 위한 오프셋
        :return: 1:1 비율로 잘린 썸네일 이미지
        """
        width, height = image.size
        new_height = width
        top = max(0, y - offset_y)
        bottom = top + new_height
        if bottom > height:
            bottom = height
            top = height - new_height
        return image.crop((0, top, width, bottom))

    def _crop_horizontal_image(self, image, x, w):
        """
        가로 이미지를 적절한 위치에서 1:1 비율로 잘라내는 함수
        :param image: PIL 이미지 객체
        :param x: 얼굴의 x 좌표
        :param w: 얼굴의 너비
        :return: 1:1 비율로 잘린 썸네일 이미지
        """
        width, height = image.size
        new_width = height
        left = max(0, x + w // 2 - new_width // 2)
        right = left + new_width
        if right > width:
            right = width
            left = width - new_width
        return image.crop((left, 0, right, height))
    
    def _save_thumbnail(self, thumbnail, save_folder, image_name):
        """
        썸네일 이미지를 저장하고 저장 경로를 반환하는 함수
        :param thumbnail: PIL 썸네일 이미지 객체
        :param save_folder: 저장할 폴더 경로
        :param image_name: 이미지 파일명
        :return: 저장된 썸네일 이미지의 경로
        """
        thumbnail_np = cv2.cvtColor(np.array(thumbnail), cv2.COLOR_RGB2BGR)
        thumbnail_np = cv2.resize(thumbnail_np, (300, 300), interpolation=cv2.INTER_AREA)
        thumbnail_path = os.path.join(save_folder, f'thumbnail_{image_name}')
        cv2.imwrite(thumbnail_path, thumbnail_np)
        return thumbnail_path

 

ThumbnailCreator 클래스

우선 전체적인 구조로 ThumbnailCreator 클래스를 생성하였습니다. 이 클래스는 주어진 이미지를 바탕으로 1:1 비율의 썸네일 이미지를 생성하는 기능을 담당합니다.

 

  • __init__ 메소드: OpenCV의 얼굴 인식 기능을 초기화하는 부분입니다.
  • create_square_thumbnail: 외부에서 호출되는 주요 함수로, 이미지를 입력받아 썸네일을 생성하며, 생성된 썸네일 이미지의 저장 경로를 반환합니다.
  • _detect_face: 이미지 내의 얼굴 위치와 크기를 반환하는 함수입니다. 얼굴이 없는 경우 None을 반환합니다.
  • _create_thumbnail_based_on_face: 얼굴의 위치를 기준으로 이미지를 1:1 비율로 잘라내는 작업을 수행합니다.
  • _crop_vertical_image와 _crop_horizontal_image: 세로 또는 가로 이미지를 적절한 위치에서 잘라내는 함수입니다.
  • _save_thumbnail: 생성된 썸네일 이미지를 저장하고, 저장된 경로를 반환합니다.

사용 방법

  1. ThumbnailCreator 클래스를 초기화합니다.
  2. create_square_thumbnail 메소드를 호출하여 이미지를 입력받고, 썸네일을 생성합니다.

예시:

thumbnail_creator = ThumbnailCreator()
thumbnail_path = thumbnail_creator.create_square_thumbnail(image, './save_folder/', 'sample_image.jpg')
print(f"생성된 썸네일 경로: {thumbnail_path}")

이 클래스를 활용하면 손쉽게 얼굴 인식을 통한 1:1 비율의 썸네일을 생성할 수 있습니다. 특히 얼굴 위치를 기준으로 썸네일을 생성함으로써 사용자가 중요하게 여기는 정보를 잃지 않고, 썸네일 이미지를 표현할 수 있습니다.

 

 

이렇게 주어진 이미지를 바탕으로 얼굴을 인식하여 1:1 비율의 썸네일을 생성완료 하였습니다.

세로 또는 가로로 긴 이미지들을 자동화하여 썸네일을 만들일이 있었는데 이 로직으로 많은 썸네일을 효율적으로 만들수 있게 되었습니다.

 

 

 

반응형