Slicelayers commited on
Commit
0914dea
·
verified ·
1 Parent(s): bfea3f5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +31 -18
app.py CHANGED
@@ -120,10 +120,12 @@ async def nano_banana_completion_api(base_image: Image.Image, prompt: str) -> Im
120
  await asyncio.sleep(delay)
121
  delay *= 2 # 指数バックオフ
122
  else:
 
123
  raise gr.Error(f"API呼び出しエラー: Client error '{e.response.status_code} {e.response.reason_phrase}' for url '{e.request.url}'")
124
  except httpx.RequestError as e:
125
  raise gr.Error(f"APIリクエストエラー: ネットワークまたはタイムアウトエラーが発生しました。詳細: {e}")
126
  except Exception as e:
 
127
  raise gr.Error(f"予期せぬエラーが発生しました: {e}")
128
 
129
  raise gr.Error("APIリクエストが最大リトライ回数を超えて失敗しました。")
@@ -135,30 +137,43 @@ async def segment_and_inpaint(original_image_np: np.ndarray, sketchpad_image_np:
135
  """
136
  アップロードされた一枚絵とブラシで描かれたマスクを受け取り、
137
  マスクされた領域を分割し、欠損部分をAI補完する関数。
138
- (出力を6つにし、エラーメッセージを分離してorjsonエラーを回避)
139
  """
140
  # 失敗時の統一リターン(6つの出力に対応)
141
  def fail(message):
142
- return None, None, None, None, None, f"❌ 処理失敗: {message}"
 
143
 
144
  if original_image_np is None:
145
  return fail("元の画像がアップロードされていません。")
146
 
147
- # Sketchpad入力の型チェックを強化(TypeError対策)
148
- if not isinstance(sketchpad_image_np, np.ndarray) or sketchpad_image_np.ndim != 4:
149
- return fail("分割したいパーツをブラシで描画してください。描画キャンバスが空または不正な形式です。")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
  # PIL Image (元の画像) を取得
152
  original_image = Image.fromarray(original_image_np).convert("RGBA")
153
 
154
  # Sketchpadからの入力は、元の画像の上にブラシで描画されたRGBA画像
155
- try:
156
- # Sketchpadの画像からアルファチャンネルを抽出(これがユーザーの描いたマスク)
157
- sketch_mask_alpha = sketchpad_image_np[:, :, 3]
158
- except IndexError:
159
- return fail("描画データ形式が不正です。RGBA (4チャンネル) の画像として処理できませんでした。")
160
 
161
- # ユーザーが何も描画しなかった場合のチェック
 
162
  if np.all(sketch_mask_alpha == 0):
163
  return fail("分割したいパーツをブラシで描画してください。(描画が検出されません)")
164
 
@@ -182,7 +197,6 @@ async def segment_and_inpaint(original_image_np: np.ndarray, sketchpad_image_np:
182
  try:
183
  completed_body_part = await nano_banana_completion_api(body_with_hole, inpaint_prompt or "マスクされた領域のキャラクターの顔と身体の肌、及び下に着ている服を、元のイラストのテイストに合わせて自然に補完してください。")
184
  except gr.Error as e:
185
- # APIエラーはここで捕まえる
186
  return fail(f"AI補完APIエラー: {e}")
187
  except Exception as e:
188
  return fail(f"AI補完処理中に予期せぬエラー: {e}")
@@ -265,12 +279,12 @@ with gr.Blocks(theme=theme, title="Live2D素材自動分割・補完アプリ")
265
  type="numpy",
266
  height=200,
267
  )
268
- # 描画専用のSketchpadコンポーネント (type="numpy"に戻し、Python側で堅牢にチェック)
269
  input_sketchpad = gr.Sketchpad(
270
  label="② 分割したいパーツをブラシで塗る (例: 前髪)",
271
  height=400,
272
  brush={"color": "#FF0000", "size": 20},
273
- type="numpy" # ★★★ 修正: numpyに戻す ★★★
274
  )
275
 
276
  inpaint_prompt = gr.Textbox(
@@ -283,8 +297,7 @@ with gr.Blocks(theme=theme, title="Live2D素材自動分割・補完アプリ")
283
  gr.Markdown(
284
  """
285
  ### 📌 開発のポイント
286
- * **安定性の向上:** 発生していた型の不一致エラー(`slice`や`read`)を回避するため、Gradioの出力をNumPy配列に統一し、入力チェックを強化しました。
287
- * **エラー表示の改善:** 処理ステータスを専用のテキストボックスに出力するように変更し、JSONデコードエラーを防いでいます。
288
  """
289
  )
290
 
@@ -318,9 +331,9 @@ with gr.Blocks(theme=theme, title="Live2D素材自動分割・補完アプリ")
318
  completed_body_output,
319
  hair_part_output,
320
  body_with_hole_output,
321
- output_json_text, # JSON出力をテキストボックスに変更
322
  download_zip,
323
- status_message # ステータスメッセージを追加
324
  ]
325
  )
326
 
 
120
  await asyncio.sleep(delay)
