Slicelayers commited on
Commit
4ac4284
·
verified ·
1 Parent(s): f989429

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +39 -25
app.py CHANGED
@@ -26,7 +26,7 @@ MAX_API_SIZE = 1024
26
 
27
  def pil_to_base64(img: Image.Image) -> str:
28
  """PIL画像をBase64エンコードされたPNGデータに変換します。"""
29
- buffered = io.BytesIO()
30
  # アルファチャンネル(PNG)を維持
31
  if img.mode != 'RGBA':
32
  img = img.convert('RGBA')
@@ -131,27 +131,25 @@ async def nano_banana_completion_api(base_image: Image.Image, prompt: str) -> Im
131
 
132
  # --- メイン処理ロジック (Main Processing Logic) ---
133
 
134
- async def segment_and_inpaint(image_data_tuple: tuple, inpaint_prompt: str):
135
  """
136
  アップロードされた一枚絵とブラシで描かれたマスクを受け取り、
137
  マスクされた領域を分割し、欠損部分をAI補完する関数。
138
  """
139
- if image_data_tuple is None:
140
- return None, None, None, "エラー: 画像がアップロードされていないか、分割したいパーツをブラシで描画してください。", None
 
 
141
 
142
- # Gradioのtype="sketch"は (画像データ, マスクデータ) のタプルを返す
143
- # NOTE: type="sketch"の出力構造は (元の画像 (np.array), マスク画像 (np.array)) です。
144
- original_rgba_np, sketch_mask_np = image_data_tuple
145
-
146
  # PIL Image (元の画像) を取得
147
- original_image = Image.fromarray(original_rgba_np).convert("RGBA")
148
- H, W = original_image.size
149
-
150
- # --- 1. 手動パーツ分割 (ユーザーのブラシ入力に基づく) ---
 
151
 
152
- # スケッチマスクからパーツのアルファチャンネル(描画した部分)を抽出
153
- # sketch_mask_np はRGBA形式で、描画された部分が不透明(アルファ値255)
154
- sketch_mask_alpha = sketch_mask_np[:, :, 3]
155
 
156
  # ユーザーが何も描画しなかった場合のチェック
157
  if np.all(sketch_mask_alpha == 0):
@@ -173,7 +171,7 @@ async def segment_and_inpaint(image_data_tuple: tuple, inpaint_prompt: str):
173
  # 反転マスクを体パーツのアルファチャンネルとして設定
174
  body_with_hole.putalpha(Image.fromarray(inverted_mask_np, mode='L'))
175
 
176
- print("--- 1. 手動パーツ分割 (Gradio Sketch Mask利用) 完了: 分割パーツと補完が必要な欠損体を作成 ---")
177
 
178
  # 2. 欠損領域のAI補完 (AI Inpainting)
179
  completed_body_part = await nano_banana_completion_api(body_with_hole, inpaint_prompt or "マスクされた領域のキャラクターの顔と身体の肌、及び下に着ている服を、元のイラストのテイストに合わせて自然に補完してください。")
@@ -244,30 +242,46 @@ with gr.Blocks(theme=theme, title="Live2D素材自動分割・補完アプリ")
244
  <div style='text-align: center; margin-bottom: 20px; padding: 10px; background: #E0F7FA; border-radius: 12px;'>
245
  <h1 style='color: #00796B; font-size: 2.5em; font-weight: 700;'>🎨 Live2D 素材 自動分割・補完アプリ 🤖</h1>
246
  <p style='color: #004D40; font-size: 1.1em;'>一枚絵をアップロードし、**ブラシでパーツを指定**することで、AIによる欠損部分の自動補完(Nano Banana API利用)を行います。</p>
247
- <p style='color: #004D40; font-size: 1.0em;'>💡 **操作方法:** ①のキャンバスに画像をアップロードした後、**ブラシ**を使って、前髪など**分割したいパーツ**を塗りつぶしてください。塗った部分がパーツとして分離され、AIがその下の欠損を補完します。</p>
248
  </div>
249
  """
250
  )
251
 
252
  with gr.Row():
253
  with gr.Column(scale=1):
254
- # ★★★ 修正��所: tool="sketch" type="sketch" に変更し、互換性を確保 ★★★
255
- input_image = gr.Image(
256
- type="sketch", # 描画ツールを有効化し、(画像, マスク) タプルを出力
257
- label="① 分割したいパーツをブラシで塗る (例: 前髪)",
 
 
 
 
 
258
  height=400,
 
 
 
 
 
 
 
 
 
 
259
  )
 
260
  inpaint_prompt = gr.Textbox(
261
- label=" AI補完プロンプト(オプション)",
262
  value="マスクされた領域のキャラクターの顔と身体の肌、及び下に着ている服を、元のイラストのテイストに合わせて自然に補完してください。",
263
  placeholder="例: マスクされた領域を元の絵柄で自然に描き足す"
264
  )
265
- process_button = gr.Button(" 手動分割・AI補完を実行", variant="primary", scale=0)
266
 
267
  gr.Markdown(
268
  """
269
  ### 📌 開発のポイント
