ffgs 0.1.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. ffgs/__init__.py +39 -0
  2. ffgs/dynamic.py +239 -0
  3. ffgs/geometry.py +418 -0
  4. ffgs/image.py +204 -0
  5. ffgs/models/__init__.py +2 -0
  6. ffgs/models/anysplat/LICENSE +1120 -0
  7. ffgs/models/anysplat/__init__.py +49 -0
  8. ffgs/models/anysplat/encoder.py +372 -0
  9. ffgs/models/anysplat/evaluation.py +98 -0
  10. ffgs/models/anysplat/gaussian_adapter.py +96 -0
  11. ffgs/models/anysplat/gs_head.py +166 -0
  12. ffgs/models/anysplat/modeling.py +95 -0
  13. ffgs/models/anysplat/processing.py +249 -0
  14. ffgs/models/anysplat/vggt/__init__.py +0 -0
  15. ffgs/models/anysplat/vggt/heads/__init__.py +0 -0
  16. ffgs/models/anysplat/vggt/heads/camera_head.py +181 -0
  17. ffgs/models/anysplat/vggt/heads/dpt_head.py +612 -0
  18. ffgs/models/anysplat/vggt/heads/head_act.py +134 -0
  19. ffgs/models/anysplat/vggt/heads/utils.py +126 -0
  20. ffgs/models/anysplat/vggt/layers/__init__.py +28 -0
  21. ffgs/models/anysplat/vggt/layers/attention.py +92 -0
  22. ffgs/models/anysplat/vggt/layers/block.py +157 -0
  23. ffgs/models/anysplat/vggt/layers/drop_path.py +42 -0
  24. ffgs/models/anysplat/vggt/layers/layer_scale.py +32 -0
  25. ffgs/models/anysplat/vggt/layers/mlp.py +45 -0
  26. ffgs/models/anysplat/vggt/layers/patch_embed.py +106 -0
  27. ffgs/models/anysplat/vggt/layers/rope.py +211 -0
  28. ffgs/models/anysplat/vggt/layers/swiglu_ffn.py +77 -0
  29. ffgs/models/anysplat/vggt/layers/vision_transformer.py +463 -0
  30. ffgs/models/anysplat/vggt/models/__init__.py +0 -0
  31. ffgs/models/anysplat/vggt/models/aggregator.py +398 -0
  32. ffgs/models/anysplat/vggt/utils/__init__.py +0 -0
  33. ffgs/models/anysplat/vggt/utils/geometry.py +230 -0
  34. ffgs/models/anysplat/vggt/utils/pose_enc.py +144 -0
  35. ffgs/models/anysplat/vggt/utils/rotation.py +148 -0
  36. ffgs/models/tokengs/__init__.py +43 -0
  37. ffgs/models/tokengs/activations.py +135 -0
  38. ffgs/models/tokengs/attention.py +189 -0
  39. ffgs/models/tokengs/enc_dec.py +323 -0
  40. ffgs/models/tokengs/modeling.py +173 -0
  41. ffgs/models/tokengs/processing.py +339 -0
  42. ffgs/pipeline.py +341 -0
  43. ffgs/processor.py +236 -0
  44. ffgs/registry.py +132 -0
  45. ffgs/render.py +111 -0
  46. ffgs/types.py +291 -0
  47. ffgs/zoo/__init__.py +234 -0
  48. ffgs/zoo/anysplat/default.json +34 -0
  49. ffgs/zoo/tokengs/dl3dv-2v.json +29 -0
  50. ffgs/zoo/tokengs/dl3dv-4v.json +29 -0
  51. ffgs/zoo/tokengs/dl3dv-6v.json +29 -0
  52. ffgs/zoo/tokengs/dl3dv-base.json +29 -0
  53. ffgs/zoo/tokengs/dl3dv-latent-2v-lpips.json +48 -0
  54. ffgs/zoo/tokengs/dl3dv-latent-2v-ssim.json +48 -0
  55. ffgs/zoo/tokengs/dl3dv-latent-4v-lpips.json +48 -0
  56. ffgs/zoo/tokengs/dl3dv-latent-4v-ssim.json +48 -0
  57. ffgs/zoo/tokengs/dl3dv-latent-6v-lpips.json +48 -0
  58. ffgs/zoo/tokengs/dl3dv-latent-6v-ssim.json +48 -0
  59. ffgs-0.1.0.dev0.dist-info/METADATA +320 -0
  60. ffgs-0.1.0.dev0.dist-info/RECORD +63 -0
  61. ffgs-0.1.0.dev0.dist-info/WHEEL +4 -0
  62. ffgs-0.1.0.dev0.dist-info/licenses/LICENSE +237 -0
  63. ffgs-0.1.0.dev0.dist-info/licenses/src/ffgs/models/anysplat/LICENSE +1120 -0
