Spaces:
Running on Zero
Running on Zero
Fix ZeroGPU pickling error: save files inside @spaces.GPU function
Browse filesThe Gaussian object contains lambdas that can't be pickled across
the multiprocessing boundary. Move all file I/O into _run_pipeline
so only paths/numbers are returned.
app.py
CHANGED
|
@@ -118,9 +118,14 @@ async def get_example(idx: int):
|
|
| 118 |
# ----------------------------------------------------------------------------
|
| 119 |
|
| 120 |
@spaces.GPU
|
| 121 |
-
def _run_pipeline(pil_image, seed, steps, guidance_scale, num_gaussians
|
| 122 |
-
|
| 123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
t0 = time.time()
|
| 125 |
prepared = PIPE.preprocess_image(pil_image)
|
| 126 |
gen = torch.Generator(device=PIPE._device).manual_seed(int(seed))
|
|
@@ -134,7 +139,27 @@ def _run_pipeline(pil_image, seed, steps, guidance_scale, num_gaussians):
|
|
| 134 |
)
|
| 135 |
gaussian = PIPE.decode_latent(out["latent"], num_gaussians=int(num_gaussians))
|
| 136 |
gen_dt = time.time() - t0
|
| 137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
|
| 139 |
|
| 140 |
# ----------------------------------------------------------------------------
|
|
@@ -157,38 +182,23 @@ def generate(
|
|
| 157 |
"""
|
| 158 |
pil_image = Image.open(image["path"]).convert("RGBA")
|
| 159 |
|
| 160 |
-
prepared, gaussian, gen_dt = _run_pipeline(
|
| 161 |
-
pil_image, seed, steps, guidance_scale, num_gaussians,
|
| 162 |
-
)
|
| 163 |
-
|
| 164 |
out_dir = OUT_ROOT / uuid4().hex[:12]
|
| 165 |
out_dir.mkdir(parents=True, exist_ok=True)
|
| 166 |
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
# Save PLY (always needed for the viewer)
|
| 172 |
-
ply_path = out_dir / "splat.ply"
|
| 173 |
-
gaussian.save_ply(str(ply_path))
|
| 174 |
-
|
| 175 |
-
# Save in the requested download format
|
| 176 |
-
fmt = output_format.lower()
|
| 177 |
-
if fmt == "splat":
|
| 178 |
-
download_path = out_dir / "splat.splat"
|
| 179 |
-
gaussian.save_splat(str(download_path))
|
| 180 |
-
else:
|
| 181 |
-
download_path = ply_path
|
| 182 |
|
| 183 |
info = (
|
| 184 |
-
f"{
|
| 185 |
-
f"generation: {gen_dt:.1f}s · saved: {download_path.name}"
|
| 186 |
)
|
| 187 |
|
| 188 |
return (
|
| 189 |
-
FileData(path=
|
| 190 |
-
FileData(path=
|
| 191 |
-
FileData(path=
|
| 192 |
info,
|
| 193 |
)
|
| 194 |
|
|
|
|
| 118 |
# ----------------------------------------------------------------------------
|
| 119 |
|
| 120 |
@spaces.GPU
|
| 121 |
+
def _run_pipeline(pil_image, seed, steps, guidance_scale, num_gaussians,
|
| 122 |
+
out_dir, output_format):
|
| 123 |
+
"""Run the full pipeline (preprocess → encode → sample → decode → save)
|
| 124 |
+
in a single GPU acquisition.
|
| 125 |
+
|
| 126 |
+
All file I/O happens here so the unpicklable Gaussian object never
|
| 127 |
+
crosses the ZeroGPU multiprocessing boundary.
|
| 128 |
+
"""
|
| 129 |
t0 = time.time()
|
| 130 |
prepared = PIPE.preprocess_image(pil_image)
|
| 131 |
gen = torch.Generator(device=PIPE._device).manual_seed(int(seed))
|
|
|
|
| 139 |
)
|
| 140 |
gaussian = PIPE.decode_latent(out["latent"], num_gaussians=int(num_gaussians))
|
| 141 |
gen_dt = time.time() - t0
|
| 142 |
+
|
| 143 |
+
# Save preprocessed image
|
| 144 |
+
prep_path = out_dir / "preprocessed.png"
|
| 145 |
+
prepared.save(str(prep_path))
|
| 146 |
+
|
| 147 |
+
# Save PLY (always needed for the viewer)
|
| 148 |
+
ply_path = out_dir / "splat.ply"
|
| 149 |
+
gaussian.save_ply(str(ply_path))
|
| 150 |
+
|
| 151 |
+
# Save in the requested download format
|
| 152 |
+
fmt = output_format.lower()
|
| 153 |
+
if fmt == "splat":
|
| 154 |
+
download_path = out_dir / "splat.splat"
|
| 155 |
+
gaussian.save_splat(str(download_path))
|
| 156 |
+
else:
|
| 157 |
+
download_path = ply_path
|
| 158 |
+
|
| 159 |
+
n_gaussians = gaussian.get_xyz.shape[0]
|
| 160 |
+
|
| 161 |
+
# Return only picklable primitives / paths
|
| 162 |
+
return str(prep_path), str(ply_path), str(download_path), n_gaussians, gen_dt
|
| 163 |
|
| 164 |
|
| 165 |
# ----------------------------------------------------------------------------
|
|
|
|
| 182 |
"""
|
| 183 |
pil_image = Image.open(image["path"]).convert("RGBA")
|
| 184 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
out_dir = OUT_ROOT / uuid4().hex[:12]
|
| 186 |
out_dir.mkdir(parents=True, exist_ok=True)
|
| 187 |
|
| 188 |
+
prep_path, ply_path, download_path, n_gaussians, gen_dt = _run_pipeline(
|
| 189 |
+
pil_image, seed, steps, guidance_scale, num_gaussians,
|
| 190 |
+
out_dir, output_format,
|
| 191 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
|
| 193 |
info = (
|
| 194 |
+
f"{n_gaussians:,} gaussians · "
|
| 195 |
+
f"generation: {gen_dt:.1f}s · saved: {Path(download_path).name}"
|
| 196 |
)
|
| 197 |
|
| 198 |
return (
|
| 199 |
+
FileData(path=prep_path),
|
| 200 |
+
FileData(path=ply_path),
|
| 201 |
+
FileData(path=download_path),
|
| 202 |
info,
|
| 203 |
)
|
| 204 |
|