Restore full image app with GPU-lazy DVD imports
Browse files- README.md +2 -1
- app_dvd_image.py +89 -5
README.md
CHANGED
|
@@ -5,11 +5,12 @@ colorFrom: blue
|
|
| 5 |
colorTo: green
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 5.34.2
|
| 8 |
-
app_file:
|
| 9 |
pinned: false
|
| 10 |
license: mit
|
| 11 |
models:
|
| 12 |
- Zhengrui/dvd
|
|
|
|
| 13 |
---
|
| 14 |
|
| 15 |
# DVD: Discrete Voxel Diffusion for 3D Generation and Editing
|
|
|
|
| 5 |
colorTo: green
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 5.34.2
|
| 8 |
+
app_file: app_dvd_image.py
|
| 9 |
pinned: false
|
| 10 |
license: mit
|
| 11 |
models:
|
| 12 |
- Zhengrui/dvd
|
| 13 |
+
- microsoft/TRELLIS-image-large
|
| 14 |
---
|
| 15 |
|
| 16 |
# DVD: Discrete Voxel Diffusion for 3D Generation and Editing
|
app_dvd_image.py
CHANGED
|
@@ -90,6 +90,89 @@ def log_event(message: str):
|
|
| 90 |
print(f"[DVD Space] {message}", flush=True)
|
| 91 |
|
| 92 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
def ensure_dvd_imports():
|
| 94 |
global DVDImageToVoxelPipeline
|
| 95 |
global TrellisImageTo3DPipeline
|
|
@@ -203,7 +286,7 @@ EDIT_IMAGE_EXAMPLES = [
|
|
| 203 |
(asset_label(path), str(path)) for path in list_asset_files("assets/example_image_edit", IMAGE_EXTENSIONS)
|
| 204 |
]
|
| 205 |
EDIT_VOXEL_EXAMPLES = [
|
| 206 |
-
(asset_label(path), str(path)) for path in list_asset_files("assets/example_voxel_edit", {".npy", ".pt", ".pth"})
|
| 207 |
]
|
| 208 |
|
| 209 |
|
|
@@ -290,7 +373,7 @@ def ensure_trellis_pipeline(device: str | None = None):
|
|
| 290 |
|
| 291 |
def preload_zero_gpu_models(device: str | None = None, preload: str | None = None):
|
| 292 |
device = device or os.environ.get("DVD_SPACE_DEVICE", "cuda")
|
| 293 |
-
preload = preload or os.environ.get("DVD_STARTUP_PRELOAD", "
|
| 294 |
requested = {
|
| 295 |
item.strip().lower()
|
| 296 |
for item in preload.replace(";", ",").split(",")
|
|
@@ -530,7 +613,7 @@ def mask_inputs():
|
|
| 530 |
]
|
| 531 |
|
| 532 |
|
| 533 |
-
@spaces.GPU(duration=
|
| 534 |
def generate_voxels(
|
| 535 |
image: Image.Image,
|
| 536 |
seed: int,
|
|
@@ -651,7 +734,7 @@ def visualize_edit_mask(
|
|
| 651 |
return mesh_path
|
| 652 |
|
| 653 |
|
| 654 |
-
@spaces.GPU(duration=
|
| 655 |
def run_editing(
|
| 656 |
target_image: Image.Image,
|
| 657 |
voxels,
|
|
@@ -1025,7 +1108,8 @@ def parse_args():
|
|
| 1025 |
if __name__ == "__main__":
|
| 1026 |
args = parse_args()
|
| 1027 |
|
| 1028 |
-
#
|
|
|
|
| 1029 |
preload_zero_gpu_models(os.environ.get("DVD_SPACE_DEVICE", "cuda"))
|
| 1030 |
demo.queue().launch(
|
| 1031 |
share=args.share,
|
|
|
|
| 90 |
print(f"[DVD Space] {message}", flush=True)
|
| 91 |
|
| 92 |
|
| 93 |
+
def _download_file(url: str, dest: Path):
|
| 94 |
+
import urllib.request
|
| 95 |
+
|
| 96 |
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
| 97 |
+
if dest.exists() and dest.stat().st_size > 0:
|
| 98 |
+
log_event(f"using cached {dest.name}")
|
| 99 |
+
return
|
| 100 |
+
tmp = dest.with_name(dest.name + ".tmp")
|
| 101 |
+
if tmp.exists():
|
| 102 |
+
tmp.unlink()
|
| 103 |
+
log_event(f"downloading {url} -> {dest}")
|
| 104 |
+
urllib.request.urlretrieve(url, tmp)
|
| 105 |
+
tmp.replace(dest)
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def prefetch_dvd_weights():
|
| 109 |
+
from huggingface_hub import hf_hub_download, snapshot_download
|
| 110 |
+
|
| 111 |
+
if DVD_MODEL_REPO:
|
| 112 |
+
filenames = (
|
| 113 |
+
"dvd_img.json",
|
| 114 |
+
"dvd_img.safetensors",
|
| 115 |
+
"dvd_img_BSP_ft.json",
|
| 116 |
+
"dvd_img_BSP_ft.safetensors",
|
| 117 |
+
)
|
| 118 |
+
for filename in filenames:
|
| 119 |
+
log_event(f"prefetching {DVD_MODEL_REPO}/{filename}")
|
| 120 |
+
hf_hub_download(
|
| 121 |
+
repo_id=DVD_MODEL_REPO,
|
| 122 |
+
filename=filename,
|
| 123 |
+
subfolder=DVD_MODEL_SUBFOLDER,
|
| 124 |
+
revision=DVD_MODEL_REVISION,
|
| 125 |
+
token=DVD_MODEL_TOKEN,
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
if os.environ.get("DVD_PREFETCH_TRELLIS", "0").lower() not in {"0", "false", "no", "off"}:
|
| 129 |
+
log_event(f"prefetching TRELLIS stage2 model {TRELLIS_IMAGE_MODEL}")
|
| 130 |
+
snapshot_download(repo_id=TRELLIS_IMAGE_MODEL)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def prefetch_dinov2():
|
| 134 |
+
import shutil
|
| 135 |
+
import zipfile
|
| 136 |
+
|
| 137 |
+
hub_dir = Path(torch.hub.get_dir())
|
| 138 |
+
hub_dir.mkdir(parents=True, exist_ok=True)
|
| 139 |
+
repo_dir = hub_dir / "facebookresearch_dinov2_main"
|
| 140 |
+
if repo_dir.exists():
|
| 141 |
+
log_event(f"using cached DINOv2 repo {repo_dir}")
|
| 142 |
+
else:
|
| 143 |
+
zip_path = hub_dir / "main.zip"
|
| 144 |
+
_download_file("https://github.com/facebookresearch/dinov2/zipball/main", zip_path)
|
| 145 |
+
extract_tmp = hub_dir / "_dvd_dinov2_extract"
|
| 146 |
+
if extract_tmp.exists():
|
| 147 |
+
shutil.rmtree(extract_tmp)
|
| 148 |
+
extract_tmp.mkdir(parents=True, exist_ok=True)
|
| 149 |
+
log_event(f"extracting DINOv2 repo to {repo_dir}")
|
| 150 |
+
with zipfile.ZipFile(zip_path) as zf:
|
| 151 |
+
zf.extractall(extract_tmp)
|
| 152 |
+
top_level = zf.namelist()[0].split("/", 1)[0]
|
| 153 |
+
shutil.move(str(extract_tmp / top_level), str(repo_dir))
|
| 154 |
+
shutil.rmtree(extract_tmp, ignore_errors=True)
|
| 155 |
+
|
| 156 |
+
ckpt_dir = hub_dir / "checkpoints"
|
| 157 |
+
_download_file(
|
| 158 |
+
"https://dl.fbaipublicfiles.com/dinov2/dinov2_vitl14/dinov2_vitl14_reg4_pretrain.pth",
|
| 159 |
+
ckpt_dir / "dinov2_vitl14_reg4_pretrain.pth",
|
| 160 |
+
)
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
def prefetch_assets():
|
| 164 |
+
if os.environ.get("DVD_PREFETCH_ASSETS", "1").lower() in {"0", "false", "no", "off"}:
|
| 165 |
+
log_event("asset prefetch disabled")
|
| 166 |
+
return
|
| 167 |
+
try:
|
| 168 |
+
log_event("asset prefetch start")
|
| 169 |
+
prefetch_dvd_weights()
|
| 170 |
+
prefetch_dinov2()
|
| 171 |
+
log_event("asset prefetch done")
|
| 172 |
+
except Exception as exc:
|
| 173 |
+
log_event(f"asset prefetch failed; continuing without prefetch: {exc}")
|
| 174 |
+
|
| 175 |
+
|
| 176 |
def ensure_dvd_imports():
|
| 177 |
global DVDImageToVoxelPipeline
|
| 178 |
global TrellisImageTo3DPipeline
|
|
|
|
| 286 |
(asset_label(path), str(path)) for path in list_asset_files("assets/example_image_edit", IMAGE_EXTENSIONS)
|
| 287 |
]
|
| 288 |
EDIT_VOXEL_EXAMPLES = [
|
| 289 |
+
(asset_label(path), str(path)) for path in list_asset_files("assets/example_voxel_edit", {".npy", ".npz", ".pt", ".pth"})
|
| 290 |
]
|
| 291 |
|
| 292 |
|
|
|
|
| 373 |
|
| 374 |
def preload_zero_gpu_models(device: str | None = None, preload: str | None = None):
|
| 375 |
device = device or os.environ.get("DVD_SPACE_DEVICE", "cuda")
|
| 376 |
+
preload = preload or os.environ.get("DVD_STARTUP_PRELOAD", "none")
|
| 377 |
requested = {
|
| 378 |
item.strip().lower()
|
| 379 |
for item in preload.replace(";", ",").split(",")
|
|
|
|
| 613 |
]
|
| 614 |
|
| 615 |
|
| 616 |
+
@spaces.GPU(duration=600)
|
| 617 |
def generate_voxels(
|
| 618 |
image: Image.Image,
|
| 619 |
seed: int,
|
|
|
|
| 734 |
return mesh_path
|
| 735 |
|
| 736 |
|
| 737 |
+
@spaces.GPU(duration=600)
|
| 738 |
def run_editing(
|
| 739 |
target_image: Image.Image,
|
| 740 |
voxels,
|
|
|
|
| 1108 |
if __name__ == "__main__":
|
| 1109 |
args = parse_args()
|
| 1110 |
|
| 1111 |
+
# Keep startup free of DVD/TRELLIS imports; only prefetch raw assets.
|
| 1112 |
+
prefetch_assets()
|
| 1113 |
preload_zero_gpu_models(os.environ.get("DVD_SPACE_DEVICE", "cuda"))
|
| 1114 |
demo.queue().launch(
|
| 1115 |
share=args.share,
|