ffgs/image.py ADDED
@@ -0,0 +1,204 @@
1
+ # Adapted from the crop_shim of pixelSplat (https://github.com/dcharatan/pixelsplat):
2
+ # Copyright (c) 2023 David Charatan, Sizhe Li, Andrea Tagliasacchi, and Vincent Sitzmann
3
+ # SPDX-License-Identifier: MIT (see the pixelSplat section at the end of LICENSE)
4
+
5
+ """Fitting input images to a model's input shape.
6
+
7
+ The default (`crop_mode="crop"`) is the rule most feed-forward 3DGS models are
8
+ trained with: scale to cover the shape, crop the middle, keep the normalised
9
+ principal point. `tests/test_image.py` pins it bit for bit to frozen reference
10
+ outputs (`tests/fixtures/`). `"pad"` scales to fit inside and pads instead
11
+ (nothing is cut off); `"none"` takes inputs already at the model's shape as they are.
12
+
13
+ Intrinsics are the normalised 3x3 (fx / W, cx / W, fy / H, cy / H). The centre crop
14
+ scales fx / fy and leaves the normalised principal point where it is -- the rule the
15
+ models were trained with, which assumes a centred principal point. Padding moves the
16
+ principal point with the content.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from collections.abc import Callable
22
+
23
+ import numpy as np
24
+ import torch
25
+ import torchvision.transforms.functional as F
26
+ from PIL import Image
27
+
28
+ RESIZE_MODES = ("lanczos", "bilinear")
29
+ CROP_MODES = ("crop", "pad", "none")
30
+
31
+ # (images [..., 3, h, w], (h_out, w_out)) -> images [..., 3, h_out, w_out]
32
+ Resize = Callable[[torch.Tensor, tuple[int, int]], torch.Tensor]
33
+
34
+
35
+ def rescale(
36
+ images: torch.Tensor,
37
+ shape: tuple[int, int],
38
+ resize_mode: str = "lanczos",
39
+ ) -> torch.Tensor:
40
+ """[..., 3, h_in, w_in] float in [0, 1] -> [..., 3, h_out, w_out]."""
41
+ *batch, c, h, w = images.shape
42
+ images = images.reshape(-1, c, h, w)
43
+ h_out, w_out = shape
44
+
45
+ if resize_mode == "bilinear":
46
+ images_new = F.resize(
47
+ images,
48
+ (h_out, w_out),
49
+ interpolation=F.InterpolationMode.BILINEAR,
50
+ antialias=True,
51
+ )
52
+ elif resize_mode == "lanczos":
53
+ # PIL takes uint8 only, so this path quantises to 8 bit before resampling.
54
+ device, dtype = images.device, images.dtype
55
+ imgs_np = (images * 255).clip(min=0, max=255).to(torch.uint8)
56
+ imgs_np = imgs_np.permute(0, 2, 3, 1).contiguous().cpu().numpy()
57
+ rescaled_list = [
58
+ np.array(Image.fromarray(img).resize((w_out, h_out), Image.LANCZOS))
59
+ for img in imgs_np
60
+ ]
61
+ images_new = torch.from_numpy(np.stack(rescaled_list) / 255.0)
62
+ images_new = images_new.to(dtype=dtype, device=device).permute(0, 3, 1, 2)
63
+ else:
64
+ raise ValueError(
65
+ f"Unknown resize_mode {resize_mode!r}; expected one of {RESIZE_MODES}."
66
+ )
67
+
68
+ return images_new.reshape(*batch, c, h_out, w_out)
69
+
70
+
71
+ def center_crop(
72
+ images: torch.Tensor,
73
+ intrinsics: torch.Tensor,
74
+ shape: tuple[int, int],
75
+ ) -> tuple[torch.Tensor, torch.Tensor]:
76
+ *_, h_in, w_in = images.shape
77
+ h_out, w_out = shape
78
+
79
+ # Odd size differences induce half-pixel misalignments, as in training.
80
+ row = (h_in - h_out) // 2
81
+ col = (w_in - w_out) // 2
82
+ images = images[..., :, row : row + h_out, col : col + w_out]
83
+
84
+ intrinsics = intrinsics.clone()
85
+ intrinsics[..., 0, 0] *= w_in / w_out # fx
86
+ intrinsics[..., 1, 1] *= h_in / h_out # fy
87
+ return images, intrinsics
88
+
89
+
90
+ def scaled_shape(
91
+ image_shape: tuple[int, int], shape: tuple[int, int], crop_mode: str
92
+ ) -> tuple[int, int]:
93
+ """The size an image is resized to before the crop / pad to `shape`."""
94
+ h_in, w_in = image_shape
95
+ h_out, w_out = shape
96
+ if crop_mode == "none":
97
+ return h_in, w_in
98
+ pick = max if crop_mode == "crop" else min
99
+ scale_factor = pick(h_out / h_in, w_out / w_in)
100
+ h_scaled = round(h_in * scale_factor)
101
+ w_scaled = round(w_in * scale_factor)
102
+ assert h_scaled == h_out or w_scaled == w_out
103
+ return h_scaled, w_scaled
104
+
105
+
106
+ def rescale_and_crop(
107
+ images: torch.Tensor,
108
+ intrinsics: torch.Tensor,
109
+ shape: tuple[int, int],
110
+ resize_mode: str = "lanczos",
111
+ resize: Resize | None = None,
112
+ ) -> tuple[torch.Tensor, torch.Tensor]:
113
+ """Scale so the image covers `shape`, then crop the middle.
114
+
115
+ `resize` replaces the built-in `resize_mode` kernels when given.
116
+ """
117
+ scaled = scaled_shape(tuple(images.shape[-2:]), shape, "crop")
118
+ if resize is None:
119
+ images = rescale(images, scaled, resize_mode)
120
+ else:
121
+ images = resize(images, scaled)
122
+ return center_crop(images, intrinsics, shape)
123
+
124
+
125
+ def rescale_and_pad(
126
+ images: torch.Tensor,
127
+ intrinsics: torch.Tensor,
128
+ shape: tuple[int, int],
129
+ resize_mode: str = "lanczos",
130
+ resize: Resize | None = None,
131
+ pad_value: float = 0.0,
132
+ ) -> tuple[torch.Tensor, torch.Tensor]:
133
+ """Scale so the image fits inside `shape`, then pad around it (centred)."""
134
+ image_shape = tuple(images.shape[-2:])
135
+ h_scaled, w_scaled = scaled_shape(image_shape, shape, "pad")
136
+ if resize is None:
137
+ images = rescale(images, (h_scaled, w_scaled), resize_mode)
138
+ else:
139
+ images = resize(images, (h_scaled, w_scaled))
140
+ h_out, w_out = shape
141
+ top, left = (h_out - h_scaled) // 2, (w_out - w_scaled) // 2
142
+ padded = images.new_full((*images.shape[:-2], h_out, w_out), pad_value)
143
+ padded[..., top : top + h_scaled, left : left + w_scaled] = images
144
+ return padded, fit_intrinsics(intrinsics, image_shape, shape, "pad")
145
+
146
+
147
+ def fit_intrinsics(
148
+ intrinsics: torch.Tensor,
149
+ image_shape: tuple[int, int],
150
+ shape: tuple[int, int],
151
+ crop_mode: str = "crop",
152
+ ) -> torch.Tensor:
153
+ """Normalised K for an `image_shape` image after `fit_images` to `shape`."""
154
+ check_crop_mode(crop_mode)
155
+ h_out, w_out = shape
156
+ if crop_mode == "none":
157
+ check_shape(image_shape, shape)
158
+ return intrinsics.clone()
159
+ h_scaled, w_scaled = scaled_shape(image_shape, shape, crop_mode)
160
+ k = intrinsics.clone()
161
+ k[..., 0, 0] *= w_scaled / w_out
162
+ k[..., 1, 1] *= h_scaled / h_out
163
+ if crop_mode == "pad":
164
+ top, left = (h_out - h_scaled) // 2, (w_out - w_scaled) // 2
165
+ k[..., 0, 2] = (k[..., 0, 2] * w_scaled + left) / w_out
166
+ k[..., 1, 2] = (k[..., 1, 2] * h_scaled + top) / h_out
167
+ return k
168
+
169
+
170
+ def fit_images(
171
+ images: torch.Tensor,
172
+ intrinsics: torch.Tensor,
173
+ shape: tuple[int, int],
174
+ resize_mode: str = "lanczos",
175
+ crop_mode: str = "crop",
176
+ resize: Resize | None = None,
177
+ pad_value: float = 0.0,
178
+ ) -> tuple[torch.Tensor, torch.Tensor]:
179
+ """[..., 3, h, w] images + normalised K -> `shape` by `crop_mode`."""
180
+ check_crop_mode(crop_mode)
181
+ shape = tuple(shape)
182
+ if crop_mode == "crop":
183
+ return rescale_and_crop(images, intrinsics, shape, resize_mode, resize)
184
+ if crop_mode == "pad":
185
+ return rescale_and_pad(
186
+ images, intrinsics, shape, resize_mode, resize, pad_value
187
+ )
188
+ check_shape(tuple(images.shape[-2:]), shape)
189
+ return images, intrinsics
190
+
191
+
192
+ def check_crop_mode(crop_mode: str) -> None:
193
+ if crop_mode not in CROP_MODES:
194
+ raise ValueError(
195
+ f"Unknown crop_mode {crop_mode!r}; expected one of {CROP_MODES}."
196
+ )
197
+
198
+
199
+ def check_shape(image_shape: tuple[int, int], shape: tuple[int, int]) -> None:
200
+ if tuple(image_shape) != tuple(shape):
201
+ raise ValueError(
202
+ f'crop_mode="none" needs inputs at the model shape {tuple(shape)}, '
203
+ f"got {tuple(image_shape)}"
204
+ )
@@ -0,0 +1,2 @@
1
+ """Built-in models, each imported only when its `model_type` is first loaded
2
+ (see `ffgs.registry`)."""