Spaces:
Running
on
Zero
Running
on
Zero
Update app.py
Browse files
app.py
CHANGED
|
@@ -97,6 +97,8 @@ def initialize_pipelines():
|
|
| 97 |
if upsampler is None and not UPSCALE_OK: # 一度失敗したら再試行しない
|
| 98 |
print("Checking for Upscaler...")
|
| 99 |
try:
|
|
|
|
|
|
|
| 100 |
from basicsr.archs.rrdb_arch import RRDBNet
|
| 101 |
from realesrgan import RealESRGAN
|
| 102 |
rrdb = RRDBNet(3, 3, 64, 23, 32, scale=8)
|
|
@@ -115,10 +117,8 @@ def initialize_pipelines():
|
|
| 115 |
BASE_PROMPT = ("(masterpiece:1.2), best quality, ultra-realistic, RAW photo, 8k,\n""photo of {subject},\n""cinematic lighting, golden hour, rim light, shallow depth of field,\n""textured skin, high detail, shot on Canon EOS R5, 85 mm f/1.4, ISO 200,\n""<lora:ip-adapter-faceid-plusv2_sd15_lora:0.65>, (face),\n""(aesthetic:1.1), (cinematic:0.8)")
|
| 116 |
NEG_PROMPT = ("ng_deepnegative_v1_75t, CyberRealistic_Negative-neg, UnrealisticDream, ""(worst quality:2), (low quality:1.8), lowres, (jpeg artifacts:1.2), ""painting, sketch, illustration, drawing, cartoon, anime, cgi, render, 3d, ""monochrome, grayscale, text, logo, watermark, signature, username, ""(MajicNegative_V2:0.8), bad hands, extra digits, fused fingers, malformed limbs, ""missing arms, missing legs, (badhandv4:0.7), BadNegAnatomyV1-neg, skin blemishes, acnes, age spot, glans")
|
| 117 |
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
@spaces.GPU(duration=60)
|
| 121 |
-
def _generate_core(face_img, subject, add_prompt, add_neg, cfg, ip_scale, steps, w, h, upscale, up_factor, progress=gr.Progress(track_tqdm=True)):
|
| 122 |
# 初回呼び出し時にパイプラインを初期化
|
| 123 |
initialize_pipelines()
|
| 124 |
|
|
@@ -131,18 +131,31 @@ def _generate_core(face_img, subject, add_prompt, add_neg, cfg, ip_scale, steps,
|
|
| 131 |
result = pipe(prompt=prompt, negative_prompt=neg, ip_adapter_image=face_img, image=face_img, controlnet_conditioning_scale=0.9, num_inference_steps=int(steps) + 5, guidance_scale=cfg, width=int(w), height=int(h)).images[0]
|
| 132 |
|
| 133 |
if upscale and UPSCALE_OK:
|
|
|
|
|
|
|
| 134 |
progress(0.8, desc="Upscaling...")
|
| 135 |
up, _ = upsampler.enhance(cv2.cvtColor(np.array(result), cv2.COLOR_RGB2BGR), outscale=up_factor)
|
| 136 |
result = Image.fromarray(cv2.cvtColor(up, cv2.COLOR_BGR2RGB))
|
| 137 |
|
| 138 |
return result
|
| 139 |
|
| 140 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
def generate_ui(face_np, subject, add_prompt, add_neg, cfg, ip_scale, steps, w, h, upscale, up_factor, progress=gr.Progress(track_tqdm=True)):
|
| 142 |
if face_np is None: raise gr.Error("顔画像をアップロードしてください。")
|
| 143 |
# NumPy配列をPillow画像に変換
|
| 144 |
face_img = Image.fromarray(face_np)
|
| 145 |
-
|
|
|
|
| 146 |
|
| 147 |
|
| 148 |
# 5. Gradio UI Definition
|
|
@@ -166,7 +179,6 @@ with gr.Blocks() as demo:
|
|
| 166 |
with gr.Column():
|
| 167 |
out_img = gr.Image(label="結果")
|
| 168 |
|
| 169 |
-
# .queue() はGradioの通常機能として必要
|
| 170 |
demo.queue()
|
| 171 |
|
| 172 |
btn.click(
|
|
@@ -178,7 +190,7 @@ with gr.Blocks() as demo:
|
|
| 178 |
# 6. FastAPI Mounting
|
| 179 |
app = FastAPI()
|
| 180 |
|
| 181 |
-
# FastAPI
|
| 182 |
@app.post("/api/predict")
|
| 183 |
async def predict_endpoint(
|
| 184 |
face_image: UploadFile = File(...),
|
|
@@ -197,8 +209,8 @@ async def predict_endpoint(
|
|
| 197 |
contents = await face_image.read()
|
| 198 |
pil_image = Image.open(io.BytesIO(contents))
|
| 199 |
|
| 200 |
-
#
|
| 201 |
-
result_pil_image =
|
| 202 |
pil_image, subject, add_prompt, add_neg, cfg, ip_scale,
|
| 203 |
steps, w, h, upscale, up_factor
|
| 204 |
)
|
|
@@ -217,5 +229,24 @@ app = gr.mount_gradio_app(app, demo, path="/")
|
|
| 217 |
|
| 218 |
print("Application startup script finished. Waiting for requests.")
|
| 219 |
if __name__ == "__main__":
|
| 220 |
-
import uvicorn
|
| 221 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
if upsampler is None and not UPSCALE_OK: # 一度失敗したら再試行しない
|
| 98 |
print("Checking for Upscaler...")
|
| 99 |
try:
|
| 100 |
+
# cv2のインポートをここに追加
|
| 101 |
+
import cv2
|
| 102 |
from basicsr.archs.rrdb_arch import RRDBNet
|
| 103 |
from realesrgan import RealESRGAN
|
| 104 |
rrdb = RRDBNet(3, 3, 64, 23, 32, scale=8)
|
|
|
|
| 117 |
BASE_PROMPT = ("(masterpiece:1.2), best quality, ultra-realistic, RAW photo, 8k,\n""photo of {subject},\n""cinematic lighting, golden hour, rim light, shallow depth of field,\n""textured skin, high detail, shot on Canon EOS R5, 85 mm f/1.4, ISO 200,\n""<lora:ip-adapter-faceid-plusv2_sd15_lora:0.65>, (face),\n""(aesthetic:1.1), (cinematic:0.8)")
|
| 118 |
NEG_PROMPT = ("ng_deepnegative_v1_75t, CyberRealistic_Negative-neg, UnrealisticDream, ""(worst quality:2), (low quality:1.8), lowres, (jpeg artifacts:1.2), ""painting, sketch, illustration, drawing, cartoon, anime, cgi, render, 3d, ""monochrome, grayscale, text, logo, watermark, signature, username, ""(MajicNegative_V2:0.8), bad hands, extra digits, fused fingers, malformed limbs, ""missing arms, missing legs, (badhandv4:0.7), BadNegAnatomyV1-neg, skin blemishes, acnes, age spot, glans")
|
| 119 |
|
| 120 |
+
# 【変更点①】内部的な画像生成関数。@spaces.GPUデコレータを外す
|
| 121 |
+
def _generate_internal(face_img, subject, add_prompt, add_neg, cfg, ip_scale, steps, w, h, upscale, up_factor, progress=gr.Progress(track_tqdm=True)):
|
|
|
|
|
|
|
| 122 |
# 初回呼び出し時にパイプラインを初期化
|
| 123 |
initialize_pipelines()
|
| 124 |
|
|
|
|
| 131 |
result = pipe(prompt=prompt, negative_prompt=neg, ip_adapter_image=face_img, image=face_img, controlnet_conditioning_scale=0.9, num_inference_steps=int(steps) + 5, guidance_scale=cfg, width=int(w), height=int(h)).images[0]
|
| 132 |
|
| 133 |
if upscale and UPSCALE_OK:
|
| 134 |
+
# cv2のインポートをここにも追加
|
| 135 |
+
import cv2
|
| 136 |
progress(0.8, desc="Upscaling...")
|
| 137 |
up, _ = upsampler.enhance(cv2.cvtColor(np.array(result), cv2.COLOR_RGB2BGR), outscale=up_factor)
|
| 138 |
result = Image.fromarray(cv2.cvtColor(up, cv2.COLOR_BGR2RGB))
|
| 139 |
|
| 140 |
return result
|
| 141 |
|
| 142 |
+
# 【変更点②】@spaces.GPUデコレータを持つ新しいラッパー関数を定義
|
| 143 |
+
@spaces.GPU(duration=60)
|
| 144 |
+
def generate_gpu_wrapper(face_img, subject, add_prompt, add_neg, cfg, ip_scale, steps, w, h, upscale, up_factor, progress=gr.Progress(track_tqdm=True)):
|
| 145 |
+
"""
|
| 146 |
+
Hugging Face SpacesプラットフォームにGPUを要求するためのラッパー関数。
|
| 147 |
+
実際の処理は _generate_internal を呼び出して実行する。
|
| 148 |
+
"""
|
| 149 |
+
return _generate_internal(face_img, subject, add_prompt, add_neg, cfg, ip_scale, steps, w, h, upscale, up_factor, progress)
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
# 【変更点③】GradioのUIから新しいラッパー関数を呼び出すように変更
|
| 153 |
def generate_ui(face_np, subject, add_prompt, add_neg, cfg, ip_scale, steps, w, h, upscale, up_factor, progress=gr.Progress(track_tqdm=True)):
|
| 154 |
if face_np is None: raise gr.Error("顔画像をアップロードしてください。")
|
| 155 |
# NumPy配列をPillow画像に変換
|
| 156 |
face_img = Image.fromarray(face_np)
|
| 157 |
+
# _generate_coreの代わりにgenerate_gpu_wrapperを呼び出す
|
| 158 |
+
return generate_gpu_wrapper(face_img, subject, add_prompt, add_neg, cfg, ip_scale, steps, w, h, upscale, up_factor, progress)
|
| 159 |
|
| 160 |
|
| 161 |
# 5. Gradio UI Definition
|
|
|
|
| 179 |
with gr.Column():
|
| 180 |
out_img = gr.Image(label="結果")
|
| 181 |
|
|
|
|
| 182 |
demo.queue()
|
| 183 |
|
| 184 |
btn.click(
|
|
|
|
| 190 |
# 6. FastAPI Mounting
|
| 191 |
app = FastAPI()
|
| 192 |
|
| 193 |
+
# 【変更点④】FastAPIのエンドポイントも新しいラッパー関数を呼び出すように変更
|
| 194 |
@app.post("/api/predict")
|
| 195 |
async def predict_endpoint(
|
| 196 |
face_image: UploadFile = File(...),
|
|
|
|
| 209 |
contents = await face_image.read()
|
| 210 |
pil_image = Image.open(io.BytesIO(contents))
|
| 211 |
|
| 212 |
+
# _generate_coreの代わりにgenerate_gpu_wrapperを呼び出す
|
| 213 |
+
result_pil_image = generate_gpu_wrapper(
|
| 214 |
pil_image, subject, add_prompt, add_neg, cfg, ip_scale,
|
| 215 |
steps, w, h, upscale, up_factor
|
| 216 |
)
|
|
|
|
| 229 |
|
| 230 |
print("Application startup script finished. Waiting for requests.")
|
| 231 |
if __name__ == "__main__":
|
| 232 |
+
import os, time, socket, uvicorn
|
| 233 |
+
|
| 234 |
+
def port_is_free(port: int) -> bool:
|
| 235 |
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
| 236 |
+
return s.connect_ex(("0.0.0.0", port)) != 0
|
| 237 |
+
|
| 238 |
+
port = int(os.getenv("PORT", 7860))
|
| 239 |
+
# ローカルでのテスト用にタイムアウトを短縮
|
| 240 |
+
# timeout_sec = 30
|
| 241 |
+
# poll_interval = 2
|
| 242 |
+
#
|
| 243 |
+
# t0 = time.time()
|
| 244 |
+
# while not port_is_free(port):
|
| 245 |
+
# waited = time.time() - t0
|
| 246 |
+
# if waited >= timeout_sec:
|
| 247 |
+
# raise RuntimeError(f"Port {port} is still busy after {timeout_sec}s")
|
| 248 |
+
# print(f"⚠️ Port {port} busy, retrying in {poll_interval}s …")
|
| 249 |
+
# time.sleep(poll_interval)
|
| 250 |
+
|
| 251 |
+
# Hugging Face Spaces環境ではポートの競合は起こりにくいため、ポートチェックロジックを簡略化・無効化
|
| 252 |
+
uvicorn.run(app, host="0.0.0.0", port=port, workers=1, log_level="info")
|