oualidD4 commited on
Commit
2de6da3
·
verified ·
1 Parent(s): 22c23b5

Preprocess without torchvision (ZeroGPU base lacks it); show_error=True

Browse files
Files changed (2) hide show
  1. app.py +1 -1
  2. pipeline.py +52 -10
app.py CHANGED
@@ -185,4 +185,4 @@ def build_demo():
185
 
186
 
187
  if __name__ == "__main__":
188
- build_demo().queue().launch()
 
185
 
186
 
187
  if __name__ == "__main__":
188
+ build_demo().queue().launch(show_error=True)
pipeline.py CHANGED
@@ -25,10 +25,8 @@ import torch
25
  from lingbot_map.models.gct_stream import GCTStream
26
  from lingbot_map.utils.pose_enc import pose_encoding_to_extri_intri
27
  # (w2c kept for the GLB path → closed_form_inverse_se3_general intentionally NOT imported)
28
- # NB: load_and_preprocess_images (utils.load_fn) is the only thing here that pulls
29
- # torchvision; it is imported lazily inside preprocess_paths() so this module stays
30
- # importable on boxes where torchvision is unavailable (e.g. a pyenv build missing
31
- # _lzma) for CPU-only export testing.
32
 
33
  # NB: import the exporter from the *submodule*, not ``lingbot_map.vis``. The package
34
  # __init__ imports the viser-based PointCloudViewer at load time, which would force a
@@ -137,15 +135,59 @@ def preprocess_paths(
137
  image_size: int = 518,
138
  patch_size: int = 14,
139
  ) -> torch.Tensor:
140
- """Wrap ``load_and_preprocess_images`` (crop mode width 518, EXIF-honored).
 
 
 
 
 
 
141
 
142
  Returns a tensor of shape [S, 3, H, W] in range [0, 1].
143
  """
144
- from lingbot_map.utils.load_fn import load_and_preprocess_images # lazy (torchvision)
145
-
146
- return load_and_preprocess_images(
147
- list(paths), mode="crop", image_size=image_size, patch_size=patch_size
148
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
 
151
  # =============================================================================
 
25
  from lingbot_map.models.gct_stream import GCTStream
26
  from lingbot_map.utils.pose_enc import pose_encoding_to_extri_intri
27
  # (w2c kept for the GLB path → closed_form_inverse_se3_general intentionally NOT imported)
28
+ # NB: preprocess_paths() reimplements upstream load_and_preprocess_images WITHOUT torchvision
29
+ # (the ZeroGPU base image ships torch but not torchvision), so nothing here imports torchvision.
 
 
30
 
31
  # NB: import the exporter from the *submodule*, not ``lingbot_map.vis``. The package
32
  # __init__ imports the viser-based PointCloudViewer at load time, which would force a
 
135
  image_size: int = 518,
136
  patch_size: int = 14,
137
  ) -> torch.Tensor:
138
+ """Preprocess images to the model's canonical input — a **torchvision-free**, faithful
139
+ reimplementation of upstream ``load_and_preprocess_images`` (crop mode).
140
+
141
+ The ZeroGPU base image ships torch but NOT torchvision, and upstream's loader imports
142
+ ``torchvision.transforms`` only for ``ToTensor``. We replicate the exact logic (EXIF
143
+ transpose, alpha-on-white, width=518, height→multiple of patch_size, center-crop height,
144
+ pad mismatched shapes with white) and swap ToTensor for a numpy→torch equivalent.
145
 
146
  Returns a tensor of shape [S, 3, H, W] in range [0, 1].
147
  """
148
+ from PIL import Image, ImageOps
149
+
150
+ def to_tensor_rgb(pil_img):
151
+ # torchvision.transforms.ToTensor() equivalent for an 8-bit RGB image:
152
+ # HWC uint8 [0,255] -> CHW float32 [0,1]
153
+ arr = np.array(pil_img, dtype=np.uint8) # [H, W, 3] (copy → writable)
154
+ return torch.from_numpy(arr).permute(2, 0, 1).contiguous().float().div_(255.0)
155
+
156
+ target = image_size
157
+ imgs = []
158
+ for p in paths:
159
+ img = Image.open(p)
160
+ img = ImageOps.exif_transpose(img) # honor phone/camera EXIF
161
+ if img.mode == "RGBA":
162
+ bg = Image.new("RGBA", img.size, (255, 255, 255, 255))
163
+ img = Image.alpha_composite(bg, img)
164
+ img = img.convert("RGB")
165
+ w, h = img.size
166
+ new_w = target
167
+ new_h = round(h * (new_w / w) / patch_size) * patch_size
168
+ img = img.resize((new_w, new_h), Image.Resampling.BICUBIC)
169
+ t = to_tensor_rgb(img) # [3, new_h, new_w]
170
+ if new_h > target: # center-crop height
171
+ sy = (new_h - target) // 2
172
+ t = t[:, sy:sy + target, :]
173
+ imgs.append(t)
174
+
175
+ # If frames differ in shape (mixed aspect ratios), pad to the max with white (1.0).
176
+ shapes = {(t.shape[1], t.shape[2]) for t in imgs}
177
+ if len(shapes) > 1:
178
+ max_h = max(s[0] for s in shapes)
179
+ max_w = max(s[1] for s in shapes)
180
+ out = []
181
+ for t in imgs:
182
+ ph, pw = max_h - t.shape[1], max_w - t.shape[2]
183
+ if ph > 0 or pw > 0:
184
+ pt, pb = ph // 2, ph - ph // 2
185
+ pl, pr = pw // 2, pw - pw // 2
186
+ t = torch.nn.functional.pad(t, (pl, pr, pt, pb), mode="constant", value=1.0)
187
+ out.append(t)
188
+ imgs = out
189
+
190
+ return torch.stack(imgs)
191
 
192
 
193
  # =============================================================================