Spaces:
Running on Zero
Running on Zero
Update app.py
Browse files app.py CHANGED
| @@ -62,13 +62,20 @@ PROMPTS = { | |
| 62 | "right": "Move the camera to a side view (profile) from the right so the full body of the character is visible. Background is plain white." |
| 63 | } |
| 64 | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| 65 | def _append_prompt(base: str, extra: str) -> str: |
| 66 | - """末尾にユーザー指定のプロンプトを追記(空なら変更なし)""" |
| 67 | extra = (extra or "").strip() |
| 68 | return (base if not extra else f"{base} {extra}").strip() |
| 69 | |
| 70 | def generate_single_view(input_images, prompt, seed, num_inference_steps, true_guidance_scale): |
| 71 | - """単一視点画像生成""" |
| 72 | generator = torch.Generator(device=device).manual_seed(seed) |
| 73 | result = pipe( |
| 74 | image=input_images if input_images else None, |
| @@ -82,7 +89,6 @@ def generate_single_view(input_images, prompt, seed, num_inference_steps, true_g | |
| 82 | return result[0] |
| 83 | |
| 84 | def concat_images_horizontally(images, bg_color=(255, 255, 255)): |
| 85 | - """画像を横に連結""" |
| 86 | images = [img.convert("RGB") for img in images if img is not None] |
| 87 | if not images: |
| 88 | return None |
| @@ -101,17 +107,23 @@ def concat_images_horizontally(images, bg_color=(255, 255, 255)): | |
| 101 | x += img.width |
| 102 | return canvas |
| 103 | |
| | |
| | |
| | |
| | |
| | |
| 104 | @spaces.GPU() |
| 105 | def generate_turnaround( |
| 106 | image, |
| 107 | extra_prompt="", |
| | |
| 108 | seed=42, |
| 109 | randomize_seed=False, |
| 110 | true_guidance_scale=1.0, |
| 111 | num_inference_steps=4, |
| 112 | progress=gr.Progress(track_tqdm=True), |
| 113 | ): |
| 114 | - """4視点+横連結PNG |
| 115 | if randomize_seed: |
| 116 | seed = random.randint(0, MAX_SEED) |
| 117 | if image is None: |
| @@ -142,8 +154,16 @@ def generate_turnaround( | |
| 142 | progress(1.0, desc="右側面生成中...") |
| 143 | right = generate_single_view([front], p_right, seed+3, num_inference_steps, true_guidance_scale) |
| 144 | |
| 145 | - |
| 146 | - |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| 147 | |
| 148 | # --- UI --- |
| 149 | css = """ |
| @@ -167,7 +187,6 @@ with gr.Blocks(css=css) as demo: | |
| 167 | gr.Markdown("アップロードしたキャラクター画像から正面・背面・左右側面、さらに4枚連結のPNG画像を出力します。") |
| 168 | |
| 169 | with gr.Column(elem_id="col-container"): |
| 170 | - # 追加: 注意文(アップロード欄の直前に表示) |
| 171 | gr.HTML( |
| 172 | "<div class='notice'>" |
| 173 | "注意:他者が作成した画像のアップロードはご遠慮ください。" |
| @@ -185,6 +204,13 @@ with gr.Blocks(css=css) as demo: | |
| 185 | lines=2 |
| 186 | ) |
| 187 | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| 188 | run_button = gr.Button("🎨 生成開始", variant="primary") |
| 189 | status_text = gr.Textbox(label="ステータス", interactive=False) |
| 190 | |
| @@ -204,9 +230,10 @@ with gr.Blocks(css=css) as demo: | |
| 204 | true_guidance_scale = gr.Slider(label="True guidance scale", minimum=1.0, maximum=10.0, step=0.1, value=1.0) |
| 205 | num_inference_steps = gr.Slider(label="生成ステップ数", minimum=1, maximum=40, step=1, value=4) |
| 206 | |
| | |
| 207 | run_button.click( |
| 208 | fn=generate_turnaround, |
| 209 | - inputs=[input_image, extra_prompt, seed, randomize_seed, true_guidance_scale, num_inference_steps], |
| 210 | outputs=[result_front, result_back, result_left, result_right, result_concat, seed, status_text], |
| 211 | ) |
| 212 | |
| | |
| 62 | "right": "Move the camera to a side view (profile) from the right so the full body of the character is visible. Background is plain white." |
| 63 | } |
| 64 | |
| 65 | + # NEW: 出力解像度プリセット |
| 66 | + RESOLUTIONS = { |
| 67 | + "1:4": (512, 2048), |
| 68 | + "1:3": (576, 1728), |
| 69 | + "nealy 9:16": (768, 1344), |
| 70 | + "nealy 2:3": (832, 1216), |
| 71 | + "3:4": (896, 1152), |
| 72 | + } |
| 73 | + |
| 74 | def _append_prompt(base: str, extra: str) -> str: |
| | |
| 75 | extra = (extra or "").strip() |
| 76 | return (base if not extra else f"{base} {extra}").strip() |
| 77 | |
| 78 | def generate_single_view(input_images, prompt, seed, num_inference_steps, true_guidance_scale): |
| | |
| 79 | generator = torch.Generator(device=device).manual_seed(seed) |
| 80 | result = pipe( |
| 81 | image=input_images if input_images else None, |
| | |
| 89 | return result[0] |
| 90 | |
| 91 | def concat_images_horizontally(images, bg_color=(255, 255, 255)): |
| | |
| 92 | images = [img.convert("RGB") for img in images if img is not None] |
| 93 | if not images: |
| 94 | return None |
| | |
| 107 | x += img.width |
| 108 | return canvas |
| 109 | |
| 110 | + # NEW: リサイズユーティリティ |
| 111 | + def resize_to_preset(img: Image.Image, preset_key: str) -> Image.Image: |
| 112 | + w, h = RESOLUTIONS[preset_key] |
| 113 | + return img.resize((w, h), Image.LANCZOS) |
| 114 | + |
| 115 | @spaces.GPU() |
| 116 | def generate_turnaround( |
| 117 | image, |
| 118 | extra_prompt="", |
| 119 | + preset_key="nealy 9:16", # NEW: デフォルト |
| 120 | seed=42, |
| 121 | randomize_seed=False, |
| 122 | true_guidance_scale=1.0, |
| 123 | num_inference_steps=4, |
| 124 | progress=gr.Progress(track_tqdm=True), |
| 125 | ): |
| 126 | + """4視点+横連結PNG生成(ユーザー追記プロンプト対応 & 出力解像度プリセット対応)""" |
| 127 | if randomize_seed: |
| 128 | seed = random.randint(0, MAX_SEED) |
| 129 | if image is None: |
| | |
| 154 | progress(1.0, desc="右側面生成中...") |
| 155 | right = generate_single_view([front], p_right, seed+3, num_inference_steps, true_guidance_scale) |
| 156 | |
| 157 | + # NEW: ここで指定プリセットにリサイズ |
| 158 | + front_r = resize_to_preset(front, preset_key) |
| 159 | + back_r = resize_to_preset(back, preset_key) |
| 160 | + left_r = resize_to_preset(left, preset_key) |
| 161 | + right_r = resize_to_preset(right, preset_key) |
| 162 | + |
| 163 | + # NEW: リサイズ後を連結(横:正面→右→背面→左) |
| 164 | + concat = concat_images_horizontally([front_r, right_r, back_r, left_r]) |
| 165 | + |
| 166 | + return front_r, back_r, left_r, right_r, concat, seed, f"✅ {preset_key} にリサイズして4視点+連結画像を生成しました" |
| 167 | |
| 168 | # --- UI --- |
| 169 | css = """ |
| | |
| 187 | gr.Markdown("アップロードしたキャラクター画像から正面・背面・左右側面、さらに4枚連結のPNG画像を出力します。") |
| 188 | |
| 189 | with gr.Column(elem_id="col-container"): |
| | |
| 190 | gr.HTML( |
| 191 | "<div class='notice'>" |
| 192 | "注意:他者が作成した画像のアップロードはご遠慮ください。" |
| | |
| 204 | lines=2 |
| 205 | ) |
| 206 | |
| 207 | + # NEW: 出力解像度プリセットのプルダウン |
| 208 | + preset_dropdown = gr.Dropdown( |
| 209 | + label="出力解像度プリセット", |
| 210 | + choices=list(RESOLUTIONS.keys()), |
| 211 | + value="nealy 9:16" |
| 212 | + ) |
| 213 | + |
| 214 | run_button = gr.Button("🎨 生成開始", variant="primary") |
| 215 | status_text = gr.Textbox(label="ステータス", interactive=False) |
| 216 | |
| | |
| 230 | true_guidance_scale = gr.Slider(label="True guidance scale", minimum=1.0, maximum=10.0, step=0.1, value=1.0) |
| 231 | num_inference_steps = gr.Slider(label="生成ステップ数", minimum=1, maximum=40, step=1, value=4) |
| 232 | |
| 233 | + # NEW: クリック時に preset_dropdown を引数として渡す |
| 234 | run_button.click( |
| 235 | fn=generate_turnaround, |
| 236 | + inputs=[input_image, extra_prompt, preset_dropdown, seed, randomize_seed, true_guidance_scale, num_inference_steps], |
| 237 | outputs=[result_front, result_back, result_left, result_right, result_concat, seed, status_text], |
| 238 | ) |
| 239 | |