본문 바로가기
AI/기술

CycleGAN으로 성별 전환 프로그램 만들기 (1)

by ai.forme 2020. 12. 8.
반응형

CycleGAN 설명 유튜브 영상 - Naver D2 (링크)

CycleGAN 논문 (링크)

CycleGAN 페이지 (링크)

 

CycleGAN에 대한 내용은 이번 포스팅에서는 다루지 않을 것입니다. 위의 유튜브 영상은 CycleGAN의 저자 중 한 명인 박태성 박사님이 발표하시는 영상입니다.

정말 재밌게 발표하시고 길이도 그다지 길지 않으니 꼭 한 번쯤 보시는 것을 적극 추천드립니다!

 


프로젝트 시작 동기

 

본래 CycleGAN에 관심이 많았는데, 이번에는 작게나마 사람들이 사용할 수 있는 서비스를 만들어보고 싶었습니다. 그래서 사람들이 재밌어할 만한 것이 무엇이 있을까 하다가 성별 전환 프로그램을 만들기로 결정하였습니다. 사실 성별 전환 카메라로 동일한 기능을 제공하는 앱이 이미 있지만 (Face App이 가장 유명한 것 같다), 작게나마 만들어 주위 사람들에게 소소한 재미라도 주고자 합니다. 성능이 좋아야 할 텐데요. :) 한 번에 끝내기는 길 것 같아, 두 편으로 나누어 포스팅하려고 합니다. 사실 로컬 GPU가 메모리가 작아 현재 모델을 학습시키지 못하고 있습니다. 종강하면 혹시 학교 서버를 빌릴 수 있는지 메일을 보내보려고 합니다. 안되면 Co-Lab 써야죠.. 만약 결과가 괜찮다면 여러 새로운 프로그램들도 만들어보려고 합니다. 새로운 것이 최고다! 항상 짜릿하다.

 

 

CycleGAN

 

CycleGAN은 쉽게 말해 한 데이터를 다른 데이터의 특징을 가지도록 변형하는 모델입니다. 기존의 pix2pix와 역할이 비슷하지만, CycleGAN은 데이터 쌍이 필요가 없다는 것에 큰 메리트가 있습니다. 아래 사진들을 보면 이해에 큰 도움이 되실 것 같습니다. CycleGAN 논문 내용 정리는 다음에 기회가 된다면 올려보도록 하겠습니다.

 

 

CycleGAN 적용 예시들

 

사진에서 볼 수 있듯이, 모네의 그림을 사진으로, 말을 얼룩말로 바꾸는 기능을 가지고 있습니다. 논문에 제시된 예시들인데, 이는 상당히 선별적으로 선택된 결과들이라고 합니다. 그래도 신기하다는 것은 여전한 것 같습니다. CycleGAN 같은 경우에는 원본 이미지의 모양새를 유지하며 특성이 바뀌도록 학습이 진행되고 있습니다. 하지만 성별 전환의 경우 이미지의 모양새가 어느 정도 변해야 좀 더 좋은 결과가 나올 것이라는 생각을 하였습니다. 그렇기에 약간의 의구심과 함께 프로젝트를 시작하였습니다.

 

데이터 수집

 

사실 Human Face Image는 이미 전처리해놓은 것들이 꽤 있습니다. 하지만 대부분 서양 Human Face에 굉장히 초점이 맞추어져 있습니다. 제 프로젝트의 동기는 주위 사람들에게 재미를 주는 것이었기 때문에, 기존의 Human Face Dataset들을 사용하면, 한국 사람 사진이 들어왔을 때 제대로 변형하지 못하는 상황이 발생할 수 있다고 생각하였습니다. 그렇기에 그냥 처음부터 시작하자! 는 마음으로 동양(대부분 한국) 사람들의 사진을 수집하였습니다.

 

