Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -61,11 +61,10 @@ def _patch_gradio_client_bool_schema():
|
|
| 61 |
except Exception as e:
|
| 62 |
print("gradio_client patch failed:", repr(e), flush=True)
|
| 63 |
|
| 64 |
-
_patch_gradio_client_bool_schema()
|
| 65 |
|
|
|
|
| 66 |
|
| 67 |
import torch
|
| 68 |
-
import numpy as np
|
| 69 |
from torchvision import transforms
|
| 70 |
|
| 71 |
from huggingface_hub import login, snapshot_download
|
|
@@ -185,6 +184,8 @@ def clamp_int(x, lo, hi):
|
|
| 185 |
|
| 186 |
|
| 187 |
_last_call_ts = 0.0
|
|
|
|
|
|
|
| 188 |
def allow_call(min_interval_sec: float = 2.5) -> Tuple[bool, str]:
|
| 189 |
global _last_call_ts
|
| 190 |
now = time.time()
|
|
@@ -261,7 +262,6 @@ def start_tryon(
|
|
| 261 |
denoise_steps: int = 25,
|
| 262 |
seed: int = 42,
|
| 263 |
) -> Image.Image:
|
| 264 |
-
|
| 265 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 266 |
dtype = torch.float16 if device == "cuda" else torch.float32
|
| 267 |
|
|
@@ -302,16 +302,18 @@ def start_tryon(
|
|
| 302 |
human_img_arg = _apply_exif_orientation(human_img.resize((384, 512)))
|
| 303 |
human_img_arg = convert_PIL_to_numpy(human_img_arg, format="BGR")
|
| 304 |
|
| 305 |
-
args = apply_net.create_argument_parser().parse_args(
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
|
|
|
|
|
|
| 315 |
pose_img = args.func(args, human_img_arg)
|
| 316 |
pose_img = pose_img[:, :, ::-1]
|
| 317 |
pose_img = Image.fromarray(pose_img).resize((768, 1024))
|
|
@@ -329,9 +331,14 @@ def start_tryon(
|
|
| 329 |
if device == "cuda":
|
| 330 |
autocast_ctx = torch.cuda.amp.autocast()
|
| 331 |
else:
|
|
|
|
| 332 |
class _NoCtx:
|
| 333 |
-
def __enter__(self):
|
| 334 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
autocast_ctx = _NoCtx()
|
| 336 |
|
| 337 |
with autocast_ctx:
|
|
@@ -392,7 +399,7 @@ def start_tryon(
|
|
| 392 |
|
| 393 |
|
| 394 |
# =========================
|
| 395 |
-
# UI
|
| 396 |
# =========================
|
| 397 |
CUSTOM_CSS = """
|
| 398 |
footer {display:none !important;}
|
|
@@ -401,6 +408,18 @@ div[class*="footer"] {display:none !important;}
|
|
| 401 |
button[aria-label="Settings"] {display:none !important;}
|
| 402 |
"""
|
| 403 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 404 |
def refresh_catalog():
|
| 405 |
ensure_garments_downloaded()
|
| 406 |
files = list_garments()
|
|
@@ -408,6 +427,7 @@ def refresh_catalog():
|
|
| 408 |
status = "✅ Каталог обновлён" if files else "⚠️ Каталог пуст (проверь dataset/токен)"
|
| 409 |
return items, files, None, status
|
| 410 |
|
|
|
|
| 411 |
def on_gallery_select(files_list: List[str], evt: gr.SelectData):
|
| 412 |
if not files_list:
|
| 413 |
return None, "⚠️ Каталог пуст"
|
|
@@ -415,6 +435,7 @@ def on_gallery_select(files_list: List[str], evt: gr.SelectData):
|
|
| 415 |
idx = max(0, min(idx, len(files_list) - 1))
|
| 416 |
return files_list[idx], f"👕 Выбрано: {files_list[idx]}"
|
| 417 |
|
|
|
|
| 418 |
def tryon_ui(person_pil, selected_filename):
|
| 419 |
yield None, "⏳ Обработка... (первый запуск может быть дольше)"
|
| 420 |
|
|
@@ -436,7 +457,7 @@ def tryon_ui(person_pil, selected_filename):
|
|
| 436 |
return
|
| 437 |
|
| 438 |
try:
|
| 439 |
-
|
| 440 |
human_pil=person_pil,
|
| 441 |
garm_img=garm,
|
| 442 |
auto_mask=True,
|
|
@@ -444,7 +465,7 @@ def tryon_ui(person_pil, selected_filename):
|
|
| 444 |
denoise_steps=25,
|
| 445 |
seed=42,
|
| 446 |
)
|
| 447 |
-
yield
|
| 448 |
except Exception as e:
|
| 449 |
yield None, f"❌ Ошибка: {type(e).__name__}: {str(e)[:220]}"
|
| 450 |
|
|
@@ -464,6 +485,9 @@ with gr.Blocks(title="Virtual Try-On Rendez-vous", css=CUSTOM_CSS) as demo:
|
|
| 464 |
with gr.Column():
|
| 465 |
person = gr.Image(label="Фото человека", type="pil", height=420)
|
| 466 |
|
|
|
|
|
|
|
|
|
|
| 467 |
with gr.Row():
|
| 468 |
refresh_btn = gr.Button("🔄 Обновить каталог одежды", variant="secondary")
|
| 469 |
selected_label = gr.Markdown("👕 Выберите одежду ниже")
|
|
|
|
| 61 |
except Exception as e:
|
| 62 |
print("gradio_client patch failed:", repr(e), flush=True)
|
| 63 |
|
|
|
|
| 64 |
|
| 65 |
+
_patch_gradio_client_bool_schema()
|
| 66 |
|
| 67 |
import torch
|
|
|
|
| 68 |
from torchvision import transforms
|
| 69 |
|
| 70 |
from huggingface_hub import login, snapshot_download
|
|
|
|
| 184 |
|
| 185 |
|
| 186 |
_last_call_ts = 0.0
|
| 187 |
+
|
| 188 |
+
|
| 189 |
def allow_call(min_interval_sec: float = 2.5) -> Tuple[bool, str]:
|
| 190 |
global _last_call_ts
|
| 191 |
now = time.time()
|
|
|
|
| 262 |
denoise_steps: int = 25,
|
| 263 |
seed: int = 42,
|
| 264 |
) -> Image.Image:
|
|
|
|
| 265 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 266 |
dtype = torch.float16 if device == "cuda" else torch.float32
|
| 267 |
|
|
|
|
| 302 |
human_img_arg = _apply_exif_orientation(human_img.resize((384, 512)))
|
| 303 |
human_img_arg = convert_PIL_to_numpy(human_img_arg, format="BGR")
|
| 304 |
|
| 305 |
+
args = apply_net.create_argument_parser().parse_args(
|
| 306 |
+
(
|
| 307 |
+
"show",
|
| 308 |
+
"./configs/densepose_rcnn_R_50_FPN_s1x.yaml",
|
| 309 |
+
"./ckpt/densepose/model_final_162be9.pkl",
|
| 310 |
+
"dp_segm",
|
| 311 |
+
"-v",
|
| 312 |
+
"--opts",
|
| 313 |
+
"MODEL.DEVICE",
|
| 314 |
+
"cuda" if device == "cuda" else "cpu",
|
| 315 |
+
)
|
| 316 |
+
)
|
| 317 |
pose_img = args.func(args, human_img_arg)
|
| 318 |
pose_img = pose_img[:, :, ::-1]
|
| 319 |
pose_img = Image.fromarray(pose_img).resize((768, 1024))
|
|
|
|
| 331 |
if device == "cuda":
|
| 332 |
autocast_ctx = torch.cuda.amp.autocast()
|
| 333 |
else:
|
| 334 |
+
|
| 335 |
class _NoCtx:
|
| 336 |
+
def __enter__(self):
|
| 337 |
+
return None
|
| 338 |
+
|
| 339 |
+
def __exit__(self, *args):
|
| 340 |
+
return False
|
| 341 |
+
|
| 342 |
autocast_ctx = _NoCtx()
|
| 343 |
|
| 344 |
with autocast_ctx:
|
|
|
|
| 399 |
|
| 400 |
|
| 401 |
# =========================
|
| 402 |
+
# UI
|
| 403 |
# =========================
|
| 404 |
CUSTOM_CSS = """
|
| 405 |
footer {display:none !important;}
|
|
|
|
| 408 |
button[aria-label="Settings"] {display:none !important;}
|
| 409 |
"""
|
| 410 |
|
| 411 |
+
PHOTO_TIPS_MD = """
|
| 412 |
+
### Какое фото подойдёт
|
| 413 |
+
|
| 414 |
+
✅ В полный рост или по пояс
|
| 415 |
+
✅ Руки и предметы не закрывают тело
|
| 416 |
+
✅ Одежда по фигуре
|
| 417 |
+
✅ Вы стоите прямо и смотрите в камеру
|
| 418 |
+
✅ Хорошее освещение
|
| 419 |
+
✅ В кадре нет других людей
|
| 420 |
+
"""
|
| 421 |
+
|
| 422 |
+
|
| 423 |
def refresh_catalog():
|
| 424 |
ensure_garments_downloaded()
|
| 425 |
files = list_garments()
|
|
|
|
| 427 |
status = "✅ Каталог обновлён" if files else "⚠️ Каталог пуст (проверь dataset/токен)"
|
| 428 |
return items, files, None, status
|
| 429 |
|
| 430 |
+
|
| 431 |
def on_gallery_select(files_list: List[str], evt: gr.SelectData):
|
| 432 |
if not files_list:
|
| 433 |
return None, "⚠️ Каталог пуст"
|
|
|
|
| 435 |
idx = max(0, min(idx, len(files_list) - 1))
|
| 436 |
return files_list[idx], f"👕 Выбрано: {files_list[idx]}"
|
| 437 |
|
| 438 |
+
|
| 439 |
def tryon_ui(person_pil, selected_filename):
|
| 440 |
yield None, "⏳ Обработка... (первый запуск может быть дольше)"
|
| 441 |
|
|
|
|
| 457 |
return
|
| 458 |
|
| 459 |
try:
|
| 460 |
+
out_img = start_tryon(
|
| 461 |
human_pil=person_pil,
|
| 462 |
garm_img=garm,
|
| 463 |
auto_mask=True,
|
|
|
|
| 465 |
denoise_steps=25,
|
| 466 |
seed=42,
|
| 467 |
)
|
| 468 |
+
yield out_img, "✅ Готово"
|
| 469 |
except Exception as e:
|
| 470 |
yield None, f"❌ Ошибка: {type(e).__name__}: {str(e)[:220]}"
|
| 471 |
|
|
|
|
| 485 |
with gr.Column():
|
| 486 |
person = gr.Image(label="Фото человека", type="pil", height=420)
|
| 487 |
|
| 488 |
+
# ✅ Добавлено: подсказка сразу под загрузкой фото
|
| 489 |
+
gr.Markdown(PHOTO_TIPS_MD)
|
| 490 |
+
|
| 491 |
with gr.Row():
|
| 492 |
refresh_btn = gr.Button("🔄 Обновить каталог одежды", variant="secondary")
|
| 493 |
selected_label = gr.Markdown("👕 Выберите одежду ниже")
|