import gradio as gr import numpy as np import random import torch import os from diffusers import DiffusionPipeline from openai import OpenAI from PIL import Image, ImageDraw # 建立 OpenAI client(讀環境變數) client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) device = "cuda" if torch.cuda.is_available() else "cpu" model_repo_id = "stabilityai/sdxl-turbo" if torch.cuda.is_available(): torch_dtype = torch.float16 else: torch_dtype = torch.float32 pipe = DiffusionPipeline.from_pretrained(model_repo_id, torch_dtype=torch_dtype) pipe = pipe.to(device) MAX_SEED = np.iinfo(np.int32).max MAX_IMAGE_SIZE = 1024 # 1️⃣ 中文 → 英文繪圖 prompt def generate_prompt(chinese_text): response = client.chat.completions.create( model="gpt-4o-mini", messages=[ { "role": "system", "content": ( "你是一位專業的海報設計師和健康新知專家。你的任務是將使用者提供的健康主題,轉換為一個專為 SDXL 模型優化的提示詞(Prompt),以生成一張視覺上引人入勝且資訊清晰的健康新知海報。" "請確保提示詞包含以下元素:" "1. **海報風格**:必須明確指定為「專業的健康海報設計(professional health poster design)」、「資訊圖表(infographic)」或類似的風格。" "2. **核心主題**:將使用者提供的健康主題轉換為具體的視覺元素。例如,如果主題是「每日喝水的重要性」,請將其轉化為「一個水杯中充滿水的插畫(an illustration of a water glass full of water)」。" "3. **視覺細節**:加入能提升海報專業感的細節,例如「色彩鮮明(vibrant colors)」、「簡潔的設計(minimalist design)」、「高解析度(high resolution)」、「清晰的排版(clear typography)」。" "4. **畫面構圖**:利用提示詞引導構圖,例如「中央特寫(center focus)」或「清晰的背景(clean background)」。" "5. **負面提示詞**:自動加入常見的負面提示詞,以避免生成低品質、不專業的圖像。例如:`blurry, low quality, deformed, messy, amateur, text, watermark`。" "**格式要求**:" "* 只輸出一個完整的提示詞,不要有任何解釋或額外文字。" "* 請將正向提示詞與負面提示詞分開,中間用逗號隔開。" "* 所有提示詞都使用英文。" "**範例**:" "如果使用者輸入:`每日喝水的重要性`" "請輸出:`a professional health poster design about daily hydration, an illustration of a water glass full of water, clean and vibrant colors, minimalist style, high resolution, clear typography --no blurry, low quality, deformed, messy, amateur, text, watermark`" ) }, {"role": "user", "content": chinese_text} ], max_tokens=150, temperature=0.7, ) return response.choices[0].message.content.strip() # 2️⃣ 繪圖主函式 def infer( prompt, negative_prompt, seed, randomize_seed, width, height, guidance_scale, num_inference_steps, white_width, white_height, white_alpha, # 透明度 corner_radius, # 圓角 progress=gr.Progress(track_tqdm=True), ): if randomize_seed: seed = random.randint(0, MAX_SEED) generator = torch.Generator().manual_seed(seed) # 🔹 GPT 產生完整英文 prompt final_prompt = generate_prompt(prompt) # 強化的負面 prompt strong_negative = ( "text, words, letters, typography, logo, watermark, signature, " "messy details, clutter, objects in the center, elements overlapping the central blank area" ) if negative_prompt: strong_negative += ", " + negative_prompt # 生成圖像 image = pipe( prompt=final_prompt, negative_prompt=strong_negative, guidance_scale=guidance_scale, num_inference_steps=num_inference_steps, width=width, height=height, generator=generator, ).images[0].convert("RGBA") # 🔹 建立透明圖層,畫半透明白底 overlay = Image.new("RGBA", (width, height), (0, 0, 0, 0)) draw = ImageDraw.Draw(overlay) center_x, center_y = width // 2, height // 2 x0 = center_x - white_width // 2 y0 = center_y - white_height // 2 x1 = center_x + white_width // 2 y1 = center_y + white_height // 2 draw.rounded_rectangle( [x0, y0, x1, y1], radius=corner_radius, fill=(255, 255, 255, white_alpha) # 白色 + 透明度 ) # 疊合到原圖 image = Image.alpha_composite(image, overlay).convert("RGB") return image, seed, final_prompt # 範例 examples = [ "多吃水果促進健康", "運動對小朋友的好處", "保持醫院清潔的重要性", ] css = """ #col-container { margin: 0 auto; max-width: 640px; } """ # 3️⃣ Gradio UI with gr.Blocks(css=css) as demo: with gr.Column(elem_id="col-container"): gr.Markdown(" # 健康布告欄產生器 🩺") with gr.Row(): prompt = gr.Text( label="輸入中文主題", max_lines=2, placeholder="輸入布告欄主題,例如:多吃水果促進健康", ) run_button = gr.Button("產生圖像", variant="primary") result = gr.Image(label="Result", show_label=False) prompt_box = gr.Textbox(label="英文繪圖 Prompt(AI 產生)", interactive=False) with gr.Accordion("進階設定", open=False): negative_prompt = gr.Text(label="Negative prompt", placeholder="額外要避免的內容") seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0) randomize_seed = gr.Checkbox(label="隨機種子", value=True) with gr.Row(): width = gr.Slider(label="寬度", minimum=256, maximum=MAX_IMAGE_SIZE, step=32, value=384) height = gr.Slider(label="高度", minimum=256, maximum=MAX_IMAGE_SIZE, step=32, value=384) with gr.Row(): guidance_scale = gr.Slider(label="Guidance scale", minimum=0.0, maximum=15.0, step=0.5, value=8) num_inference_steps = gr.Slider(label="步數", minimum=1, maximum=50, step=1, value=25) with gr.Row(): white_width = gr.Slider(label="白底寬度", minimum=50, maximum=800, step=10, value=300) white_height = gr.Slider(label="白底高度", minimum=50, maximum=800, step=10, value=300) with gr.Row(): white_alpha = gr.Slider(label="白底透明度 (0透明-255不透明)", minimum=0, maximum=255, step=5, value=210) corner_radius = gr.Slider(label="圓角大小", minimum=0, maximum=400, step=5, value=50) gr.Examples(examples=examples, inputs=[prompt]) gr.on( triggers=[run_button.click, prompt.submit], fn=infer, inputs=[ prompt, negative_prompt, seed, randomize_seed, width, height, guidance_scale, num_inference_steps, white_width, white_height, white_alpha, corner_radius, ], outputs=[result, seed, prompt_box], ) if __name__ == "__main__": demo.launch()