ai-studio / app.py
lookarooka's picture
Upload app.py with huggingface_hub
19c84ab verified
Raw
History Blame Contribute Delete
7.83 kB
# ๐ŸŽจ Connect AI LAB โ€” ๋ฌด๋ฃŒ ์˜คํ”ˆ์†Œ์Šค AI ์ด๋ฏธ์ง€ ์ƒ์„ฑ + ํŽธ์ง‘ (ZeroGPU)
# ๐ŸŽจ ์ƒ์„ฑ = Krea-2-Turbo (12B) ยท โœ๏ธ ํŽธ์ง‘ = FLUX.1-Kontext(์ตœ๊ณ ๊ธ‰, ํ† ํฐ ์žˆ์œผ๋ฉด) / InstructPix2Pix(ํด๋ฐฑ)
# ์œ ํŠœ๋ธŒ(@CONNECT-AI-LAB) ํ™๋ณด ยท ๋””์ž์ธ์€ ๊ธฐ๋ณธ ํ…Œ๋งˆ + ์ƒ๋‹จ ๋ฐฐ๋„ˆ.
import random
import gradio as gr
import numpy as np
import spaces
import torch
GEN_ID = "krea/Krea-2-Turbo"
EDIT_ID = "black-forest-labs/FLUX.2-klein-4B" # ์ตœ์‹ ยทungatedยทApache 2.0ยท์ƒ์„ฑ+ํŽธ์ง‘ ํ†ตํ•ฉ(4B)
IP2P_ID = "timbrooks/instruct-pix2pix" # ํด๋ฐฑ(ungated)
YOUTUBE = "https://www.youtube.com/@CONNECT-AI-LAB"
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
MAX_SEED = np.iinfo(np.int32).max
# โ”€โ”€ ๐ŸŽจ ์ƒ์„ฑ (Krea-2-Turbo) โ”€โ”€
try:
from diffusers import Krea2Pipeline
gen_pipe = Krea2Pipeline.from_pretrained(GEN_ID, torch_dtype=torch.bfloat16)
except Exception:
from diffusers import DiffusionPipeline
gen_pipe = DiffusionPipeline.from_pretrained(GEN_ID, torch_dtype=torch.bfloat16, trust_remote_code=True)
gen_pipe = gen_pipe.to(DEVICE)
# โ”€โ”€ โœ๏ธ ํŽธ์ง‘: FLUX.2-klein-4B(์ตœ์‹ ยทungated) ์šฐ์„ , ์‹คํŒจ ์‹œ InstructPix2Pix ํด๋ฐฑ โ”€โ”€
EDIT_MODE = "ip2p"
try:
from diffusers import Flux2KleinPipeline
edit_pipe = Flux2KleinPipeline.from_pretrained(EDIT_ID, torch_dtype=torch.bfloat16)
EDIT_MODE = "flux2"
except Exception as e:
print(f"[edit] FLUX.2-klein ๋ถˆ๊ฐ€({e}); InstructPix2Pix๋กœ ํด๋ฐฑ")
from diffusers import StableDiffusionInstructPix2PixPipeline
edit_pipe = StableDiffusionInstructPix2PixPipeline.from_pretrained(
IP2P_ID, torch_dtype=torch.float16, safety_checker=None)
edit_pipe = edit_pipe.to(DEVICE)
@spaces.GPU(duration=50)
def generate(prompt, seed, randomize_seed, steps, progress=gr.Progress(track_tqdm=True)):
if not prompt or not prompt.strip():
raise gr.Error("๋ฌด์—‡์„ ๊ทธ๋ฆด์ง€ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š” ๐Ÿ™‚ (์˜ˆ: ๋ˆˆ ์†์˜ ๊ท€์—ฌ์šด ์—ฌ์šฐ)")
if randomize_seed:
seed = random.randint(0, MAX_SEED)
g = torch.Generator(device=DEVICE).manual_seed(int(seed))
image = gen_pipe(prompt, num_inference_steps=int(steps), generator=g).images[0]
return image, seed
@spaces.GPU(duration=55)
def edit(image, instruction, steps, strength, progress=gr.Progress(track_tqdm=True)):
if image is None:
raise gr.Error("ํŽธ์ง‘ํ•  ์ด๋ฏธ์ง€๋ฅผ ๋จผ์ € ์˜ฌ๋ ค์ฃผ์„ธ์š” ๐Ÿ™‚")
if not instruction or not instruction.strip():
raise gr.Error("์–ด๋–ป๊ฒŒ ๋ฐ”๊ฟ€์ง€ ์ ์–ด์ฃผ์„ธ์š” (์˜ˆ: ํ•˜๋Š˜์„ ๋ณด๋ผ์ƒ‰์œผ๋กœ, ๊ฒจ์šธ๋กœ ๋ฐ”๊ฟ”์ค˜)")
img = image.convert("RGB")
img.thumbnail((1024, 1024))
if EDIT_MODE == "flux2":
out = edit_pipe(image=img, prompt=instruction,
guidance_scale=float(strength), num_inference_steps=int(steps)).images[0]
else:
out = edit_pipe(instruction, image=img, num_inference_steps=int(steps),
image_guidance_scale=1.5, guidance_scale=7.5).images[0]
return out
GEN_EXAMPLES = [
"๋ˆˆ ์†์„ ๊ฑท๋Š” ๊ท€์—ฌ์šด ์•„๊ธฐ ์—ฌ์šฐ, ๋”ฐ๋œปํ•œ ์กฐ๋ช…, ์‹œ๋„ค๋งˆํ‹ฑ, ๊ณ ํ™”์งˆ",
"๋„ค์˜จ ์‚ฌ์ธ ๊ฐ€๋“ํ•œ ๋ฐค์˜ ์„œ์šธ ๊ฑฐ๋ฆฌ, ๋น„ ์˜ค๋Š” ์‚ฌ์ด๋ฒ„ํŽ‘ํฌ, ๋””ํ…Œ์ผ",
"๋ฏธ๋‹ˆ๋ฉ€ํ•œ ๋กœ๊ณ : ์ดˆ๋ก๋น› AI ๋‘๋‡Œ, ์‹ฌํ”Œ, ๋ฒกํ„ฐ ์Šคํƒ€์ผ, ํฐ ๋ฐฐ๊ฒฝ",
"์šฐ์ฃผ์—์„œ ์ง€๊ตฌ๋ฅผ ๋ฐ”๋ผ๋ณด๋Š” ์šฐ์ฃผ๋น„ํ–‰์‚ฌ, ์‚ฌ์‹ค์ , 4k, ์˜ํ™” ๊ฐ™์€",
]
EDIT_EXAMPLES = ["ํ•˜๋Š˜์„ ๋ณด๋ผ์ƒ‰ ๋…ธ์„๋กœ ๋ฐ”๊ฟ”์ค˜", "๊ฒจ์šธ ๋ˆˆ ๋‚ด๋ฆฌ๋Š” ์žฅ๋ฉด์œผ๋กœ", "์ˆ˜์ฑ„ํ™” ๊ทธ๋ฆผ ์Šคํƒ€์ผ๋กœ",
"๋„ค์˜จ ์‚ฌ์ด๋ฒ„ํŽ‘ํฌ ๋А๋‚Œ์œผ๋กœ", "๋ฐฐ๊ฒฝ์„ ์šฐ์ฃผ๋กœ ๋ฐ”๊ฟ”์ค˜"]
CSS = """
.gradio-container {max-width: 960px !important; margin: auto !important;}
#hero {text-align:center; padding: 26px 16px; margin-bottom: 8px; border-radius: 18px;
background: linear-gradient(135deg, #0c2a1a, #0a1830); border: 1px solid rgba(57,255,20,.25);}
#hero .brand {font-family: ui-monospace, monospace; letter-spacing: 6px; color: #58ff86; font-size: 12px;}
#hero h1 {color:#ffffff; font-size: 34px; font-weight: 800; margin: 8px 0 4px;}
#hero .sub {color:#bfeecf; font-size: 14px; margin: 2px 0 0;}
#hero .grn {color:#39ff14; font-weight:700;}
.yt-btn {display:inline-flex; align-items:center; gap:8px; margin-top:14px; padding:12px 24px; border-radius:12px;
text-decoration:none; font-weight:800; font-size:14px; color:#ffffff !important;
background:linear-gradient(135deg,#ff2d55,#ff0000); box-shadow:0 6px 22px rgba(255,0,0,.35);}
.yt-btn:hover {filter:brightness(1.08);}
#foot {text-align:center; color:#8a8f98; font-size:13px; padding:16px 8px;}
#foot a {color:#e11d48; text-decoration:none; font-weight:700;}
"""
_edit_note = ("โœ๏ธ ํŽธ์ง‘: FLUX.2-klein (์ตœ์‹ ยท๊ณ ๊ธ‰)" if EDIT_MODE == "flux2"
else "โœ๏ธ ํŽธ์ง‘: ๊ธฐ๋ณธ ๋ชจ๋“œ (FLUX.2 ๋กœ๋“œ ๋Œ€๊ธฐ)")
with gr.Blocks(css=CSS, theme=gr.themes.Soft(primary_hue="green"), title="Connect AI LAB") as demo:
gr.HTML(f"""
<div id="hero">
<div class="brand">โ—ˆ CONNECT ยท AI ยท LAB</div>
<h1>๐ŸŽจ AI ์ด๋ฏธ์ง€ ์ƒ์„ฑ &amp; ํŽธ์ง‘</h1>
<div class="sub">ํ…์ŠคํŠธ๋กœ <span class="grn">๊ทธ๋ฆฌ๊ณ </span>, ์ด๋ฏธ์ง€๋ฅผ <span class="grn">ํŽธ์ง‘</span>๊นŒ์ง€ ยท ๋ฌด๋ฃŒ ยท ์˜คํ”ˆ์†Œ์Šค</div>
<a class="yt-btn" href="{YOUTUBE}" target="_blank">โ–ถ ์œ ํŠœ๋ธŒ ๊ตฌ๋… ยท @CONNECT-AI-LAB</a>
</div>
""")
with gr.Tabs():
with gr.TabItem("๐ŸŽจ ์ƒ์„ฑ"):
with gr.Row():
g_prompt = gr.Textbox(label="๋ฌด์—‡์„ ๊ทธ๋ฆด๊นŒ์š”?",
placeholder="์˜ˆ: ๋ˆˆ ์†์˜ ๊ท€์—ฌ์šด ์•„๊ธฐ ์—ฌ์šฐ, ๋”ฐ๋œปํ•œ ์กฐ๋ช…, ์‹œ๋„ค๋งˆํ‹ฑ",
scale=5, lines=2)
g_run = gr.Button("๐ŸŽจ ๊ทธ๋ฆฌ๊ธฐ", variant="primary", scale=1)
g_result = gr.Image(label="๊ฒฐ๊ณผ", height=520)
with gr.Accordion("โš™๏ธ ์„ธ๋ถ€ ์„ค์ •", open=False):
with gr.Row():
g_steps = gr.Slider(4, 16, value=8, step=1, label="์Šคํ…")
g_seed = gr.Slider(0, MAX_SEED, value=42, step=1, label="์‹œ๋“œ")
g_rand = gr.Checkbox(label="์‹œ๋“œ ๋žœ๋ค(๋งค๋ฒˆ ๋‹ค๋ฅธ ๊ทธ๋ฆผ)", value=True)
gr.Examples(examples=GEN_EXAMPLES, inputs=[g_prompt], label="โœจ ์˜ˆ์‹œ")
g_in = [g_prompt, g_seed, g_rand, g_steps]
g_run.click(generate, inputs=g_in, outputs=[g_result, g_seed])
g_prompt.submit(generate, inputs=g_in, outputs=[g_result, g_seed])
with gr.TabItem("โœ๏ธ ํŽธ์ง‘"):
gr.Markdown(f"##### {_edit_note}")
with gr.Row():
e_img = gr.Image(label="ํŽธ์ง‘ํ•  ์ด๋ฏธ์ง€ ์˜ฌ๋ฆฌ๊ธฐ", type="pil", height=340)
e_out = gr.Image(label="ํŽธ์ง‘ ๊ฒฐ๊ณผ", height=340)
with gr.Row():
e_inst = gr.Textbox(label="์–ด๋–ป๊ฒŒ ๋ฐ”๊ฟ€๊นŒ์š”? (์ง€์‹œ)",
placeholder="์˜ˆ: ํ•˜๋Š˜์„ ๋ณด๋ผ์ƒ‰ ๋…ธ์„๋กœ, ๊ฒจ์šธ๋กœ ๋ฐ”๊ฟ”์ค˜, ์ˆ˜์ฑ„ํ™” ์Šคํƒ€์ผ๋กœ",
scale=5, lines=2)
e_run = gr.Button("โœ๏ธ ํŽธ์ง‘", variant="primary", scale=1)
with gr.Accordion("โš™๏ธ ์„ธ๋ถ€ ์„ค์ •", open=False):
e_steps = gr.Slider(8, 40, value=18, step=1, label="์Šคํ…(๋‚ฎ์„์ˆ˜๋ก ๋น ๋ฆ„ยท์ฟผํ„ฐ ์ ˆ์•ฝ)")
e_str = gr.Slider(1.0, 5.0, value=2.5, step=0.1, label="์ง€์‹œ ๋ฐ˜์˜ ๊ฐ•๋„")
gr.Examples(examples=EDIT_EXAMPLES, inputs=[e_inst], label="โœจ ํŽธ์ง‘ ์˜ˆ์‹œ")
e_run.click(edit, inputs=[e_img, e_inst, e_steps, e_str], outputs=[e_out])
gr.HTML(f"""
<div id="foot">๐ŸŒฑ Powered by <b>Connect AI LAB</b> ยท ๋” ๋งŽ์€ AI ํ™œ์šฉ๋ฒ•์€
๐Ÿ‘‰ <a href="{YOUTUBE}" target="_blank">์œ ํŠœ๋ธŒ @CONNECT-AI-LAB</a> ๊ตฌ๋…!</div>
""")
if __name__ == "__main__":
demo.launch()