일단 다양한 얼굴들을 수집하고 싶었으나, 그래도 변형된 얼굴이 잘생기고 예쁘면 좋으니까(?) 우선 연예인들의 사진을 수집하기로 결정하였습니다. 

 

Raw Image Download Code  - Python Selenium

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time
import urllib.request

man_list = [
    연예인들 이름 List
]

count = 0

for man in man_list:

    driver = webdriver.Chrome('data_utils/chromedriver.exe')
    driver.get('https://www.google.co.kr/imghp?hl=ko&tab=wi&ogbl')

    elem = driver.find_element_by_name("q") # Get Search Bar
    elem.send_keys('{} 얼굴 사진'.format(man)) # Type search words
    elem.send_keys(Keys.RETURN) # Press Enter

    time.sleep(2)

    try:   
        images = driver.find_elements_by_css_selector(".rg_i.Q4LuWd")
    except:
        continue

    if len(images) == 0 :
        continue
    
    for image_idx, image in enumerate(images):
        
        try:
            image.click()
            time.sleep(3)
            
            imgURL = driver.find_element_by_css_selector(".n3VNCb").get_attribute('src')
            urllib.request.urlretrieve(imgURL, 'data/celebrity_man/image_{}.jpg'.format(str(count)))
            
            count += 1
            if image_idx == 30 :
                break
        except:
            pass

    driver.close()
    

 

 

데이터 수집은 Selenium 이미지 크롤링으로 자동화하였습니다. 그래도 각 이미지마다 다운로드 딜레이 시간이 3초라 꽤 오랜 시간이 걸렸던 것 같습니다. 남/여 각각 60명 정도의 연예인들을 대상으로, 한 명당 30장 정도의 사진을 다운로드하였습니다. 아직 데이터셋이 연예인들 사진으로만 구성되어있는데, 일반인들의 사진을 어떻게 구할 수 있을지 고민 중입니다. 내가 여러 각도로 찍어야 되나..? 

 

파이선 셀레니움으로 다운받은 이미지들

 

또한, 한국 연예인들 사진으로만 하면 성별적인 특징이 잘 학습되지 않을 것 같아 외국인 얼굴 사진들 또한 일부 포함했습니다. 그냥 '외국 남자 얼굴 사진', ''외국 여자 얼굴 사진'으로 검색하여 나온 사진들을 다운로드하였는데, 그 검색 결과들이 대부분 백인에 편향되어 있었습니다. 추후에 흑인 사진들을 더 넣는 작업을 해야 될 것 같습니다. 구글 왜 편향해..?

 

외국인 얼굴 사진들

 

데이터 가공

 

위의 예시는 어느 정도 괜찮은 이미지들만 가져왔는데요, 사실 이름을 검색해서 나온 사진들을 모두 다운로드하였기 때문에 얼굴만 있는 사진도 있는 반면 여러 사람이 있거나 다른 수식 문구들이 있는 사진들도 다수 포함되어 있습니다. 굉장히 노이즈가 심한 데이터들이 많아 전처리 과정이 반드시 필요했습니다.  이러한 데이터를 그냥 CycleGAN 학습에 사용하게 되면 결말이 좋지 않을 것이 뻔하니까요. 대게 과정에서의 고통을 피해 가면 결과는 달지 않더군요.. 전처리로 헤어스타일과 얼굴 부분만 잘라내기로 하였는데요, 이는 사진의 전체적인 분위기나 옷의 특징들이 모델이 학습하는 것이 모델에 부정적인 영향을 줄 것이라고 생각했기 때문입니다. 전처리로는 Face Recognition 파이썬 라이브러리를 사용했는데, pip 설치 과정에서 에러를 제외하고는 성능도 사용하기도 굉장히 좋고 편하였습니다.

 

Image Preprocessing Code ( Cropping the face ) - Face Recognition

import face_recognition
import cv2

count = 0

