셀럽미: 닮은 아이돌 찾기 2024년 맞이 업데이트 (2/3) – 얼굴영역 크롭

셀럽미: 닮은 아이돌 찾기 서비스(https://celebme.net) 의 2024년 맞이 업데이트의 두번째 이야기입니
지난 글에서 이미지 수집을 다뤘었는데요~
* 지난 글: 셀럽미: 닮은 아이돌 찾기 2024년 맞이 업데이트(1/3) – 이미지 수집
이번엔 수집된 이미지를 전처리하는, 가공하는 내용을 다뤄보겠습니다.

얼굴영역 크롭 배경

우선 아래의 이미지를 한번 볼까요?
이 이미지는 여자 아이돌 김세정씨를 검색했을 때 수집된 이미지 중 일부인데요.
한 이미지 내에 김세정씨 외에 사내맞선의 주인공인 안효섭씨도 같이 포함된 걸 볼 수 있어요.

여자 아이돌 김세정씨만 사진에서 발췌한다면 당연히 모델 학습의 정확도가 올라갈 텐데요~
이 과정을 자동화해본다면 다음과 같은 순서로 이뤄지게 됩니다.

  • 이미지 내의 모든 얼굴 영역을 추출
  • 한 이미지 내에는 한 사람의 얼굴이 포함된 이미지만 남기고 나머지는 버림
  • 한 사람의 이미지의 퀄리티가 좋은 경우만 선별하여 저장

단순해 보이는 위 과정으로 자동화해서 저장해본다면, 위와 같이 두 사람 이상이 포함된 이미지는 버려지게 됩니다.
두 명이 속한 이미지는 아니지만, 아래와 같은 경우는 어떨까요?
얼굴이 속한 부분만 크롭해서 저장한다면 모델의 정확도를 늘릴 수 있습니다.

얼굴만 크롭해서 저장하면 아래와 같은 결과를 볼 수 있어요.

얼굴영역 크롭 코드

위의 얼굴영역 크롭 배경에 기술된 과정을 따라 자동화한 코드는 아래와 같습니다.

# %pip install --upgrade pip setuptools wheel opencv-python
# %pip install git+https://github.com/serengil/retinaface.git
# sudo apt install -y libhdf5-dev


import os

import cv2
import numpy as np
from retinaface import RetinaFace

os.environ["CUDA_VISIBLE_DEVICES"] = "-1"

model = RetinaFace.build_model()


def detect_and_save_faces(input_image_path, output_folder, face_idx):
    # Load the input image
    image = cv2.imread(input_image_path)

    try:
        # Detect faces using the SSD model
        detected_faces = RetinaFace.extract_faces(
            input_image_path,
            threshold=0.97,
            model=model,
            align=True,
            align_first=True,
            allow_upscaling=True,
            expand_face_area=10,
        )

        if len(detected_faces) > 1:
            # print(f'{input_image_path} has more than 1 face.')
            return face_idx

        # Create the output folder if it doesn't exist
        if not os.path.exists(output_folder):
            os.makedirs(output_folder)

        # Loop through each detected face and save it as a 300x300 PNG file
        for i, face_info in enumerate(detected_faces):
            face = face_info
            # print(face)

            # print("resizing..")
            maxwidth, maxheight = 300, 300
            f1 = maxwidth / face.shape[1]
            f2 = maxheight / face.shape[0]
            f = min(f1, f2)
            dim = (int(face.shape[1] * f), int(face.shape[0] * f))
            face_resized = cv2.resize(face, dim, interpolation=cv2.INTER_AREA)

            face_rgb = cv2.cvtColor(face_resized, cv2.COLOR_BGR2RGB)

            # Create a black image with the target size
            padded_image = np.zeros((300, 300, 3), dtype=np.uint8)
            padding_rows = (300 - dim[1]) // 2
            padding_cols = (300 - dim[0]) // 2

            # Add the resized image to the padded image, with padding on the left and right sides
            padded_image[padding_rows : padding_rows + dim[1], padding_cols : padding_cols + dim[0]] = face_rgb

            face_idx += 1
            # Save the face as a PNG file in the output folder
            output_path = os.path.join(output_folder, f"face_{face_idx}.png")
            cv2.imwrite(output_path, padded_image)

        # print(f"{len(detected_faces)} faces detected and saved to {output_folder}")
    except Exception as e:
        print(str(e))

    return face_idx


import os

path_img = "images"
path_faces = "faces"

# # test
# input_image_path = '01.png'
# output_folder = 'output/'
# detect_and_save_faces(input_image_path, output_folder, 0)

for gender in sorted(os.listdir(path_img)):
    path_gender = os.path.join(path_img, gender)
    for name in sorted(os.listdir(path_gender)):
        if os.path.exists(os.path.join(path_faces, gender, name)):
            continue

        path_name = os.path.join(path_img, gender, name)
        face_idx = 0
        for path_file in sorted(os.listdir(path_name)):
            path_img_file = os.path.join(path_name, path_file)
            if os.path.getsize(path_img_file) < 100000:
                continue
            # print(path_img_file)
            # input_image_path = '01.png'
            output_folder = os.path.join(path_faces, gender, name)
            face_idx = detect_and_save_faces(path_img_file, output_folder, face_idx)

위 코드의 처리 과정은 아래와 같습니다.

  • images/[성별]/[아이돌이름]/[그림파일명] 폴더구조를 가진 이미지 파일들을 로드
  • 로드한 이미지에서 얼굴이 포함된 개수를 집계
  • 얼굴수가 1보다 크면 스킵
  • 그렇지 않다면, 해당 얼굴만큼 이미지를 크롭
  • 이미지를 크롭할 때, 얼굴부분의 각도를 평평하게 transform
  • 넓이나, 높이 중 긴 부분을 300 픽셀로 리사이징
  • 300×300 픽셀이 되도록 나머지 부분은 검정색으로 padding하여 채우기
  • 결과물인 300×300 픽셀의 얼굴이미지를 faces/[성별]/[아이돌이름]/[얼굴번호] 밑으로 저장

비교적 코드가 길지 않지만, 단순하지는 않아보이네요~
그래도 꽤나 성가시고 손이 많이 가는 프로세스였는데, 자동화해서 이렇게 잘 돌아가는 코드를 보면 뿌듯함이 올라옵니다.

이미지 수집과 얼굴영역 크롭 결과

이전 글에서 작성한 코드로 수집된 이미지에 목록과 사이즈에요.

도합 75000 장에 달하는 이미지를 수집했고, 사이즈로는 합쳐서 약 14GB의 사이즈를 수집했네요.

여자 아이돌 목록
  • EXID 솔지
  • EXID 하니
  • EXID 혜린
  • ITZY 류진
  • ITZY 리아
  • ITZY 예지
  • ITZY 유나
  • ITZY 채령
  • 강미나
  • 김세정
  • 나인뮤지스 경리
  • 뉴진스 다니엘
  • 뉴진스 민지
  • 뉴진스 하니
  • 뉴진스 해린
  • 뉴진스 혜인
  • 다이아 기희현
  • 다이아 예빈
  • 다이아 유니스
  • 다이아 은채
  • 다이아 정채연
  • 다이아 주은
  • 라붐 소연
  • 라붐 솔빈
  • 라붐 진예
  • 라붐 해인
  • 러블리즈 이미주
  • 레드벨벳 슬기
  • 레드벨벳 아이린
  • 레드벨벳 예리
  • 레드벨벳 웬디
  • 레드벨벳 조이
  • 르세라핌 카즈하
  • 르세라핌 허윤진
  • 르세라핌 홍은채
  • 마마무 솔라
  • 마마무 화사
  • 브레이브걸스 민영
  • 브레이브걸스 유나
  • 브레이브걸스 유정
  • 브레이브걸스 은지
  • 블랙핑크 로제
  • 블랙핑크 리사
  • 블랙핑크 제니
  • 블랙핑크 지수
  • 설현
  • 소녀시대 윤아
  • 소녀시대 태연
  • 소녀시대 티파니
  • 소녀시대 효연
  • 아이브 가을
  • 아이브 레이
  • 아이브 리즈
  • 아이브 안유진
  • 아이브 이서
  • 아이브 장원영
  • 아이유
  • 아이즈원 권은비
  • 아이즈원 김채원
  • 아이즈원 이채연
  • 에스파 닝닝
  • 에스파 윈터
  • 에스파 지젤
  • 에스파 카리나
  • 엔믹스 규진
  • 엔믹스 릴리
  • 엔믹스 배이
  • 엔믹스 설윤
  • 엔믹스 지니
  • 엔믹스 지우
  • 엔믹스 해원
  • 여자아이들 미연
  • 여자아이들 민니
  • 여자아이들 소연
  • 여자아이들 슈화
  • 여자아이들 우기
  • 여자친구 유주
  • 여자친구 은하
  • 트와이스 나연
  • 트와이스 모모
  • 트와이스 사나
  • 트와이스 정연
  • 트와이스 지효
  • 하이키 리이나
  • 하이키 서이
  • 하이키 옐
  • 하이키 휘서
남자 아이돌 목록
  • BTS RM
  • BTS 뷔
  • BTS 슈가
  • BTS 정국
  • BTS 제이홉
  • BTS 지민
  • BTS 진
  • EXO 디오
  • NCT 마크
  • NCT 재현
  • WINNER 강승윤
  • WINNER 송민호
  • WINNER 이승훈
  • 더보이즈 뉴
  • 더보이즈 상연
  • 더보이즈 선우
  • 더보이즈 에릭
  • 더보이즈 영훈
  • 더보이즈 제이콥
  • 더보이즈 주연
  • 더보이즈 주학년
  • 더보이즈 케빈
  • 더보이즈 큐
  • 더보이즈 현재
  • 라이즈 성찬
  • 라이즈 소희
  • 라이즈 쇼타로
  • 라이즈 승한
  • 라이즈 앤톤
  • 라이즈 원빈
  • 라이즈 은석
  • 몬스타엑스 기현
  • 몬스타엑스 민혁
  • 몬스타엑스 셔누
  • 몬스타엑스 아이엠
  • 몬스타엑스 주헌
  • 몬스타엑스 형원
  • 블락비 박경
  • 블락비 지코
  • 블락비 피오
  • 비스트 양요섭
  • 비스트 용준형
  • 비스트 윤두준
  • 비투비 이창섭
  • 샤이니 온유
  • 샤이니 키
  • 샤이니 태민
  • 세븐틴 민규
  • 세븐틴 에스쿱스
  • 세븐틴 원우
  • 세븐틴 준
  • 세븐틴 호시
  • 슈퍼주니어 신동
  • 스트레이키즈 리노
  • 스트레이키즈 방찬
  • 스트레이키즈 승민
  • 스트레이키즈 창빈
  • 스트레이키즈 필릭스
  • 스트레이키즈 한
  • 스트레이키즈 현진
  • 아스트로 MJ
  • 아스트로 라키
  • 아스트로 문빈
  • 아스트로 윤산하
  • 아스트로 진진
  • 아스트로 차은우
  • 워너원 강다니엘
  • 워너원 옹성우
  • 워너원 황민현
  • 인피니트 엘
  • 제국의아이들 김동준
  • 제국의아이들 임시완
  • 제국의아이들 황광희
  • 투모로우바이투게더 수빈
  • 투모로우바이투게더 연준

이번엔 얼굴영역 크롭 결과 이미지 목록과 사이즈를 살펴볼까요?

중간에 모델결과물이 섞여있어서 모델 결과 용량 약 240MB 를 제외하면, 약 25000개 수준의 얼굴이미지 파일과, 용량으로는 대략 3.3GB 로 줄어든걸 확인할 수 있어요.

  • 얼굴영역 크롭 전
    • 전체 아이돌 이미지 개수: 75220
    • 이미지 크기 합: 14GB
  • 얼굴영역 크롭 후 전체 아이돌 이미지 개수: 25242
    • 전체 아이돌 이미지 개수: 25242 (약 53000개 감소)
    • 이미지 크기 합: 3.3GB (약 11GB 감소)

마무리

이번 글에서는 셀럽미: 닮은 아이돌 찾기 2024년 맞이 업데이트를 위해 수집된 이미지를 얼굴 영역 기준으로 크롭하는 과정을 다뤄봤는데요.
코드를 전체적으로 담았기 때문에 이 과정에 대해 궁금하신 분들에게 도움이 되셨다면 좋겠습니다.
다음 글에서는 이 이미지들을 활용해서 닮은 아이돌 찾기 모델을 만든 과정을 다뤄볼게요~