121
  delay *= 2 # 指数バックオフ
122
  else:
123
+ # ユーザーへの表示用にgr.Errorを再raise
124
  raise gr.Error(f"API呼び出しエラー: Client error '{e.response.status_code} {e.response.reason_phrase}' for url '{e.request.url}'")
125
  except httpx.RequestError as e:
126
  raise gr.Error(f"APIリクエストエラー: ネットワークまたはタイムアウトエラーが発生しました。詳細: {e}")
127
  except Exception as e:
128
+ # その他の予期せぬエラー
129
  raise gr.Error(f"予期せぬエラーが発生しました: {e}")
130
 
131
  raise gr.Error("APIリクエストが最大リトライ回数を超えて失敗しました。")
 
137
  """
138
  アップロードされた一枚絵とブラシで描かれたマスクを受け取り、
139
  マスクされた領域を分割し、欠損部分をAI補完する関数。
 
140
  """
141
  # 失敗時の統一リターン(6つの出力に対応)
142
  def fail(message):
143
+ # 画像出力は全てNone, JSONとZIPは空文字列/None, ステータスにエラーメッセージ
144
+ return None, None, None, "{}", None, f"❌ 処理失敗: {message}"
145
 
146
  if original_image_np is None:
147
  return fail("元の画像がアップロードされていません。")
148
 
149
+ # ★★★ 修正: Sketchpad入力の型チェックと次元調整の強化 ★★★
150
+
151
+ if not isinstance(sketchpad_image_np, np.ndarray):
152
+ # NumPy配列ではない(NoneやDictなど)場合は、描画がないと判断
153
+ return fail("分割したいパーツをブラシで描画してください。(描画データが検出されません)")
154
+
155
+ if sketchpad_image_np.ndim == 3 and sketchpad_image_np.shape[2] == 3:
156
+ # 3次元配列(RGB)の場合、透明なアルファチャンネルを追加して4次元(RGBA)に変換
157
+ print("--- SketchpadデータがRGB (3次元) で渡されました。RGBA (4次元) に変換します。 ---")
158
+ h, w, _ = sketchpad_image_np.shape
159
+ # 全て透明(0)のアルファチャンネルを作成
160
+ alpha_channel = np.zeros((h, w, 1), dtype=np.uint8)
161
+ # RGB配列とアルファチャンネルを結合
162
+ sketchpad_image_np = np.concatenate((sketchpad_image_np, alpha_channel), axis=2)
163
+
164
+ elif sketchpad_image_np.ndim != 4 or sketchpad_image_np.shape[2] != 4:
165
+ # 4次元配列ではない、または4チャンネルではない場合は不正と判断
166
+ return fail(f"描画データ形式が不正です。(次元: {sketchpad_image_np.ndim}, チャンネル: {sketchpad_image_np.shape[2] if sketchpad_image_np.ndim >= 3 else 'N/A'})。")
167
 
168
  # PIL Image (元の画像) を取得
169
  original_image = Image.fromarray(original_image_np).convert("RGBA")
170
 
171
  # Sketchpadからの入力は、元の画像の上にブラシで描画されたRGBA画像
172
+ # アルファチャンネルからマスクを抽出
173
+ sketch_mask_alpha = sketchpad_image_np[:, :, 3]
 
 
 
174
 
175
+ # ユーザーが何も描画しなかった場合のチェック (アルファチャンネルが全てゼロ)
176
+ # ブラシで描画した場合、アルファチャンネルは255の領域を持つはず
177
  if np.all(sketch_mask_alpha == 0):
178
  return fail("分割したいパーツをブラシで描画してください。(描画が検出されません)")
179
 
 
197
  try:
198
  completed_body_part = await nano_banana_completion_api(body_with_hole, inpaint_prompt or "マスクされた領域のキャラクターの顔と身体の肌、及び下に着ている服を、元のイラストのテイストに合わせて自然に補完してください。")
199
  except gr.Error as e:
 
200
  return fail(f"AI補完APIエラー: {e}")
201
  except Exception as e:
202
  return fail(f"AI補完処理中に予期せぬエラー: {e}")
 
279
  type="numpy",
280
  height=200,
281
  )
282
+ # 描画専用のSketchpadコンポーネント (type="numpy")
283
  input_sketchpad = gr.Sketchpad(
284
  label="② 分割したいパーツをブラシで塗る (例: 前髪)",
285
  height=400,
286
  brush={"color": "#FF0000", "size": 20},
287
+ type="numpy"
288
  )
289
 
290
  inpaint_prompt = gr.Textbox(
 
297
  gr.Markdown(
298
  """
299
  ### 📌 開発のポイント
300
+ * **安定性の向上:** Gradioの入力データが予期せず3次元配列で渡された場合にも、アルファチャンネルを追加して処理を継続できるようにロジックを強化しました。
 
301
  """
302
  )
303
 
 
331
  completed_body_output,
332
  hair_part_output,
333
  body_with_hole_output,
334
+ output_json_text,
335
  download_zip,
336
+ status_message
337
  ]
338
  )
339