|
import gradio as gr |
|
import replicate |
|
import requests |
|
import os |
|
import json |
|
import asyncio |
|
import concurrent.futures |
|
from io import BytesIO |
|
from PIL import Image |
|
from typing import List, Tuple, Dict |
|
|
|
|
|
REPLICATE_API_TOKEN = os.getenv("RAPI_TOKEN") |
|
FRIENDLI_TOKEN = os.getenv("FRIENDLI_TOKEN") |
|
|
|
|
|
STYLE_TEMPLATES = { |
|
"3D Style (Pixar-like)": { |
|
"name": "3D Style", |
|
"description": "Pixar-esque 3D render with volumetric lighting", |
|
"example": "A fluffy ginger cat wearing a tiny spacesuit, floating amidst a vibrant nebula in a 3D render. The cat is gazing curiously at a swirling planet with rings made of candy. Background is filled with sparkling stars and colorful gas clouds, lit with soft, volumetric lighting. Style: Pixar-esque, highly detailed, playful. Colors: Deep blues, purples, oranges, and pinks. Rendered in Octane, 8k resolution." |
|
}, |
|
"Elegant SWOT Quadrant": { |
|
"name": "SWOT Analysis", |
|
"description": "Flat-design 4-grid layout with minimal shadows", |
|
"example": "Elegant SWOT quadrant: flat-design 4-grid on matte-white backdrop, thin pastel separators, top-left 'Strengths' panel shows glowing shield icon and subtle motif, top-right 'Weaknesses' panel with cracked chain icon in soft crimson, bottom-left 'Opportunities' panel with sunrise-over-horizon icon in optimistic teal, bottom-right 'Threats' panel with storm-cloud & lightning icon in deep indigo, minimal shadows, no text, no watermark, 16:9, 4K" |
|
}, |
|
"Colorful Mind Map": { |
|
"name": "Mind Map", |
|
"description": "Hand-drawn educational style with vibrant colors", |
|
"example": "A handrawn colorful mind map diagram: educational style, vibrant colors, clear hierarchy, golden ratio layout. Central concept with branching sub-topics, each branch with unique color coding, organic flowing connections, doodle-style icons for each node" |
|
}, |
|
"Business Workflow": { |
|
"name": "Business Process", |
|
"description": "End-to-end business workflow with clear phases", |
|
"example": "A detailed hand-drawn diagram illustrating an end-to-end business workflow with Market Analysis, Strategy Development, Product Design, Implementation, and Post-Launch Review phases. Clear directional arrows, iconography for each component, vibrant educational yet professional style" |
|
}, |
|
"Industrial Design": { |
|
"name": "Product Design", |
|
"description": "Sleek industrial design concept sketch", |
|
"example": "A sleek industrial design concept: Curved metallic body with minimal bezel, Touchscreen panel for settings, Modern matte black finish, Hand-drawn concept sketch style with annotations and dimension lines" |
|
}, |
|
"3D Bubble Chart": { |
|
"name": "Bubble Chart", |
|
"description": "Clean 3D bubble visualization", |
|
"example": "3-D bubble chart on clean white 2Γ2 grid, quadrant titles hidden, four translucent spheres in lime, azure, amber, magenta, gentle depth-of-field, modern consulting aesthetic, no text, 4K" |
|
}, |
|
"Timeline Ribbon": { |
|
"name": "Timeline", |
|
"description": "Horizontal ribbon timeline with cyber-futuristic vibe", |
|
"example": "Horizontal ribbon timeline, milestone pins glowing hot pink on charcoal, year markers as circles, faint motion streaks, cyber-futuristic vibe, no text, 1920Γ1080" |
|
}, |
|
"Risk Heat Map": { |
|
"name": "Heat Map", |
|
"description": "Risk assessment heat map with gradient colors", |
|
"example": "Risk Heat Map: square grid, smooth gradient from mint to fire-red, cells beveled, simple legend strip hidden, long subtle shadow, sterile white frame, no text" |
|
}, |
|
"Pyramid/Funnel": { |
|
"name": "Funnel Chart", |
|
"description": "Multi-layer gradient funnel visualization", |
|
"example": "Pyramid / Funnel: 5-layer gradient funnel narrowing downwards, top vivid sky-blue, mid mint-green, bottom sunset-orange, glass reflection, minimal background, no text" |
|
}, |
|
"KPI Dashboard": { |
|
"name": "Dashboard", |
|
"description": "Dark-mode analytics dashboard with sci-fi interface", |
|
"example": "KPI Dashboard: Dark-mode analytic dashboard, three glass speedometers glowing neon lime, two sparkline charts under, black glass background, sci-fi interface, no text, 4K" |
|
}, |
|
"Value Chain": { |
|
"name": "Value Chain", |
|
"description": "Horizontal value chain with industrial look", |
|
"example": "Value Chain Diagram: Horizontal value chain blocks, steel-blue gradient bars with subtle bevel, small gear icons above each segment, sleek industrial look, shadow cast, no text" |
|
}, |
|
"Gantt Chart": { |
|
"name": "Gantt Chart", |
|
"description": "Hand-drawn style Gantt chart with playful colors", |
|
"example": "Gantt Chart: Hand-drawn style Gantt bars sketched with vibrant markers on dotted grid notebook page, sticky-note color palette, playful yet organized, perspective tilt, no text" |
|
}, |
|
"Mobile App Mockup": { |
|
"name": "App Mockup", |
|
"description": "Clean wireframe for mobile app design", |
|
"example": "MOCKUP DESIGN: A clean hand-drawn style wireframe for a mobile app with Title screen, Login screen, Dashboard with sections, Bottom navigation bar, minimalist design with annotations" |
|
}, |
|
"Flowchart": { |
|
"name": "Flowchart", |
|
"description": "Vibrant flowchart with minimalistic icons", |
|
"example": "FLOWCHART DESIGN: A hand-drawn style flowchart, vibrant colors, minimalistic icons showing process flow from START to END with decision points, branches, and clear directional arrows" |
|
} |
|
} |
|
|
|
def generate_prompt_with_llm(topic: str, style_example: str = None) -> str: |
|
"""μ£Όμ μ μ€νμΌ μμ λ₯Ό λ°μμ LLMμ μ¬μ©ν΄ μ΄λ―Έμ§ ν둬ννΈλ₯Ό μμ±""" |
|
url = "https://api.friendli.ai/dedicated/v1/chat/completions" |
|
headers = { |
|
"Authorization": f"Bearer {FRIENDLI_TOKEN}", |
|
"Content-Type": "application/json" |
|
} |
|
|
|
|
|
system_prompt = """You are an expert image prompt engineer specializing in creating detailed, visually rich prompts for AI image generation. |
|
|
|
Your task is to create prompts that: |
|
1. Are highly specific and visual, describing exact details, colors, lighting, and composition |
|
2. Include style references (e.g., "3D render", "hand-drawn", "flat design", "industrial sketch") |
|
3. Specify technical details like resolution, aspect ratio, or rendering style when appropriate |
|
4. Use descriptive adjectives for materials, textures, and atmospheres |
|
5. Avoid abstract concepts - focus on concrete visual elements |
|
|
|
Important guidelines: |
|
- If given a style example, adapt the topic to match that specific visual style |
|
- Maintain the technical vocabulary and visual descriptors from the style example |
|
- Always output ONLY the prompt without any explanation or additional text |
|
- Make prompts between 50-150 words for optimal results""" |
|
|
|
user_message = f"Topic: {topic}" |
|
if style_example: |
|
user_message += f"\n\nStyle reference to follow:\n{style_example}\n\nAdapt the topic to match this exact visual style." |
|
|
|
payload = { |
|
"model": "dep89a2fld32mcm", |
|
"messages": [ |
|
{ |
|
"role": "system", |
|
"content": system_prompt |
|
}, |
|
{ |
|
"role": "user", |
|
"content": user_message |
|
} |
|
], |
|
"max_tokens": 300, |
|
"top_p": 0.8, |
|
"temperature": 0.7, |
|
"stream": False |
|
} |
|
|
|
try: |
|
response = requests.post(url, json=payload, headers=headers) |
|
if response.status_code == 200: |
|
result = response.json() |
|
return result['choices'][0]['message']['content'].strip() |
|
else: |
|
return f"ν둬ννΈ μμ± μ€ν¨: {response.status_code}" |
|
except Exception as e: |
|
return f"ν둬ννΈ μμ± μ€ μ€λ₯ λ°μ: {str(e)}" |
|
|
|
def translate_to_english(text: str) -> str: |
|
"""νκΈ ν
μ€νΈλ₯Ό μμ΄λ‘ λ²μ (LLM μ¬μ©)""" |
|
if not any(ord('κ°') <= ord(char) <= ord('ν£') for char in text): |
|
return text |
|
|
|
url = "https://api.friendli.ai/dedicated/v1/chat/completions" |
|
headers = { |
|
"Authorization": f"Bearer {FRIENDLI_TOKEN}", |
|
"Content-Type": "application/json" |
|
} |
|
|
|
payload = { |
|
"model": "dep89a2fld32mcm", |
|
"messages": [ |
|
{ |
|
"role": "system", |
|
"content": "You are a translator. Translate the given Korean text to English. Only return the translation without any explanation." |
|
}, |
|
{ |
|
"role": "user", |
|
"content": text |
|
} |
|
], |
|
"max_tokens": 500, |
|
"top_p": 0.8, |
|
"stream": False |
|
} |
|
|
|
try: |
|
response = requests.post(url, json=payload, headers=headers) |
|
if response.status_code == 200: |
|
result = response.json() |
|
return result['choices'][0]['message']['content'].strip() |
|
else: |
|
return text |
|
except Exception as e: |
|
return text |
|
|
|
def generate_image(prompt: str, seed: int = 10) -> Tuple[Image.Image, str]: |
|
"""Replicate APIλ₯Ό μ¬μ©ν΄ μ΄λ―Έμ§ μμ±""" |
|
try: |
|
english_prompt = translate_to_english(prompt) |
|
|
|
if not REPLICATE_API_TOKEN: |
|
return None, "RAPI_TOKEN νκ²½λ³μκ° μ€μ λμ§ μμμ΅λλ€." |
|
|
|
client = replicate.Client(api_token=REPLICATE_API_TOKEN) |
|
|
|
input_params = { |
|
"seed": seed, |
|
"prompt": english_prompt, |
|
"speed_mode": "Extra Juiced π (even more speed)", |
|
"output_quality": 80 |
|
} |
|
|
|
output = client.run( |
|
"prunaai/hidream-l1-fast:17c237d753218fed0ed477cb553902b6b75735f48c128537ab829096ef3d3645", |
|
input=input_params |
|
) |
|
|
|
if output: |
|
if isinstance(output, str) and output.startswith('http'): |
|
response = requests.get(output) |
|
img = Image.open(BytesIO(response.content)) |
|
return img, english_prompt |
|
else: |
|
img = Image.open(BytesIO(output.read())) |
|
return img, english_prompt |
|
else: |
|
return None, "μ΄λ―Έμ§ μμ± μ€ν¨" |
|
|
|
except Exception as e: |
|
return None, f"μ€λ₯: {str(e)}" |
|
|
|
def generate_images_parallel(topic: str, selected_styles: List[str], seed: int) -> List[Dict]: |
|
"""μ νλ μ€νμΌλ€μ λν΄ λ³λ ¬λ‘ μ΄λ―Έμ§ μμ±""" |
|
results = [] |
|
|
|
|
|
prompts = [] |
|
for style_name in selected_styles: |
|
if style_name in STYLE_TEMPLATES: |
|
style_info = STYLE_TEMPLATES[style_name] |
|
prompt = generate_prompt_with_llm(topic, style_info["example"]) |
|
prompts.append({ |
|
"style": style_name, |
|
"prompt": prompt, |
|
"style_info": style_info |
|
}) |
|
|
|
|
|
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: |
|
future_to_style = {} |
|
for prompt_data in prompts: |
|
future = executor.submit(generate_image, prompt_data["prompt"], seed) |
|
future_to_style[future] = prompt_data |
|
|
|
for future in concurrent.futures.as_completed(future_to_style): |
|
prompt_data = future_to_style[future] |
|
try: |
|
img, used_prompt = future.result() |
|
results.append({ |
|
"style": prompt_data["style"], |
|
"image": img, |
|
"prompt": prompt_data["prompt"], |
|
"used_prompt": used_prompt, |
|
"success": img is not None |
|
}) |
|
except Exception as e: |
|
results.append({ |
|
"style": prompt_data["style"], |
|
"image": None, |
|
"prompt": prompt_data["prompt"], |
|
"used_prompt": str(e), |
|
"success": False |
|
}) |
|
|
|
return results |
|
|
|
def process_multiple_styles(topic: str, selected_styles: List[str], seed: int): |
|
"""μ¬λ¬ μ€νμΌλ‘ μ΄λ―Έμ§ μμ± μ²λ¦¬""" |
|
if not topic.strip(): |
|
return [], "μ£Όμ λ₯Ό μ
λ ₯ν΄μ£ΌμΈμ." |
|
|
|
if not selected_styles: |
|
return [], "μ΅μ νλμ μ€νμΌμ μ νν΄μ£ΌμΈμ." |
|
|
|
status = f"μ νλ {len(selected_styles)}κ° μ€νμΌλ‘ μ΄λ―Έμ§ μμ± μ€..." |
|
|
|
|
|
results = generate_images_parallel(topic, selected_styles, seed) |
|
|
|
|
|
images = [] |
|
prompts_info = [] |
|
|
|
for result in results: |
|
if result["success"]: |
|
images.append((result["image"], result["style"])) |
|
prompts_info.append(f"**{result['style']}**\nν둬ννΈ: {result['prompt']}\n") |
|
else: |
|
prompts_info.append(f"**{result['style']}** - μμ± μ€ν¨: {result['used_prompt']}\n") |
|
|
|
final_status = f"μ΄ {len(images)}κ° μ΄λ―Έμ§ μμ± μλ£\n\n" + "\n".join(prompts_info) |
|
|
|
return images, final_status |
|
|
|
|
|
with gr.Blocks(title="AI λ©ν°μ€νμΌ μ΄λ―Έμ§ μμ±κΈ°", theme=gr.themes.Soft()) as demo: |
|
gr.Markdown(""" |
|
# π¨ AI λ©ν°μ€νμΌ μ΄λ―Έμ§ μμ±κΈ° |
|
|
|
μ£Όμ λ₯Ό μ
λ ₯νκ³ μνλ μ€νμΌμ μ ννλ©΄, κ° μ€νμΌμ λ§λ μ΄λ―Έμ§λ₯Ό λμμ μμ±ν©λλ€. |
|
""") |
|
|
|
with gr.Row(): |
|
with gr.Column(scale=1): |
|
topic_input = gr.Textbox( |
|
label="μ£Όμ μ
λ ₯", |
|
placeholder="μ: μ°μ£Όλ₯Ό μ¬ννλ κ³ μμ΄, λ―Έλμ λμ, νμ μ μΈ μ ν λμμΈ", |
|
lines=2 |
|
) |
|
|
|
gr.Markdown("### μ€νμΌ μ ν (볡μ μ ν κ°λ₯)") |
|
|
|
|
|
style_checkboxes = gr.CheckboxGroup( |
|
choices=list(STYLE_TEMPLATES.keys()), |
|
label="μμ±ν μ€νμΌ", |
|
value=["3D Style (Pixar-like)"], |
|
info="κ° μ€νμΌμ κ³ μ ν μκ°μ νΉμ±μ κ°μ§κ³ μμ΅λλ€" |
|
) |
|
|
|
seed_input = gr.Slider( |
|
minimum=1, |
|
maximum=100, |
|
value=10, |
|
step=1, |
|
label="μλ κ° (λμΌν μλλ λμΌν μ΄λ―Έμ§ μμ±)" |
|
) |
|
|
|
generate_btn = gr.Button("π μ νν μ€νμΌλ‘ μ΄λ―Έμ§ μμ±", variant="primary", size="lg") |
|
|
|
with gr.Column(scale=2): |
|
|
|
output_gallery = gr.Gallery( |
|
label="μμ±λ μ΄λ―Έμ§λ€", |
|
show_label=True, |
|
elem_id="gallery", |
|
columns=2, |
|
rows=3, |
|
object_fit="contain", |
|
height="auto" |
|
) |
|
|
|
status_text = gr.Markdown( |
|
value="μμ± μν λ° ν둬ννΈ μ λ³΄κ° μ¬κΈ°μ νμλ©λλ€." |
|
) |
|
|
|
|
|
with gr.Accordion("π μ€νμΌ κ°μ΄λ", open=False): |
|
style_guide_text = "### μ¬μ© κ°λ₯ν μ€νμΌ:\n\n" |
|
for style_name, style_info in STYLE_TEMPLATES.items(): |
|
style_guide_text += f"**{style_name}**: {style_info['description']}\n\n" |
|
gr.Markdown(style_guide_text) |
|
|
|
|
|
generate_btn.click( |
|
fn=process_multiple_styles, |
|
inputs=[topic_input, style_checkboxes, seed_input], |
|
outputs=[output_gallery, status_text] |
|
) |
|
|
|
gr.Markdown(""" |
|
--- |
|
### π‘ μ¬μ© ν: |
|
- **μ£Όμ **: ꡬ체μ μΌμλ‘ μ’μ κ²°κ³Όλ₯Ό μ»μ μ μμ΅λλ€ |
|
- **μ€νμΌ μ‘°ν©**: μ¬λ¬ μ€νμΌμ μ ννλ©΄ λ€μν μκ°μ ννμ λΉκ΅ν μ μμ΅λλ€ |
|
- **λ³λ ¬ μ²λ¦¬**: μ νν λͺ¨λ μ€νμΌμ μ΄λ―Έμ§κ° λμμ μμ±λ©λλ€ |
|
- **μλ κ°**: λμΌν μλλ‘ μ¬ν κ°λ₯ν κ²°κ³Όλ₯Ό μ»μ μ μμ΅λλ€ |
|
|
|
κ° μ€νμΌμ μ λ¬Έμ μΌλ‘ νλ μ΄μ
λ μμ λ₯Ό κΈ°λ°μΌλ‘ ν둬ννΈλ₯Ό μμ±ν©λλ€. |
|
""") |
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
if not REPLICATE_API_TOKEN: |
|
print("κ²½κ³ : RAPI_TOKEN νκ²½ λ³μκ° μ€μ λμ§ μμμ΅λλ€.") |
|
if not FRIENDLI_TOKEN: |
|
print("κ²½κ³ : FRIENDLI_TOKEN νκ²½ λ³μκ° μ€μ λμ§ μμμ΅λλ€.") |
|
|
|
demo.launch() |