| |
|
|
| import os |
| import random |
| import uuid |
| import json |
| import re |
| import gradio as gr |
| import numpy as np |
| from PIL import Image |
| import spaces |
| import torch |
| from diffusers import DiffusionPipeline |
| from typing import Tuple |
| from transformers import pipeline |
|
|
| |
| bad_words = json.loads(os.getenv('BAD_WORDS', '["violence", "blood", "scary", "death", "ghost"]')) |
| default_negative = os.getenv("default_negative","") |
|
|
| |
| translator = pipeline("translation", model="Helsinki-NLP/opus-mt-ko-en") |
|
|
| def check_text(prompt, negative=""): |
| for i in bad_words: |
| if i in prompt: |
| return True |
| return False |
|
|
| |
| style_list = [ |
| { |
| "name": "๋งํ", |
| "prompt": "colorful cartoon {prompt}. vibrant, playful, friendly, suitable for children, highly detailed, bright colors", |
| "negative_prompt": "scary, dark, violent, ugly, realistic", |
| }, |
| { |
| "name": "์ด๋ฆฐ์ด ์ผ๋ฌ์คํธ", |
| "prompt": "children's illustration {prompt}. cute, colorful, fun, simple shapes, smooth lines, highly detailed, joyful", |
| "negative_prompt": "scary, dark, violent, deformed, ugly", |
| }, |
| { |
| "name": "์คํฐ์ปค", |
| "prompt": "children's sticker of {prompt}. bright colors, playful, high resolution, cartoonish", |
| "negative_prompt": "scary, dark, violent, ugly, low resolution", |
| }, |
| { |
| "name": "ํํ์ง", |
| "prompt": "fantasy world for children with {prompt}. magical, vibrant, friendly, beautiful, colorful", |
| "negative_prompt": "dark, scary, violent, ugly, realistic", |
| }, |
| { |
| "name": "(์คํ์ผ ์์)", |
| "prompt": "{prompt}", |
| "negative_prompt": "", |
| }, |
| ] |
|
|
| styles = {k["name"]: (k["prompt"], k["negative_prompt"]) for k in style_list} |
| STYLE_NAMES = list(styles.keys()) |
| DEFAULT_STYLE_NAME = "์คํฐ์ปค" |
|
|
| def apply_style(style_name: str, positive: str, negative: str = "") -> Tuple[str, str]: |
| p, n = styles.get(style_name, styles[DEFAULT_STYLE_NAME]) |
| return p.replace("{prompt}", positive), n + negative |
|
|
| DESCRIPTION = """## ์ด๋ฆฐ์ด ์คํฐ์ปค ์์ฑ๊ธฐ |
| AI๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ๋ฏธ์๊ณ ๊ท์ฌ์ด ์ด๋ฆฐ์ด ์คํฐ์ปค๋ฅผ ์์ฑํฉ๋๋ค. |
| """ |
|
|
| if not torch.cuda.is_available(): |
| DESCRIPTION += "\n<p>โ ๏ธCPU์์ ์คํ ์ค์
๋๋ค. CPU์์๋ ์ ๋๋ก ์๋ํ์ง ์์ ์ ์์ต๋๋ค.</p>" |
|
|
| MAX_SEED = np.iinfo(np.int32).max |
| CACHE_EXAMPLES = torch.cuda.is_available() and os.getenv("CACHE_EXAMPLES", "1") == "1" |
|
|
| device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") |
|
|
| |
| pipe = DiffusionPipeline.from_pretrained( |
| "SG161222/RealVisXL_V3.0_Turbo", |
| torch_dtype=torch.float16, |
| use_safetensors=True, |
| variant="fp16" |
| ).to(device) |
|
|
| |
| def mm_to_pixels(mm, dpi=300): |
| """Convert mm to pixels and make the dimensions divisible by 8.""" |
| pixels = int((mm / 25.4) * dpi) |
| return pixels - (pixels % 8) |
|
|
| |
| size_map = { |
| "75mm": (mm_to_pixels(75), mm_to_pixels(75)), |
| "35mm": (mm_to_pixels(35), mm_to_pixels(35)), |
| } |
|
|
| |
| def save_image(img, background="transparent"): |
| img = img.convert("RGBA") |
| data = img.getdata() |
| new_data = [] |
|
|
| if background == "transparent": |
| for item in data: |
| |
| if item[0] == 255 and item[1] == 255 and item[2] == 255: |
| new_data.append((255, 255, 255, 0)) |
| else: |
| new_data.append(item) |
| elif background == "white": |
| for item in data: |
| new_data.append(item) |
|
|
| img.putdata(new_data) |
| unique_name = str(uuid.uuid4()) + ".png" |
| img.save(unique_name) |
| return unique_name |
|
|
| def randomize_seed_fn(seed: int, randomize_seed: bool) -> int: |
| if randomize_seed: |
| seed = random.randint(0, MAX_SEED) |
| return seed |
|
|
| def translate_if_korean(text): |
| |
| if re.search("[\uac00-\ud7a3]", text): |
| |
| translation = translator(text, max_length=512) |
| return translation[0]['translation_text'] |
| return text |
|
|
| @spaces.GPU(enable_queue=True) |
| def generate( |
| prompt: str, |
| negative_prompt: str = "", |
| use_negative_prompt: bool = False, |
| style: str = DEFAULT_STYLE_NAME, |
| seed: int = 0, |
| size: str = "75mm", |
| guidance_scale: float = 3, |
| randomize_seed: bool = False, |
| background: str = "transparent", |
| progress=gr.Progress(track_tqdm=True), |
| ): |
| |
| prompt = translate_if_korean(prompt) |
| |
| if check_text(prompt, negative_prompt): |
| raise ValueError("ํ๋กฌํํธ์ ์ ํ๋ ๋จ์ด๊ฐ ํฌํจ๋์ด ์์ต๋๋ค.") |
| |
| |
| prompt = " ".join(re.findall(r'\w+', prompt)[:3]) |
|
|
| |
| prompt, negative_prompt = apply_style(style, prompt, negative_prompt) |
| seed = int(randomize_seed_fn(seed, randomize_seed)) |
| generator = torch.Generator().manual_seed(seed) |
|
|
| |
| width, height = size_map.get(size, (1024, 1024)) |
|
|
| if not use_negative_prompt: |
| negative_prompt = "" |
|
|
| options = { |
| "prompt": prompt, |
| "negative_prompt": negative_prompt, |
| "width": width, |
| "height": height, |
| "guidance_scale": guidance_scale, |
| "num_inference_steps": 50, |
| "generator": generator, |
| "num_images_per_prompt": 1, |
| "output_type": "pil", |
| } |
| |
| |
| images = pipe(**options).images |
| image_paths = [save_image(img, background) for img in images] |
| |
| return image_paths, seed |
|
|
| examples = [ |
| "๊ท์ฌ์ด ๊ณ ์์ด", |
| "ํ๋ณตํ ํ ๋ผ", |
| "์๊ณ ์๋ ๊ฐ์์ง", |
| "์ถค์ถ๋ ๋๊ณ ๋", |
| "์ ๋๋ ์๊ธฐ ๋
์๋ฆฌ", |
| "์ฆ๊ฑฐ์ด ์๊ธฐ ์ฌ์", |
| ] |
|
|
| css = """ |
| footer { |
| visibility: hidden; |
| } |
| """ |
|
|
| |
| with gr.Blocks(theme="Nymbo/Nymbo_Theme", css=css) as demo: |
|
|
| with gr.Group(): |
| with gr.Row(): |
| prompt = gr.Text( |
| label="ํ๋กฌํํธ ์
๋ ฅ", |
| show_label=False, |
| max_lines=1, |
| placeholder="ํ๋กฌํํธ๋ฅผ ์
๋ ฅํ์ธ์.", |
| container=False, |
| ) |
| run_button = gr.Button("์คํ") |
| result = gr.Gallery(label="์์ฑ๋ ์คํฐ์ปค", columns=2, preview=True) |
| with gr.Accordion("๊ณ ๊ธ ์ต์
", open=False): |
| use_negative_prompt = gr.Checkbox(label="๋ค๊ฑฐํฐ๋ธ ํ๋กฌํํธ ์ฌ์ฉ", value=True, visible=True) |
| negative_prompt = gr.Text( |
| label="๋ค๊ฑฐํฐ๋ธ ํ๋กฌํํธ", |
| max_lines=1, |
| placeholder="๋ค๊ฑฐํฐ๋ธ ํ๋กฌํํธ๋ฅผ ์
๋ ฅํ์ธ์", |
| value="(๋ฌด์์ด, ํญ๋ ฅ์ ์ธ, ์ด๋์ด, ์ถํ)", |
| visible=True, |
| ) |
| seed = gr.Slider( |
| label="์๋", |
| minimum=0, |
| maximum=MAX_SEED, |
| step=1, |
| value=0, |
| visible=True |
| ) |
| randomize_seed = gr.Checkbox(label="๋๋ค ์๋", value=True) |
| size_selection = gr.Radio( |
| choices=["75mm", "35mm"], |
| value="75mm", |
| label="์คํฐ์ปค ํฌ๊ธฐ", |
| ) |
| style_selection = gr.Radio( |
| choices=STYLE_NAMES, |
| value=DEFAULT_STYLE_NAME, |
| label="์ด๋ฏธ์ง ์คํ์ผ", |
| ) |
| background_selection = gr.Radio( |
| choices=["ํฌ๋ช
", "ํฐ์"], |
| value="ํฐ์", |
| label="๋ฐฐ๊ฒฝ์", |
| ) |
| guidance_scale = gr.Slider( |
| label="๊ฐ์ด๋์ค ์ค์ผ์ผ", |
| minimum=0.1, |
| maximum=20.0, |
| step=0.1, |
| value=6, |
| ) |
|
|
| gr.Examples( |
| examples=examples, |
| inputs=prompt, |
| outputs=[result, seed], |
| fn=generate, |
| cache_examples=CACHE_EXAMPLES, |
| ) |
|
|
| gr.on( |
| triggers=[ |
| prompt.submit, |
| negative_prompt.submit, |
| run_button.click, |
| ], |
| fn=generate, |
| inputs=[ |
| prompt, |
| negative_prompt, |
| use_negative_prompt, |
| style_selection, |
| seed, |
| size_selection, |
| guidance_scale, |
| randomize_seed, |
| background_selection, |
| ], |
| outputs=[result, seed], |
| api_name="run", |
| ) |
|
|
| if __name__ == "__main__": |
| demo.queue(max_size=20).launch() |