Spaces:
Sleeping
Sleeping
showcase
Browse files- app.py +8 -3
- inference.py +61 -37
app.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
import base64
|
|
|
|
| 4 |
import random
|
| 5 |
import time
|
| 6 |
import traceback
|
|
@@ -50,6 +51,7 @@ APP_DIR = Path(__file__).resolve().parent
|
|
| 50 |
LOGO_PATH = APP_DIR / "assets" / "tryon_logo_compact.png"
|
| 51 |
SHOWCASE_DIR = APP_DIR / "assets" / "showcase"
|
| 52 |
SHOWCASE_IMAGES = [SHOWCASE_DIR / f"case_{idx}.png" for idx in range(4)]
|
|
|
|
| 53 |
|
| 54 |
def _image_data_uri(path: Path) -> str:
|
| 55 |
if not path.exists():
|
|
@@ -298,12 +300,12 @@ def _predict_from_prompt_files(prompt: str, files):
|
|
| 298 |
)
|
| 299 |
|
| 300 |
|
| 301 |
-
@spaces.GPU(size="xlarge", duration=
|
| 302 |
def predict_from_inputs(prompt: str, files):
|
| 303 |
return _predict_from_prompt_files(prompt, files)
|
| 304 |
|
| 305 |
|
| 306 |
-
@spaces.GPU(size="xlarge", duration=
|
| 307 |
def predict_from_message(message):
|
| 308 |
prompt, files = _message_to_prompt_files(message)
|
| 309 |
return _predict_from_prompt_files(prompt, files)
|
|
@@ -674,8 +676,11 @@ with gr.Blocks(title="JoyAI Virtual Try-On", css=CSS) as demo:
|
|
| 674 |
inputs=[prompt_box],
|
| 675 |
outputs=predict_outputs,
|
| 676 |
api_name="tryon",
|
|
|
|
|
|
|
|
|
|
| 677 |
)
|
| 678 |
|
| 679 |
|
| 680 |
if __name__ == "__main__":
|
| 681 |
-
demo.queue(max_size=
|
|
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
import base64
|
| 4 |
+
import os
|
| 5 |
import random
|
| 6 |
import time
|
| 7 |
import traceback
|
|
|
|
| 51 |
LOGO_PATH = APP_DIR / "assets" / "tryon_logo_compact.png"
|
| 52 |
SHOWCASE_DIR = APP_DIR / "assets" / "showcase"
|
| 53 |
SHOWCASE_IMAGES = [SHOWCASE_DIR / f"case_{idx}.png" for idx in range(4)]
|
| 54 |
+
ZERO_GPU_DURATION_SECONDS = int(os.getenv("ZERO_GPU_DURATION_SECONDS", "240"))
|
| 55 |
|
| 56 |
def _image_data_uri(path: Path) -> str:
|
| 57 |
if not path.exists():
|
|
|
|
| 300 |
)
|
| 301 |
|
| 302 |
|
| 303 |
+
@spaces.GPU(size="xlarge", duration=ZERO_GPU_DURATION_SECONDS)
|
| 304 |
def predict_from_inputs(prompt: str, files):
|
| 305 |
return _predict_from_prompt_files(prompt, files)
|
| 306 |
|
| 307 |
|
| 308 |
+
@spaces.GPU(size="xlarge", duration=ZERO_GPU_DURATION_SECONDS)
|
| 309 |
def predict_from_message(message):
|
| 310 |
prompt, files = _message_to_prompt_files(message)
|
| 311 |
return _predict_from_prompt_files(prompt, files)
|
|
|
|
| 676 |
inputs=[prompt_box],
|
| 677 |
outputs=predict_outputs,
|
| 678 |
api_name="tryon",
|
| 679 |
+
trigger_mode="once",
|
| 680 |
+
concurrency_limit=1,
|
| 681 |
+
concurrency_id="tryon_gpu",
|
| 682 |
)
|
| 683 |
|
| 684 |
|
| 685 |
if __name__ == "__main__":
|
| 686 |
+
demo.queue(max_size=4, default_concurrency_limit=1).launch(show_error=True)
|
inference.py
CHANGED
|
@@ -10,6 +10,7 @@ import random
|
|
| 10 |
import re
|
| 11 |
import sys
|
| 12 |
import contextlib
|
|
|
|
| 13 |
import time
|
| 14 |
from dataclasses import dataclass
|
| 15 |
from pathlib import Path
|
|
@@ -73,9 +74,14 @@ class LoadedTryOnModel:
|
|
| 73 |
|
| 74 |
_MODEL: LoadedTryOnModel | None = None
|
| 75 |
_MODEL_DIR: Path | None = None
|
|
|
|
| 76 |
_PE_CACHE: dict[str, str] = {}
|
| 77 |
|
| 78 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
def _get_bool_env(name: str, default: bool = False) -> bool:
|
| 80 |
value = os.getenv(name)
|
| 81 |
if value is None:
|
|
@@ -502,43 +508,61 @@ def load_model() -> LoadedTryOnModel:
|
|
| 502 |
if _MODEL is not None:
|
| 503 |
return _MODEL
|
| 504 |
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
|
| 515 |
-
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
|
| 526 |
-
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
|
| 539 |
-
|
| 540 |
-
|
| 541 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 542 |
|
| 543 |
|
| 544 |
def get_bucket_size(img: Image.Image) -> tuple[int, int]:
|
|
|
|
| 10 |
import re
|
| 11 |
import sys
|
| 12 |
import contextlib
|
| 13 |
+
import threading
|
| 14 |
import time
|
| 15 |
from dataclasses import dataclass
|
| 16 |
from pathlib import Path
|
|
|
|
| 74 |
|
| 75 |
_MODEL: LoadedTryOnModel | None = None
|
| 76 |
_MODEL_DIR: Path | None = None
|
| 77 |
+
_MODEL_LOCK = threading.Lock()
|
| 78 |
_PE_CACHE: dict[str, str] = {}
|
| 79 |
|
| 80 |
|
| 81 |
+
def _log_stage(message: str) -> None:
|
| 82 |
+
print(f"[tryon] {time.strftime('%Y-%m-%d %H:%M:%S')} | {message}", flush=True)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
def _get_bool_env(name: str, default: bool = False) -> bool:
|
| 86 |
value = os.getenv(name)
|
| 87 |
if value is None:
|
|
|
|
| 508 |
if _MODEL is not None:
|
| 509 |
return _MODEL
|
| 510 |
|
| 511 |
+
with _MODEL_LOCK:
|
| 512 |
+
if _MODEL is not None:
|
| 513 |
+
return _MODEL
|
| 514 |
+
|
| 515 |
+
started_at = time.time()
|
| 516 |
+
_log_stage("load_model start")
|
| 517 |
+
_MODEL_DIR = _download_model_repo()
|
| 518 |
+
_add_code_paths(_MODEL_DIR)
|
| 519 |
+
|
| 520 |
+
from xvideo.config import load_config_class_from_pyfile
|
| 521 |
+
from xvideo.models import load_dit, load_pipeline
|
| 522 |
+
|
| 523 |
+
config_path = _find_default_config(_MODEL_DIR)
|
| 524 |
+
transformer_path = _find_transformer_path(_MODEL_DIR)
|
| 525 |
+
text_encoder_path = _resolve_text_encoder_path(_MODEL_DIR)
|
| 526 |
+
processor_path = _resolve_processor_path(_MODEL_DIR)
|
| 527 |
+
tokenizer_path = _resolve_tokenizer_path(_MODEL_DIR)
|
| 528 |
+
_log_stage(f"resolved config={config_path}")
|
| 529 |
+
_log_stage(f"resolved transformer={transformer_path}")
|
| 530 |
+
_log_stage(f"resolved text_encoder={text_encoder_path}")
|
| 531 |
+
_log_stage(f"resolved processor={processor_path}")
|
| 532 |
+
_log_stage(f"resolved tokenizer={tokenizer_path}")
|
| 533 |
+
|
| 534 |
+
config_class = load_config_class_from_pyfile(str(config_path))
|
| 535 |
+
cfg = config_class()
|
| 536 |
+
cfg.dit_ckpt = str(transformer_path)
|
| 537 |
+
cfg.dit_ckpt_type = os.getenv(
|
| 538 |
+
"JOY_DIT_CKPT_TYPE",
|
| 539 |
+
"safetensor" if transformer_path.is_dir() else getattr(cfg, "dit_ckpt_type", "pt"),
|
| 540 |
+
)
|
| 541 |
+
cfg.training_mode = False
|
| 542 |
+
cfg.use_fsdp_inference = False
|
| 543 |
+
cfg.hsdp_shard_dim = 1
|
| 544 |
+
_patch_config_paths(cfg, _MODEL_DIR)
|
| 545 |
+
|
| 546 |
+
device = _device()
|
| 547 |
+
_log_stage(f"loading DiT on {device}")
|
| 548 |
+
with _patch_split_firered_paths(
|
| 549 |
+
text_encoder_path=text_encoder_path,
|
| 550 |
+
processor_path=processor_path,
|
| 551 |
+
tokenizer_path=tokenizer_path,
|
| 552 |
+
):
|
| 553 |
+
dit_started_at = time.time()
|
| 554 |
+
dit = load_dit(cfg, device=device)
|
| 555 |
+
_log_stage(f"loaded DiT in {time.time() - dit_started_at:.1f}s")
|
| 556 |
+
dit.requires_grad_(False)
|
| 557 |
+
dit.eval()
|
| 558 |
+
pipeline_started_at = time.time()
|
| 559 |
+
_log_stage("loading pipeline")
|
| 560 |
+
pipeline = load_pipeline(cfg, dit, device)
|
| 561 |
+
_log_stage(f"loaded pipeline in {time.time() - pipeline_started_at:.1f}s")
|
| 562 |
+
|
| 563 |
+
_MODEL = LoadedTryOnModel(pipeline=pipeline, cfg=cfg, device=device, model_dir=_MODEL_DIR)
|
| 564 |
+
_log_stage(f"load_model done in {time.time() - started_at:.1f}s")
|
| 565 |
+
return _MODEL
|
| 566 |
|
| 567 |
|
| 568 |
def get_bucket_size(img: Image.Image) -> tuple[int, int]:
|