Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -139,19 +139,22 @@ async def segment_and_inpaint(original_image_np: np.ndarray, sketchpad_image_np:
|
|
| 139 |
"""
|
| 140 |
アップロードされた一枚絵とブラシで描かれたマスクを受け取り、
|
| 141 |
マスクされた領域を分割し、欠損部分をAI補完する関数。
|
|
|
|
| 142 |
"""
|
| 143 |
-
# 失敗時の統一リターン
|
| 144 |
def fail(message):
|
| 145 |
# 画像出力は全てNone, JSONとZIPは空文字列/None, ステータスにエラーメッセージ
|
| 146 |
-
return None, None, None, "{}", None, f"❌ 処理失敗: {message}"
|
| 147 |
|
| 148 |
if original_image_np is None:
|
| 149 |
-
return
|
| 150 |
-
|
|
|
|
|
|
|
| 151 |
# 元画像の形状を取得(SketchpadがNoneだった場合の代替用)
|
| 152 |
h, w, _ = original_image_np.shape
|
| 153 |
|
| 154 |
-
# ★★★
|
| 155 |
|
| 156 |
if not isinstance(sketchpad_image_np, np.ndarray) or sketchpad_image_np is None:
|
| 157 |
# Sketchpadの入力がNumPy配列ではないか、Noneであった場合、
|
|
@@ -169,8 +172,9 @@ async def segment_and_inpaint(original_image_np: np.ndarray, sketchpad_image_np:
|
|
| 169 |
|
| 170 |
elif sketchpad_image_np.ndim != 4 or sketchpad_image_np.shape[2] != 4:
|
| 171 |
# 4次元配列ではない、または4チャンネルではない場合は不正と判断
|
| 172 |
-
#
|
| 173 |
-
|
|
|
|
| 174 |
|
| 175 |
# PIL Image (元の画像) を取得
|
| 176 |
original_image = Image.fromarray(original_image_np).convert("RGBA")
|
|
@@ -180,10 +184,11 @@ async def segment_and_inpaint(original_image_np: np.ndarray, sketchpad_image_np:
|
|
| 180 |
sketch_mask_alpha = sketchpad_image_np[:, :, 3]
|
| 181 |
|
| 182 |
# ユーザーが何も描画しなかった場合のチェック (アルファチャンネルが全てゼロ)
|
| 183 |
-
# ブラシで描画した場合、アルファチャンネルは255の領域を持つはず
|
| 184 |
if np.all(sketch_mask_alpha == 0):
|
| 185 |
# SketchpadがNoneの場合もここに来る
|
| 186 |
-
return
|
|
|
|
|
|
|
| 187 |
|
| 188 |
# NumPy配列からPIL Imageのマスクに変換 (Lモード)
|
| 189 |
hair_mask = Image.fromarray(sketch_mask_alpha, mode='L')
|
|
@@ -211,9 +216,13 @@ async def segment_and_inpaint(original_image_np: np.ndarray, sketchpad_image_np:
|
|
| 211 |
inpaint_prompt or "マスクされた領域のキャラクターの顔と身体の肌、及び下に着ている服を、元のイラストのテイストに合わせて自然に補完してください。"
|
| 212 |
)
|
| 213 |
except gr.Error as e:
|
| 214 |
-
|
|
|
|
|
|
|
| 215 |
except Exception as e:
|
| 216 |
-
|
|
|
|
|
|
|
| 217 |
|
| 218 |
# 3. 出力ファイルの準備とZIPファイルの作成
|
| 219 |
output_parts = {
|
|
@@ -236,7 +245,7 @@ async def segment_and_inpaint(original_image_np: np.ndarray, sketchpad_image_np:
|
|
| 236 |
zip_buffer = io.BytesIO()
|
| 237 |
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
| 238 |
for name, img in output_parts.items():
|
| 239 |
-
img_buffer = io.
|
| 240 |
if img.mode != 'RGBA':
|
| 241 |
img = img.convert('RGBA')
|
| 242 |
img.save(img_buffer, format="PNG")
|
|
@@ -252,8 +261,8 @@ async def segment_and_inpaint(original_image_np: np.ndarray, sketchpad_image_np:
|
|
| 252 |
|
| 253 |
print(f"--- 4. 全パーツZIPファイル {zip_file_path} の作成完了 ---")
|
| 254 |
|
| 255 |
-
# 成功時の
|
| 256 |
-
|
| 257 |
completed_body_part,
|
| 258 |
hair_part,
|
| 259 |
body_with_hole,
|
|
@@ -311,8 +320,8 @@ with gr.Blocks(theme=theme, title="Live2D素材自動分割・補完アプリ")
|
|
| 311 |
gr.Markdown(
|
| 312 |
"""
|
| 313 |
### 📌 開発のポイント
|
| 314 |
-
* **
|
| 315 |
-
* **
|
| 316 |
"""
|
| 317 |
)
|
| 318 |
|
|
|
|
| 139 |
"""
|
| 140 |
アップロードされた一枚絵とブラシで描かれたマスクを受け取り、
|
| 141 |
マスクされた領域を分割し、欠損部分をAI補完する関数。
|
| 142 |
+
(この関数はasync generatorとして動作し、yieldで結果を返します)
|
| 143 |
"""
|
| 144 |
+
# 失敗時の統一リターン用タプルを生成
|
| 145 |
def fail(message):
|
| 146 |
# 画像出力は全てNone, JSONとZIPは空文字列/None, ステータスにエラーメッセージ
|
| 147 |
+
return (None, None, None, "{}", None, f"❌ 処理失敗: {message}")
|
| 148 |
|
| 149 |
if original_image_np is None:
|
| 150 |
+
# 修正: return -> yield
|
| 151 |
+
yield fail("元の画像がアップロードされていません。")
|
| 152 |
+
return # Generatorを終了
|
| 153 |
+
|
| 154 |
# 元画像の形状を取得(SketchpadがNoneだった場合の代替用)
|
| 155 |
h, w, _ = original_image_np.shape
|
| 156 |
|
| 157 |
+
# ★★★ Sketchpad入力の型チェックとデータ形式の強制 ★★★
|
| 158 |
|
| 159 |
if not isinstance(sketchpad_image_np, np.ndarray) or sketchpad_image_np is None:
|
| 160 |
# Sketchpadの入力がNumPy配列ではないか、Noneであった場合、
|
|
|
|
| 172 |
|
| 173 |
elif sketchpad_image_np.ndim != 4 or sketchpad_image_np.shape[2] != 4:
|
| 174 |
# 4次元配列ではない、または4チャンネルではない場合は不正と判断
|
| 175 |
+
# 修正: return -> yield
|
| 176 |
+
yield fail(f"描画データ形式が不正です。(次元: {sketchpad_image_np.ndim}, チャンネル: {sketchpad_image_np.shape[2] if sketchpad_image_np.ndim >= 3 else 'N/A'})。")
|
| 177 |
+
return # Generatorを終了
|
| 178 |
|
| 179 |
# PIL Image (元の画像) を取得
|
| 180 |
original_image = Image.fromarray(original_image_np).convert("RGBA")
|
|
|
|
| 184 |
sketch_mask_alpha = sketchpad_image_np[:, :, 3]
|
| 185 |
|
| 186 |
# ユーザーが何も描画しなかった場合のチェック (アルファチャンネルが全てゼロ)
|
|
|
|
| 187 |
if np.all(sketch_mask_alpha == 0):
|
| 188 |
# SketchpadがNoneの場合もここに来る
|
| 189 |
+
# 修正: return -> yield
|
| 190 |
+
yield fail("分割したいパーツをブラシで描画してください。(描画が検出されません)")
|
| 191 |
+
return # Generatorを終了
|
| 192 |
|
| 193 |
# NumPy配列からPIL Imageのマスクに変換 (Lモード)
|
| 194 |
hair_mask = Image.fromarray(sketch_mask_alpha, mode='L')
|
|
|
|
| 216 |
inpaint_prompt or "マスクされた領域のキャラクターの顔と身体の肌、及び下に着ている服を、元のイラストのテイストに合わせて自然に補完してください。"
|
| 217 |
)
|
| 218 |
except gr.Error as e:
|
| 219 |
+
# 修正: return -> yield
|
| 220 |
+
yield fail(f"AI補完APIエラー: {e}")
|
| 221 |
+
return # Generatorを終了
|
| 222 |
except Exception as e:
|
| 223 |
+
# 修正: return -> yield
|
| 224 |
+
yield fail(f"AI補完処理中に予期せぬエラー: {e}")
|
| 225 |
+
return # Generatorを終了
|
| 226 |
|
| 227 |
# 3. 出力ファイルの準備とZIPファイルの作成
|
| 228 |
output_parts = {
|
|
|
|
| 245 |
zip_buffer = io.BytesIO()
|
| 246 |
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
| 247 |
for name, img in output_parts.items():
|
| 248 |
+
img_buffer = io.BytesIO()
|
| 249 |
if img.mode != 'RGBA':
|
| 250 |
img = img.convert('RGBA')
|
| 251 |
img.save(img_buffer, format="PNG")
|
|
|
|
| 261 |
|
| 262 |
print(f"--- 4. 全パーツZIPファイル {zip_file_path} の作成完了 ---")
|
| 263 |
|
| 264 |
+
# 成功時の最終結果をyield (6つの出力)
|
| 265 |
+
yield (
|
| 266 |
completed_body_part,
|
| 267 |
hair_part,
|
| 268 |
body_with_hole,
|
|
|
|
| 320 |
gr.Markdown(
|
| 321 |
"""
|
| 322 |
### 📌 開発のポイント
|
| 323 |
+
* **エラー解消:** `async def` 関数内で `yield` を使用する場合の `SyntaxError` を解消しました。これにより、処理ステータスが正しくフィードバックされるようになります。
|
| 324 |
+
* **安定性の再強化:** Sketchpadの入力データ型チェックを引き続き強化しています。
|
| 325 |
"""
|
| 326 |
)
|
| 327 |
|