for i in range(이미지 개수):

    img_Url = 'data/foreign_man/image_{}.jpg'.format(i)
    image = face_recognition.load_image_file(img_Url)

    # (top, right, bottom, left) order
    face_locations = face_recognition.face_locations(image)

    if len(face_locations) > 1:
        continue

    try: 
        for idx, location in enumerate(face_locations):

            src = cv2.imread(img_Url, cv2.IMREAD_COLOR)
            height, width, channels = src.shape


            face_hieght = location[2] - location[0]
            face_width = location[1] - location[3] 
            
            face_top = location[0] - int(face_hieght * 0.7)
            face_bottom = location[2] + int(face_hieght * 0.5)
            face_left = location[3] - int(face_width * 0.5)
            face_right = location[1] + int(face_width * 0.5)

            if face_top <= 0 : face_top = 0
            if face_bottom >= height-1 : face_bottom = height
            if face_left <= 0 : face_left = 0 
            if face_right >= width-1 : face_right = width
            
            cv2.imwrite('data/man_face/{}.jpg'.format(count), src[face_top:face_bottom, face_left:face_right])
    except:
        continue

    count += 1
    

 

한 가지 특이한 점이 있다면, 얼굴을 잘라낼 때 Face Detected 된 박스에 비해 위로는 상하로는 2.2배, 좌우로는 2배 하여 잘라주었습니다. 이는 사람의 얼굴의 이목구비만을 잘라내는 상황을 방지하며, 동시에 헤어 스타일에 대한 정보를 이미지에 포함해야 됐기 때문입니다. 아래 예시는 이미지를 잘라낸 결과입니다.

 

 

이미지 전처리 결과

 

 

이러한 전처리 과정을 거치고 너무 이미지의 크기가 작은 사진들은 삭제하고 나니, 남자 얼굴 사진으로는 805개, 여자 얼굴 사진으로는 767개의 이미지들을 얻을 수 있었습니다. 하지만 다른 사진이지만 얼굴이 비슷한 사진들이 많기에 데이터셋을 좀 더 다듬는 작업이 필요할 것 같습니다. 중복되는 얼굴들을 삭제하고, 더 다양성 있는 이미지들을 넣을 예정입니다. 혹시 한국인 얼굴 이미지 어디서 얻을 수 있을까요..? 여담으로, 데이터들을 수집하면서 너무 잘생기고 예쁜 얼굴들만 보다 보니 거울을 한동안 못 보겠더군요. 눈이 부시는 작업이었습니다.

 

 

남자 얼굴 데이터셋

 

 

여자 얼굴 데이터셋

 

 

다음 포스팅에서는 CycleGAN 코드와 학습 과정, 결과로 돌아오도록 하겠습니다. 하지만 GPU 사용이 가능해야 하므로, 학교 서버를 빌리기 전 까지는 학습이 힘들 것 같습니다. 혹시 일반인이 쉽게 GPU를 사용할 수 있는 방법이 있을까요?! 어서 모델을 학습시켜서 좋은 결과가 나온다면, 작게나마 웹페이지에 배포하고 싶은 마음이 굴뚝같습니다!! ^_^ 포스팅이 완료되면 데이터셋 배포 또한 Github을 통해 하도록 하겠습니다. 아마 구글 드라이브를 사용할 것 같네요.

 

 

1편을 정리하며..

 

주도적으로 새로운 프로젝트를 아예 처음부터 하는 것은 처음인데 굉장히 재밌는 것 같습니다. 일단 기본적으로 흥미가 있던 CycleGAN을 사용한다는 점, 재밌는 주제를 한다는 점, 그리고 완성되었을 때 빨리 주위 사람들에게 모델을 사용하며 재미를 주고 싶다는 점이 큰 동기로 작용하는 것 같습니다. 동시에 프로젝트를 진행하며 CycleGAN 공부도 하게 되니 논문 리뷰도 머지않아 올릴 수 있을 것 같네요. 그럼, 감사합니다!

반응형