270
- * この機能は、ユーザーがブラシで描いた領域を分離パーツとして扱い、その下をAIで補完することで、Live2Dモデリングのコアとなる**「欠損補完」**を実現します。
271
  """
272
  )
273
 
@@ -295,7 +309,7 @@ with gr.Blocks(theme=theme, title="Live2D素材自動分割・補完アプリ")
295
  # --- イベントリスナー ---
296
  process_button.click(
297
  fn=segment_and_inpaint,
298
- inputs=[input_image, inpaint_prompt],
299
  outputs=[
300
  completed_body_output,
301
  hair_part_output,
 
26
 
27
  def pil_to_base64(img: Image.Image) -> str:
28
  """PIL画像をBase64エンコードされたPNGデータに変換します。"""
29
+ buffered = io.BytesIo()
30
  # アルファチャンネル(PNG)を維持
31
  if img.mode != 'RGBA':
32
  img = img.convert('RGBA')
 
131
 
132
  # --- メイン処理ロジック (Main Processing Logic) ---
133
 
134
+ async def segment_and_inpaint(original_image_np: np.ndarray, sketchpad_image_np: np.ndarray, inpaint_prompt: str):
135
  """
136
  アップロードされた一枚絵とブラシで描かれたマスクを受け取り、
137
  マスクされた領域を分割し、欠損部分をAI補完する関数。
138
  """
139
+ if original_image_np is None:
140
+ return None, None, None, "エラー: 元の画像がアップロードされていません。", None
141
+ if sketchpad_image_np is None:
142
+ return None, None, None, "エラー: 分割したいパーツをブラシで描画してください。", None
143
 
 
 
 
 
144
  # PIL Image (元の画像) を取得
145
+ # NOTE: Gradioのデフォルト出力はNumPy配列
146
+ original_image = Image.fromarray(original_image_np).convert("RGBA")
147
+
148
+ # Sketchpadからの入力は、元の画像の上にブラシで描画されたRGBA画像 (描画部分は不透明)
149
+ # そのため、アルファチャンネルがマスクとして機能する
150
 
151
+ # Sketchpadの画像からアルファチャンネルを抽出(これがユーザーの描いたマスク)
152
+ sketch_mask_alpha = sketchpad_image_np[:, :, 3]
 
153
 
154
  # ユーザーが何も描画しなかった場合のチェック
155
  if np.all(sketch_mask_alpha == 0):
 
171
  # 反転マスクを体パーツのアルファチャンネルとして設定
172
  body_with_hole.putalpha(Image.fromarray(inverted_mask_np, mode='L'))
173
 
174
+ print("--- 1. 手動パーツ分割 (Gradio Sketchpad利用) 完了: 分割パーツと補完が必要な欠損体を作成 ---")
175
 
176
  # 2. 欠損領域のAI補完 (AI Inpainting)
177
  completed_body_part = await nano_banana_completion_api(body_with_hole, inpaint_prompt or "マスクされた領域のキャラクターの顔と身体の肌、及び下に着ている服を、元のイラストのテイストに合わせて自然に補完してください。")
 
242
  <div style='text-align: center; margin-bottom: 20px; padding: 10px; background: #E0F7FA; border-radius: 12px;'>
243
  <h1 style='color: #00796B; font-size: 2.5em; font-weight: 700;'>🎨 Live2D 素材 自動分割・補完アプリ 🤖</h1>
244
  <p style='color: #004D40; font-size: 1.1em;'>一枚絵をアップロードし、**ブラシでパーツを指定**することで、AIによる欠損部分の自動補完(Nano Banana API利用)を行います。</p>
245
+ <p style='color: #004D40; font-size: 1.0em;'>💡 **操作方法:** ①に元画像をアップロードし、②のキャンバスにその画像が表示された後、**ブラシ**を使って、前髪など**分割したいパーツ**を塗りつぶしてください。塗った部分がパーツとして分離され、AIがその下の欠損を補完します。</p>
246
  </div>
247
  """
248
  )
249
 
250
  with gr.Row():
251
  with gr.Column(scale=1):
252
+ # ★★★ 修正箇所1: 元画像をアップロードするためのコンポーネント (描画機能なし) ★★★
253
+ original_image_upload = gr.Image(
254
+ label="① 元画像のアップロード",
255
+ type="numpy", # NumPy配列として受け取る
256
+ height=200,
257
+ )
258
+ # ★★★ 修正箇所2: 描画専用のSketchpadコンポーネント ★★★
259
+ input_sketchpad = gr.Sketchpad(
260
+ label="② 分割したいパーツをブラシで塗る (例: 前髪)",
261
  height=400,
262
+ # Sketchpadにアップロードされた元画像を表示させるためのプレースホルダー
263
+ brush={"color": "#FF0000", "size": 20}, # ブラシ設定
264
+ )
265
+
266
+ # 描画キャンバスの背景をアップロード画像に設定するための処理
267
+ original_image_upload.change(
268
+ fn=lambda x: gr.update(image=x),
269
+ inputs=[original_image_upload],
270
+ outputs=[input_sketchpad],
271
+ queue=False
272
  )
273
+
274
  inpaint_prompt = gr.Textbox(
275
+ label=" AI補完プロンプト(オプション)",
276
  value="マスクされた領域のキャラクターの顔と身体の肌、及び下に着ている服を、元のイラストのテイストに合わせて自然に補完してください。",
277
  placeholder="例: マスクされた領域を元の絵柄で自然に描き足す"
278
  )
279
+ process_button = gr.Button(" 手動分割・AI補完を実行", variant="primary", scale=0)
280
 
281
  gr.Markdown(
282
  """
283
  ### 📌 開発のポイント
284
+ * **Gradio互換性対応:** 環境のGradioバージョンが低すぎる問題を回避するため、描画機能に特化した`gr.Sketchpad`を使用しました。
285
  """
286
  )
287
 
 
309
  # --- イベントリスナー ---
310
  process_button.click(
311
  fn=segment_and_inpaint,
312
+ inputs=[original_image_upload, input_sketchpad, inpaint_prompt], # 入力コンポーネントの順序を修正
313
  outputs=[
314
  completed_body_output,
315
  hair_part_output,