Zhengrui commited on
Commit
cf18a04
·
verified ·
1 Parent(s): 8e17629

Use official-style DVD image pipeline wrapper

Browse files
Files changed (2) hide show
  1. README.md +1 -2
  2. app_dvd_image_wrapper.py +187 -0
README.md CHANGED
@@ -5,12 +5,11 @@ colorFrom: blue
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
 
5
  colorTo: green
6
  sdk: gradio
7
  sdk_version: 5.34.2
8
+ app_file: app_dvd_image_wrapper.py
9
  pinned: false
10
  license: mit
11
  models:
12
  - Zhengrui/dvd
 
13
  ---
14
 
15
  # DVD: Discrete Voxel Diffusion for 3D Generation and Editing
app_dvd_image_wrapper.py ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import random
3
+ import uuid
4
+ from pathlib import Path
5
+
6
+ os.environ.setdefault("SPCONV_ALGO", "native")
7
+ os.environ.setdefault("ATTN_BACKEND", "flash_attn")
8
+ os.environ.setdefault("TOKENIZERS_PARALLELISM", "false")
9
+ os.environ.setdefault("DVD_MODEL_REPO", "Zhengrui/dvd")
10
+
11
+ import spaces
12
+ import gradio as gr
13
+ import numpy as np
14
+ import torch
15
+
16
+ from dvd import DVDImageToVoxelPipeline, export_cubified_voxels
17
+
18
+
19
+ MAX_SEED = 2**31 - 1
20
+ ROOT_DIR = Path(__file__).resolve().parent
21
+ TMP_DIR = ROOT_DIR / "tmp" / "dvd_image_wrapper"
22
+ TMP_DIR.mkdir(parents=True, exist_ok=True)
23
+ IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp"}
24
+ EXAMPLE_DIR = ROOT_DIR / "assets" / "example_image"
25
+ EXAMPLES = [
26
+ str(path)
27
+ for path in sorted(EXAMPLE_DIR.iterdir())
28
+ if path.is_file() and path.suffix.lower() in IMAGE_EXTENSIONS
29
+ ] if EXAMPLE_DIR.exists() else []
30
+
31
+
32
+ def log_event(message: str):
33
+ print(f"[DVD Wrapper] {message}", flush=True)
34
+
35
+
36
+ def worker_path(name: str) -> str:
37
+ path = TMP_DIR / f"worker-{uuid.uuid4().hex}"
38
+ path.mkdir(parents=True, exist_ok=True)
39
+ return str(path / name)
40
+
41
+
42
+ def cfg_schedule(mode: str, constant: float, early: float, late: float, split: float):
43
+ if mode == "Constant":
44
+ return float(constant)
45
+ if mode == "Two-stage":
46
+ split = float(split)
47
+ early = float(early)
48
+ late = float(late)
49
+ return lambda t: early if t < split else late
50
+ return None
51
+
52
+
53
+ repo = os.environ.get("DVD_MODEL_REPO", "Zhengrui/dvd")
54
+ subfolder = os.environ.get("DVD_MODEL_SUBFOLDER") or None
55
+ revision = os.environ.get("DVD_MODEL_REVISION") or None
56
+ token = os.environ.get("DVD_MODEL_TOKEN") or os.environ.get("HF_TOKEN") or None
57
+
58
+ log_event(f"loading DVD image pipeline from {repo}")
59
+ dvd_pipe = DVDImageToVoxelPipeline.from_pretrained(
60
+ repo,
61
+ variant="base",
62
+ subfolder=subfolder,
63
+ revision=revision,
64
+ token=token,
65
+ )
66
+ log_event("moving DVD image pipeline to cuda")
67
+ dvd_pipe.to("cuda")
68
+ log_event("DVD image pipeline ready on cuda")
69
+
70
+
71
+ @spaces.GPU(duration=30)
72
+ def zero_gpu_smoke_test():
73
+ log_event("zero_gpu_smoke_test start")
74
+ if not torch.cuda.is_available():
75
+ log_event("zero_gpu_smoke_test no cuda")
76
+ return "CUDA unavailable inside ZeroGPU worker"
77
+ value = torch.ones((1,), device="cuda").sum().item()
78
+ name = torch.cuda.get_device_name(0)
79
+ log_event(f"zero_gpu_smoke_test done device={name} value={value}")
80
+ return f"OK: {name}, value={value}"
81
+
82
+
83
+ @spaces.GPU(duration=180)
84
+ def generate_voxels(
85
+ image,
86
+ seed: int,
87
+ randomize_seed: bool,
88
+ preprocess_image: bool,
89
+ dvd_steps: int,
90
+ dvd_cfg_mode: str,
91
+ dvd_cfg_constant: float,
92
+ dvd_cfg_early: float,
93
+ dvd_cfg_late: float,
94
+ dvd_cfg_split: float,
95
+ progress=gr.Progress(track_tqdm=True),
96
+ ):
97
+ progress(0.01, desc="Starting ZeroGPU callback")
98
+ log_event(f"generate_voxels start seed={seed} randomize={randomize_seed} steps={dvd_steps}")
99
+ if image is None:
100
+ raise gr.Error("Please provide an image.")
101
+
102
+ seed = random.randint(0, MAX_SEED) if randomize_seed else int(seed)
103
+ sampler_kwargs = {"steps": int(dvd_steps)}
104
+ schedule = cfg_schedule(
105
+ dvd_cfg_mode,
106
+ dvd_cfg_constant,
107
+ dvd_cfg_early,
108
+ dvd_cfg_late,
109
+ dvd_cfg_split,
110
+ )
111
+ if schedule is not None:
112
+ sampler_kwargs["cfg_strength"] = schedule
113
+
114
+ progress(0.08, desc="Sampling DVD voxels")
115
+ voxels = dvd_pipe.sample_voxels(
116
+ image,
117
+ seed=seed,
118
+ preprocess_image=preprocess_image,
119
+ **sampler_kwargs,
120
+ )
121
+
122
+ progress(0.88, desc="Exporting voxel preview")
123
+ mesh_path = worker_path("generated_voxels.glb")
124
+ npy_path = worker_path("generated_voxel64_coords.npy")
125
+ export_cubified_voxels(voxels, mesh_path)
126
+ np.save(npy_path, voxels.coords_without_batch.detach().cpu().numpy().astype(np.int32))
127
+ torch.cuda.empty_cache()
128
+ log_event(f"generate_voxels done seed={seed} mesh={mesh_path} npy={npy_path}")
129
+ return mesh_path, npy_path, int(seed), f"Done. seed={seed}"
130
+
131
+
132
+ with gr.Blocks(title="DVD Image", fill_width=True) as demo:
133
+ gr.Markdown("## DVD Image Voxel Generation")
134
+ with gr.Row():
135
+ smoke_btn = gr.Button("ZeroGPU Smoke Test")
136
+ smoke_out = gr.Textbox(label="ZeroGPU Status", interactive=False)
137
+ smoke_btn.click(zero_gpu_smoke_test, outputs=smoke_out)
138
+
139
+ with gr.Row(equal_height=False):
140
+ with gr.Column():
141
+ image = gr.Image(label="Input Image", format="png", image_mode="RGBA", type="pil", height=320)
142
+ if EXAMPLES:
143
+ gr.Examples(examples=EXAMPLES[:12], inputs=image, examples_per_page=6)
144
+ with gr.Accordion("DVD Settings", open=False):
145
+ seed = gr.Slider(0, MAX_SEED, value=0, step=1, label="Seed")
146
+ randomize_seed = gr.Checkbox(value=True, label="Randomize seed")
147
+ preprocess_image = gr.Checkbox(value=True, label="DVD preprocess image")
148
+ dvd_steps = gr.Slider(1, 512, value=256, step=1, label="DVD steps")
149
+ dvd_cfg_mode = gr.Radio(
150
+ ["Default schedule", "Constant", "Two-stage"],
151
+ value="Default schedule",
152
+ label="DVD CFG mode",
153
+ )
154
+ dvd_cfg_constant = gr.Slider(0.0, 5.0, value=0.7, step=0.05, label="Constant CFG")
155
+ dvd_cfg_early = gr.Slider(0.0, 5.0, value=0.4, step=0.05, label="Early CFG")
156
+ dvd_cfg_late = gr.Slider(0.0, 5.0, value=0.7, step=0.05, label="Late CFG")
157
+ dvd_cfg_split = gr.Slider(0.0, 1.0, value=0.5, step=0.05, label="CFG switch time")
158
+ gen_btn = gr.Button("Generate DVD Voxels", variant="primary")
159
+ with gr.Column():
160
+ voxel_view = gr.Model3D(
161
+ label="Generated / Cubified Voxels",
162
+ height=360,
163
+ camera_position=(-180, 90, 3),
164
+ )
165
+ npy_download = gr.DownloadButton(label="Download Voxel Coords (.npy)", interactive=False)
166
+ status = gr.Textbox(label="Status", interactive=False)
167
+
168
+ gen_btn.click(
169
+ generate_voxels,
170
+ inputs=[
171
+ image,
172
+ seed,
173
+ randomize_seed,
174
+ preprocess_image,
175
+ dvd_steps,
176
+ dvd_cfg_mode,
177
+ dvd_cfg_constant,
178
+ dvd_cfg_early,
179
+ dvd_cfg_late,
180
+ dvd_cfg_split,
181
+ ],
182
+ outputs=[voxel_view, npy_download, seed, status],
183
+ ).then(lambda: gr.DownloadButton(interactive=True), outputs=[npy_download])
184
+
185
+
186
+ if __name__ == "__main__":
187
+ demo.launch(show_api=False, show_error=True, ssr_mode=False)