Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
LTSGOD committed Jul 28, 2023
2 parents 75aed30 + e99067d commit 8c9d228
Show file tree
Hide file tree
Showing 24 changed files with 240 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ def pipe(self,image:Image.Image,input_bbox:np.array,prompt:str,negative_prompt:s
1번을 수행한다면 아래 2번과 3번 과정은 비동기로 작성해도 가능할 것 같다.
추후 속도를 위해 개선할 예정!
"""

#2-1. 위에서 추출된 마스크 이외의 배경을 제거한 뒤, 이미지 변환
segment_img = image_segmentation(image,mask)

Expand Down
58 changes: 43 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
# 인물 『 애니메이션 』 화 프로젝트

### 앗, 『이 세계』로부터의 손님이 내게 찾아왔다!?
#### 여기 애니화된 사진이 있으면 좋을듯?

<!-- <img src="https://img.shields.io/badge/Python-blue?style=flat&logo=Python&logoColor=white"/> <img src="https://img.shields.io/badge/Pytorch-orange?style=flat&logo=Pytorch&logoColor=white"/><br>
<img src="https://img.shields.io/badge/Streamlit-darkred?style=flat&logo=Streamlit&logoColor=white"/> <img src="https://img.shields.io/badge/Redis-red?style=flat&logo=Redis&logoColor=white"/> -->
<center>

| Before || After |
|--------|---|-------|
| ![Before](./src/cat1.jpg) || ![After](./src/cat1.jpg) |
| ![Before](./src/cat2.jpg) || ![After](./src/cat2.jpg) |
| ![Before](./src/cat3.jpg) || ![After](./src/cat3.jpg) |

</center>

## Visits

Expand All @@ -16,49 +22,71 @@

| [김지현](https://github.com/codehyunn) | [박상필](https://github.com/SangphilPark) | [오동혁](https://github.com/97DongHyeokOH) | [이상민](https://github.com/dldltkdals) | [이태순](https://github.com/LTSGOD) |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------: |
| <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQrscwx3lsb0twVlYNjri57vfLQ2R_c6ABDmA&usqp=CAU" alt="대체 텍스트" width="100" height="100"> | <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQrscwx3lsb0twVlYNjri57vfLQ2R_c6ABDmA&usqp=CAU" alt="대체 텍스트" width="100" height="100"> | <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQrscwx3lsb0twVlYNjri57vfLQ2R_c6ABDmA&usqp=CAU" alt="대체 텍스트" width="100" height="100"> | <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQrscwx3lsb0twVlYNjri57vfLQ2R_c6ABDmA&usqp=CAU" alt="대체 텍스트" width="100" height="100"> | <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQrscwx3lsb0twVlYNjri57vfLQ2R_c6ABDmA&usqp=CAU" alt="대체 텍스트" width="100" height="100"> |
| <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQrscwx3lsb0twVlYNjri57vfLQ2R_c6ABDmA&usqp=CAU" alt="대체 텍스트" width="100" height="100"> | <img src="./src/T5082.jpg" alt="대체 텍스트" width="100" height="100"> | <img src="./src/T5124.jpg" alt="대체 텍스트" width="100" height="100"> | <img src="./src/T5141.png" alt="대체 텍스트" width="100" height="100"> | <img src="./src/T5165.jpg" alt="대체 텍스트" width="100" height="100"> |
| 모델 엔지니어링 및 학습 | 모델 엔지니어링 및 학습 | 서비스 파이프라인 구축 | 모델 파이프라인 구축 | 웹 서비스, 클라우드 구축 |

## Introduction

### 프로젝트 동기
- 애니메이션과 웹툰 매니아 층은 정식으로 제공되는 컨텐츠를 넘어 부가적인 창작물을 소비
- CV 분야의 생성 기술을 **애니메이션 및 웹툰**에 적용하여 **자신만의 콘텐츠 제작**을 돕고자 함
- 전체적인 이미지를 변경해주는 기존 서비스들과 달리 **인물만 골라서 원하는 캐릭터로 변환**해주는 서비스 기획

### 기대효과
- **애니메이션 및 웹툰 플랫폼**에서 활용 가능
- Instagram, TikTok, YouTube 등 **소셜 미디어의 파급효과** 기대
- **디지털 콘텐츠 창작 혁명**의 시작 기능

## Dataset
- 네이버 웹툰 및 애니메이션 캐릭터 사진 데이터
- (512, 512) 크기의 원하는 캐릭터 사진 8~10장
- 준비된 데이터 셋으로 Pretrained된 모델에 학습을 진행하면 해당 캐릭터 스타일로 변환해주는 Fine-tuning된 모델이 만들어짐

## Model
- Model Pipeline

![모델 파이프라인](./src/Model%20Pipeline.png)
![모델 파이프라인](./src/Model_Pipeline.png)

- Used Model

| ![Segmentation Model](./src/SAM.png) | ![Inpainting Model](./src/LAMA.png) | ![Stable Diffusion](./src/Stable%20diffusion.png) |
|:--------------------------:|:--------------------------:|:--------------------------:|
| SAM | LAMA | Stable Diffusion |

- SAM
- 인물 추출에 사용
- 사용자의 입력을 받아 원하는 객체만 segmentation
- 다른 segmentation 모델과 비교했을 때 성능이 좋음

- SAM - 인물 추출에 사용
- LAMA
- 인물이 제거된 배경 생성에 사용
- 기존 stable diffusion의 inpaint 파이프라인을 사용하면 신체 일부분을 놓치는 점을 확인
- 인물과 배경을 분리해 캐릭터를 생성하고 배경의 인물 영역을 inpainting하고 캐릭터를 넣어줌
- LAMA 사용 전/후

![Segmentation Model](./src/SAM.png)
<center>

- LAMA - 인물이 제거된 배경 생성에 사용
| LAMA 사용 전 | LAMA 사용 후 |
|--------------|--------------|
| ![사용 전](./src/before_lama.png) | ![사용 후](./src/after_lama.png) |

![Inpainting Model](./src/LAMA.png)
</center>

- Stable Diffusion - 캐릭터 생성에 사용

![Stable Diffusion](./src/Stable%20diffusion.png)
- Stable Diffusion
- 캐릭터 생성에 사용
- 쉽고 빠른 학습 -> DreamBooth를 통해 10장 이하의 사진과 한 시간 이내의 시간으로도 스타일 학습이 가능
- 좋은 캐릭터화 성능


## Product Serving
![Product Serving](./src/Service%20Pipeline.png)

## Demo

![how_to_use](https://github.com/boostcampaitech5/level3_cv_finalproject-cv-16/assets/64296314/8bd24250-fe51-4fdc-b0a5-b15a76e1a93d)
![Demo](https://github.com/boostcampaitech5/level3_cv_finalproject-cv-16/assets/64296314/baa60c7a-605f-463f-b1fe-dd782670242f)

## Future Works
- 모델 경량화를 통한 서비스 시간 단축
- 사용자가 원하는 그림체 자동 학습 파이프라인 구축
- 사용자 편의에 맞게 UI/UX 개선
- 고품질 서비스 배포를 위한 Super Resolution 과정 추가
- 사용자가 원하는 캐릭터 자동 학습 기능 구축
- 카카오톡 등의 SNS를 통한 결과 공유 기능 추가
50 changes: 50 additions & 0 deletions app/frontend.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
@font-face {
font-family: 'twaysky';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/twaysky.woff') format('woff');
font-weight: normal;
font-style: normal;
}

@font-face {
font-family: 'Pretendard-Regular';
src: url('https://cdn.jsdelivr.net/gh/Project-Noonnu/[email protected]/Pretendard-Regular.woff') format('woff');
font-style: normal;
}

.css-10trblm {
/* Main title */
font-family : 'twaysky';
text-align: left;
}

.css-1dp5vir{
/* line on top */
background-image : none ;
background-color : #f06292;
}


.css-vk3wp9{
/* side bar */
background-color: #fce4ec;
}

.css-nziaof{
/* chosen side bar */
background-color: #f8bbd0;
}

.css-content-new-font{
font-family: 'Pretendard-Regular';
font-weight:400;
}

.css-title-new-font{
font-family: 'Pretendard-Regular';
font-weight: 900;

}

.css-5rimss{
font-family : 'Pretendard-Regular';
}
45 changes: 29 additions & 16 deletions app/frontend.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import streamlit as st
from PIL import Image

with open('frontend.css') as f :
st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)

def new_font_tag(text, mode) :
if mode == 'title' :
font = f"<span class='css-title-new-font'>{text}</span>"
elif mode == 'content' :
font = f"<span class='css-content-new-font'>{text}</span>"
st.markdown(font, unsafe_allow_html=True)

def set_image_width(image_path, width):
img_style = f"<style>img {{ max-width: {width}; }}</style>"
st.markdown(img_style, unsafe_allow_html=True)
Expand All @@ -10,8 +20,9 @@ def centered_text(link_text, url):
centered_style = "text-align: center;"
st.write(f"<p style='{centered_style}'><a href='{url}'>{link_text}</a></p>", unsafe_allow_html=True)

def service_info():
st.write("# 앗, 『이 세계』로부터의 손님이 내게 찾아왔다!?")
def service_info() :
st.write("# 앗, 『이 세계』로부터의 손님이")
st.write("# 내게 찾아왔다!?")
st.write('---')

img, _, text = st.columns([0.2, 0.02, 0.78])
Expand All @@ -23,10 +34,10 @@ def service_info():
st.image(resized_image)

with text:
st.write("## 사진 속에서 선택한 인물을 원하는 화풍을 묘사해 애니메이션화 하는 서비스")
new_font_tag("'앗, 『이 세계』로부터의 손님이 내게 찾아왔다!?'는 사진 속에서 선택한 인물을 원하는 화풍을 묘사해 애니메이션화 하는 서비스입니다.", 'title')
st.write('')
st.write('##### 저희 서비스는 사용자들이 자신의 삶과 관심사를 연관시키는 창작물을 손쉽게 제작할 수 있도록 도와줍니다. 사용자들은 자신만의 독특한 사진을 만들고 공유하며, 창작에 어려움을 겪는 분들도 손쉽게 원하는 콘텐츠를 제작할 수 있습니다.')
st.write('##### 특히 애니메이션과 웹툰을 좋아하는 분들이 쉽고 즐겁게 자신만의 창작물을 만들어 나가는 새로운 경험을 기대해 볼 수 있습니다.')
new_font_tag('저희 서비스는 사용자들이 자신의 삶과 관심사를 연관시키는 창작물을 손쉽게 제작할 수 있도록 도와줍니다. 사용자들은 자신만의 독특한 사진을 만들고 공유하며, 창작에 어려움을 겪는 분들도 손쉽게 원하는 콘텐츠를 제작할 수 있습니다.', 'content')
new_font_tag('특히 애니메이션과 웹툰을 좋아하는 분들이 쉽고 즐겁게 자신만의 창작물을 만들어 나가는 새로운 경험을 기대해 볼 수 있습니다.', 'content')

def service_example():
st.text("")
Expand All @@ -50,24 +61,25 @@ def profile():
st.text("")
st.text("")
st.text("")
st.write('## Made By CV-16')
st.write('## About us')
st.write('---')
images = st.columns([0.05, 0.18, 0.18, 0.18, 0.18, 0.18, 0.05])
names = st.columns([0.05, 0.18, 0.18, 0.18, 0.18, 0.18, 0.05])

with images[1]:
set_image_width(profile_img, '100%')
set_image_width(t5065_profile_img, '100%')

with images[2]:
set_image_width(profile_img, '100%')
set_image_width(t5082_profile_img, '100%')

with images[3]:
set_image_width(profile_img, '100%')
set_image_width(t5124_profile_img, '100%')

with images[4]:
set_image_width(profile_img, '100%')
set_image_width(t5141_profile_img, '100%')

with images[5]:
set_image_width(profile_img, '100%')
set_image_width(t5165_profile_img, '100%')

with names[1]:
centered_text('T5065_김지현B', 'https://github.com/codehyunn')
Expand All @@ -84,13 +96,14 @@ def profile():
with names[5]:
centered_text('T5165_이태순', 'https://github.com/LTSGOD')

st.set_page_config(
page_title="최『AI』",
layout='wide',
)

main_img = Image.open('image_video/main_cat.jpg')
profile_img = Image.open('image_video/best_idol.jpg')

t5065_profile_img = Image.open('image_video/best_idol.jpg')
t5082_profile_img = Image.open('image_video/T5082.jpg')
t5124_profile_img = Image.open('image_video/T5124.jpg')
t5141_profile_img = Image.open('image_video/T5141.png')
t5165_profile_img = Image.open('image_video/T5165.jpg')

st.sidebar.info("Select a service above.")

Expand Down
33 changes: 33 additions & 0 deletions app/how_to_use.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
@font-face {
font-family: 'twaysky';
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/twaysky.woff') format('woff');
font-weight: normal;
font-style: normal;
}

.css-10trblm {
font-family : 'twaysky';
}

.css-10trblm {
/* Main title */
font-family : 'twaysky';
text-align: left;
}

.css-1dp5vir{
/* line on top */
background-image : none ;
background-color : #f06292;
}


.css-vk3wp9{
/* side bar */
background-color: #fce4ec;
}

.css-nziaof{
/* chosen side bar */
background-color: #f8bbd0;
}
Binary file added app/image_video/T5082.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/image_video/T5124.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/image_video/T5141.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/image_video/T5165.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/image_video/how_to_use.mp4
Binary file not shown.
9 changes: 7 additions & 2 deletions app/pages/how_to_use.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import streamlit as st

with open('how_to_use.css') as f :
st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)

# 로컬 동영상 파일 경로
video_path = "image_video/how_to_use.mp4"

# 동영상 표시
st.write('## 사용 방법')
st.write('## 사용 방법')
st.write('---')
st.video(video_path)
st.video(video_path)

# 영상 위에 사진으로 설명 추가하면 더 좋을듯
34 changes: 22 additions & 12 deletions app/pages/upload_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,27 @@
import io
import re

with open('upload_image.css') as f :
st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)

st.set_option('deprecation.showfileUploaderEncoding', False)

# Upload an image and set some options for demo purposes
st.header("최『AI』")
img_file = st.sidebar.file_uploader(label='Upload a file', type=['png', 'jpg'])
st.header("♥ 『이 세계』로부터 온 손님 보러가기 ♥")
img_file = st.sidebar.file_uploader(label='▶ 변환하고 싶은 파일을 올려주세요 ʕت̫͡ʔ', type=['png', 'jpg'])
realtime_update = st.sidebar.checkbox(label="Update in Real Time", value=True)
box_color = st.sidebar.color_picker(label="Box Color", value='#0000FF')
box_color = st.sidebar.color_picker(label="Box Color", value='#FF001A')
aspect_choice = st.sidebar.radio(label="Aspect Ratio", options=["1:1"])
aspect_dict = {
"1:1": (1, 1),
}
aspect_dict = {"1:1": (1, 1)}
aspect_ratio = aspect_dict[aspect_choice]


def new_font_tag(text, mode) :
if mode == 'title' :
font = f"<p class='css-title-new-font'>{text}</p>"
elif mode == 'content' :
font = f"<p class='css-content-new-font'>{text}</p>"
st.markdown(font, unsafe_allow_html=True)

def box_algorithm(img_file: Image, aspect_ratio: tuple = None) -> dict:
box = (0, 0, 512, 512)
height = 512
Expand All @@ -37,6 +44,8 @@ def check_size(img):
return img

if img_file:
new_font_tag("▼ 변환하고 싶은 인물에 최대한 가깝게 박스를 조절해주세요!", 'content')

img = Image.open(img_file)
img = img.convert('RGB')

Expand All @@ -54,7 +63,7 @@ def check_size(img):
(rect['left'], rect['top'], rect['width'] + rect['left'], rect['height'] + rect['top']))

# Manipulate cropped image at will
st.write("Preview")
new_font_tag("▼ 아래 사진에서 박스가 잘 지정됐는지 확인해보세요!", 'content')
_ = cropped_img.thumbnail((512, 512))
st.image(cropped_img)

Expand All @@ -70,7 +79,7 @@ def check_size(img):
"메데이아(하루만 네가 되고 싶어)", "이아로스(하루만 네가 되고 싶어)"]

form = st.form(key='email')
selected = form.selectbox('그림체를 선택하세요', ver)
selected = form.selectbox('변환하고 싶은 인물을 선택해주세요.', ver)
email = form.text_input('결과를 받을 이메일을 입력해주세요.')
submit = form.form_submit_button('제출')

Expand All @@ -82,8 +91,9 @@ def check_size(img):
"bbox": rect, "ver": selected}
response = requests.post(
"http://127.0.0.1:8001/submit", data=json.dumps(files))
st.write(f'10분내에 {email}결과가 전송됩니다.')
new_font_tag(f'10분 내에 {email}이 세계에서 온 손님이 찾아갑니다 ʕت̫͡ʔ', 'content')
else:
st.write("유효한 메일 주소를 입력하세요.")
new_font_tag("유효한 메일 주소를 입력해주세요.", 'content')

else:
st.write('제출 버튼을 눌러 애니메이션 변환 이미지를 받아보세요.')
new_font_tag("제출 버튼을 눌러 이 세계에서 온 손님을 만나보세요 ʕت̫͡ʔ", 'content')
Loading

0 comments on commit 8c9d228

Please sign in to comment.