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
@@ -0,0 +1,463 @@
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ #
3
+ # This source code is licensed under the Apache License, Version 2.0
4
+ # found in the LICENSE file in the root directory of this source tree.
5
+ #
6
+ # Modified by the libgs contributors, 2026, from
7
+ # src/model/encoder/vggt/layers/vision_transformer.py of
8
+ # https://github.com/InternRobotics/AnySplat (5f5e208): formatted with black, unused
9
+ # imports removed.
10
+ # Apache License 2.0: see the Apache-2.0 text in LICENSE (this directory).
11
+
12
+ # References:
13
+ # https://github.com/facebookresearch/dino/blob/main/vision_transformer.py
14
+ # https://github.com/rwightman/pytorch-image-models/tree/master/timm/models/vision_transformer.py
15
+
16
+ import logging
17
+ import math
18
+ from functools import partial
19
+ from typing import Callable, Sequence, Tuple, Union
20
+
21
+ import torch
22
+ import torch.nn as nn
23
+ from torch.nn.init import trunc_normal_
24
+ from torch.utils.checkpoint import checkpoint
25
+
26
+ from . import (
27
+ MemEffAttention,
28
+ Mlp,
29
+ PatchEmbed,
30
+ SwiGLUFFNFused,
31
+ )
32
+ from . import (
33
+ NestedTensorBlock as Block,
34
+ )
35
+
36
+ logger = logging.getLogger("dinov2")
37
+
38
+
39
+ def named_apply(
40
+ fn: Callable, module: nn.Module, name="", depth_first=True, include_root=False
41
+ ) -> nn.Module:
42
+ if not depth_first and include_root:
43
+ fn(module=module, name=name)
44
+ for child_name, child_module in module.named_children():
45
+ child_name = ".".join((name, child_name)) if name else child_name
46
+ named_apply(
47
+ fn=fn,
48
+ module=child_module,
49
+ name=child_name,
50
+ depth_first=depth_first,
51
+ include_root=True,
52
+ )
53
+ if depth_first and include_root:
54
+ fn(module=module, name=name)
55
+ return module
56
+
57
+
58
+ class BlockChunk(nn.ModuleList):
59
+ def forward(self, x):
60
+ for b in self:
61
+ x = b(x)
62
+ return x
63
+
64
+
65
+ class DinoVisionTransformer(nn.Module):
66
+ def __init__(
67
+ self,
68
+ img_size=224,
69
+ patch_size=16,
70
+ in_chans=3,
71
+ embed_dim=768,
72
+ depth=12,
73
+ num_heads=12,
74
+ mlp_ratio=4.0,
75
+ qkv_bias=True,
76
+ ffn_bias=True,
77
+ proj_bias=True,
78
+ drop_path_rate=0.0,
79
+ drop_path_uniform=False,
80
+ init_values=None, # for layerscale: None or 0 => no layerscale
81
+ embed_layer=PatchEmbed,
82
+ act_layer=nn.GELU,
83
+ block_fn=Block,
84
+ ffn_layer="mlp",
85
+ block_chunks=1,
86
+ num_register_tokens=0,
87
+ interpolate_antialias=False,
88
+ interpolate_offset=0.1,
89
+ qk_norm=False,
90
+ ):
91
+ """
92
+ Args:
93
+ img_size (int, tuple): input image size
94
+ patch_size (int, tuple): patch size
95
+ in_chans (int): number of input channels
96
+ embed_dim (int): embedding dimension
97
+ depth (int): depth of transformer
98
+ num_heads (int): number of attention heads
99
+ mlp_ratio (int): ratio of mlp hidden dim to embedding dim
100
+ qkv_bias (bool): enable bias for qkv if True
101
+ proj_bias (bool): enable bias for proj in attn if True
102
+ ffn_bias (bool): enable bias for ffn if True
103
+ drop_path_rate (float): stochastic depth rate
104
+ drop_path_uniform (bool): apply uniform drop rate across blocks
105
+ weight_init (str): weight init scheme
106
+ init_values (float): layer-scale init values
107
+ embed_layer (nn.Module): patch embedding layer
108
+ act_layer (nn.Module): MLP activation layer
109
+ block_fn (nn.Module): transformer block class
110
+ ffn_layer (str): "mlp", "swiglu", "swiglufused" or "identity"
111
+ block_chunks: (int) split block sequence into block_chunks units for FSDP wrap
112
+ num_register_tokens: (int) number of extra cls tokens (so-called "registers")
113
+ interpolate_antialias: (str) flag to apply anti-aliasing when interpolating positional embeddings
114
+ interpolate_offset: (float) work-around offset to apply when interpolating positional embeddings
115
+ """
116
+ super().__init__()
117
+ norm_layer = partial(nn.LayerNorm, eps=1e-6)
118
+
119
+ # tricky but makes it work
120
+ self.use_checkpoint = True
121
+ self.use_reentrant = False
122
+ #
123
+
124
+ self.num_features = self.embed_dim = (
125
+ embed_dim # num_features for consistency with other models
126
+ )
127
+ self.num_tokens = 1
128
+ self.n_blocks = depth
129
+ self.num_heads = num_heads
130
+ self.patch_size = patch_size
131
+ self.num_register_tokens = num_register_tokens
132
+ self.interpolate_antialias = interpolate_antialias
133
+ self.interpolate_offset = interpolate_offset
134
+
135
+ self.patch_embed = embed_layer(
136
+ img_size=img_size,
137
+ patch_size=patch_size,
138
+ in_chans=in_chans,
139
+ embed_dim=embed_dim,
140
+ )
141
+ num_patches = self.patch_embed.num_patches
142
+
143
+ self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim))
144
+ self.pos_embed = nn.Parameter(
145
+ torch.zeros(1, num_patches + self.num_tokens, embed_dim)
146
+ )
147
+ assert num_register_tokens >= 0
148
+ self.register_tokens = (
149
+ nn.Parameter(torch.zeros(1, num_register_tokens, embed_dim))
150
+ if num_register_tokens
151
+ else None
152
+ )
153
+
154
+ if drop_path_uniform is True:
155
+ dpr = [drop_path_rate] * depth
156
+ else:
157
+ dpr = [
158
+ x.item() for x in torch.linspace(0, drop_path_rate, depth)
159
+ ] # stochastic depth decay rule
160
+
161
+ if ffn_layer == "mlp":
162
+ logger.info("using MLP layer as FFN")
163
+ ffn_layer = Mlp
164
+ elif ffn_layer == "swiglufused" or ffn_layer == "swiglu":
165
+ logger.info("using SwiGLU layer as FFN")
166
+ ffn_layer = SwiGLUFFNFused
167
+ elif ffn_layer == "identity":
168
+ logger.info("using Identity layer as FFN")
169
+
170
+ def f(*args, **kwargs):
171
+ return nn.Identity()
172
+
173
+ ffn_layer = f
174
+ else:
175
+ raise NotImplementedError
176
+
177
+ blocks_list = [
178
+ block_fn(
179
+ dim=embed_dim,
180
+ num_heads=num_heads,
181
+ mlp_ratio=mlp_ratio,
182
+ qkv_bias=qkv_bias,
183
+ proj_bias=proj_bias,
184
+ ffn_bias=ffn_bias,
185
+ drop_path=dpr[i],
186
+ norm_layer=norm_layer,
187
+ act_layer=act_layer,
188
+ ffn_layer=ffn_layer,
189
+ init_values=init_values,
190
+ qk_norm=qk_norm,
191
+ )
192
+ for i in range(depth)
193
+ ]
194
+ if block_chunks > 0:
195
+ self.chunked_blocks = True
196
+ chunked_blocks = []
197
+ chunksize = depth // block_chunks
198
+ for i in range(0, depth, chunksize):
199
+ # this is to keep the block index consistent if we chunk the block list
200
+ chunked_blocks.append(
201
+ [nn.Identity()] * i + blocks_list[i : i + chunksize]
202
+ )
203
+ self.blocks = nn.ModuleList([BlockChunk(p) for p in chunked_blocks])
204
+ else:
205
+ self.chunked_blocks = False
206
+ self.blocks = nn.ModuleList(blocks_list)
207
+
208
+ self.norm = norm_layer(embed_dim)
209
+ self.head = nn.Identity()
210
+
211
+ self.mask_token = nn.Parameter(torch.zeros(1, embed_dim))
212
+
213
+ self.init_weights()
214
+
215
+ def init_weights(self):
216
+ trunc_normal_(self.pos_embed, std=0.02)
217
+ nn.init.normal_(self.cls_token, std=1e-6)
218
+ if self.register_tokens is not None:
219
+ nn.init.normal_(self.register_tokens, std=1e-6)
220
+ named_apply(init_weights_vit_timm, self)
221
+
222
+ def interpolate_pos_encoding(self, x, w, h):
223
+ previous_dtype = x.dtype
224
+ npatch = x.shape[1] - 1
225
+ N = self.pos_embed.shape[1] - 1
226
+ if npatch == N and w == h:
227
+ return self.pos_embed
228
+ pos_embed = self.pos_embed.float()
229
+ class_pos_embed = pos_embed[:, 0]
230
+ patch_pos_embed = pos_embed[:, 1:]
231
+ dim = x.shape[-1]
232
+ w0 = w // self.patch_size
233
+ h0 = h // self.patch_size
234
+ M = int(math.sqrt(N)) # Recover the number of patches in each dimension
235
+ assert N == M * M
236
+ kwargs = {}
237
+ if self.interpolate_offset:
238
+ # Historical kludge: add a small number to avoid floating point error in the interpolation, see https://github.com/facebookresearch/dino/issues/8
239
+ # Note: still needed for backward-compatibility, the underlying operators are using both output size and scale factors
240
+ sx = float(w0 + self.interpolate_offset) / M
241
+ sy = float(h0 + self.interpolate_offset) / M
242
+ kwargs["scale_factor"] = (sx, sy)
243
+ else:
244
+ # Simply specify an output size instead of a scale factor
245
+ kwargs["size"] = (w0, h0)
246
+ patch_pos_embed = nn.functional.interpolate(
247
+ patch_pos_embed.reshape(1, M, M, dim).permute(0, 3, 1, 2),
248
+ mode="bicubic",
249
+ antialias=self.interpolate_antialias,
250
+ **kwargs,
251
+ )
252
+ assert (w0, h0) == patch_pos_embed.shape[-2:]
253
+ patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)
254
+ return torch.cat((class_pos_embed.unsqueeze(0), patch_pos_embed), dim=1).to(
255
+ previous_dtype
256
+ )
257
+
258
+ def prepare_tokens_with_masks(self, x, masks=None):
259
+ B, nc, w, h = x.shape
260
+ x = self.patch_embed(x)
261
+ if masks is not None:
262
+ x = torch.where(
263
+ masks.unsqueeze(-1), self.mask_token.to(x.dtype).unsqueeze(0), x
264
+ )
265
+
266
+ x = torch.cat((self.cls_token.expand(x.shape[0], -1, -1), x), dim=1)
267
+ x = x + self.interpolate_pos_encoding(x, w, h)
268
+
269
+ if self.register_tokens is not None:
270
+ x = torch.cat(
271
+ (
272
+ x[:, :1],
273
+ self.register_tokens.expand(x.shape[0], -1, -1),
274
+ x[:, 1:],
275
+ ),
276
+ dim=1,
277
+ )
278
+
279
+ return x
280
+
281
+ def forward_features_list(self, x_list, masks_list):
282
+ x = [
283
+ self.prepare_tokens_with_masks(x, masks)
284
+ for x, masks in zip(x_list, masks_list)
285
+ ]
286
+
287
+ for blk in self.blocks:
288
+ if self.use_checkpoint:
289
+ x = checkpoint(blk, x, use_reentrant=self.use_reentrant)
290
+ else:
291
+ x = blk(x)
292
+
293
+ all_x = x
294
+ output = []
295
+ for x, masks in zip(all_x, masks_list):
296
+ x_norm = self.norm(x)
297
+ output.append(
298
+ {
299
+ "x_norm_clstoken": x_norm[:, 0],
300
+ "x_norm_regtokens": x_norm[:, 1 : self.num_register_tokens + 1],
301
+ "x_norm_patchtokens": x_norm[:, self.num_register_tokens + 1 :],
302
+ "x_prenorm": x,
303
+ "masks": masks,
304
+ }
305
+ )
306
+ return output
307
+
308
+ def forward_features(self, x, masks=None):
309
+ if isinstance(x, list):
310
+ return self.forward_features_list(x, masks)
311
+
312
+ x = self.prepare_tokens_with_masks(x, masks)
313
+
314
+ for blk in self.blocks:
315
+ if self.use_checkpoint:
316
+ x = checkpoint(blk, x, use_reentrant=self.use_reentrant)
317
+ else:
318
+ x = blk(x)
319
+
320
+ x_norm = self.norm(x)
321
+ return {
322
+ "x_norm_clstoken": x_norm[:, 0],
323
+ "x_norm_regtokens": x_norm[:, 1 : self.num_register_tokens + 1],
324
+ "x_norm_patchtokens": x_norm[:, self.num_register_tokens + 1 :],
325
+ "x_prenorm": x,
326
+ "masks": masks,
327
+ }
328
+
329
+ def _get_intermediate_layers_not_chunked(self, x, n=1):
330
+ x = self.prepare_tokens_with_masks(x)
331
+ # If n is an int, take the n last blocks. If it's a list, take them
332
+ output, total_block_len = [], len(self.blocks)
333
+ blocks_to_take = (
334
+ range(total_block_len - n, total_block_len) if isinstance(n, int) else n
335
+ )
336
+ for i, blk in enumerate(self.blocks):
337
+ x = blk(x)
338
+ if i in blocks_to_take:
339
+ output.append(x)
340
+ assert len(output) == len(
341
+ blocks_to_take
342
+ ), f"only {len(output)} / {len(blocks_to_take)} blocks found"
343
+ return output
344
+
345
+ def _get_intermediate_layers_chunked(self, x, n=1):
346
+ x = self.prepare_tokens_with_masks(x)
347
+ output, i, total_block_len = [], 0, len(self.blocks[-1])
348
+ # If n is an int, take the n last blocks. If it's a list, take them
349
+ blocks_to_take = (
350
+ range(total_block_len - n, total_block_len) if isinstance(n, int) else n
351
+ )
352
+ for block_chunk in self.blocks:
353
+ for blk in block_chunk[i:]: # Passing the nn.Identity()
354
+ x = blk(x)
355
+ if i in blocks_to_take:
356
+ output.append(x)
357
+ i += 1
358
+ assert len(output) == len(
359
+ blocks_to_take
360
+ ), f"only {len(output)} / {len(blocks_to_take)} blocks found"
361
+ return output
362
+
363
+ def get_intermediate_layers(
364
+ self,
365
+ x: torch.Tensor,
366
+ n: Union[int, Sequence] = 1, # Layers or n last layers to take
367
+ reshape: bool = False,
368
+ return_class_token: bool = False,
369
+ norm=True,
370
+ ) -> Tuple[Union[torch.Tensor, Tuple[torch.Tensor]]]:
371
+ if self.chunked_blocks:
372
+ outputs = self._get_intermediate_layers_chunked(x, n)
373
+ else:
374
+ outputs = self._get_intermediate_layers_not_chunked(x, n)
375
+ if norm:
376
+ outputs = [self.norm(out) for out in outputs]
377
+ class_tokens = [out[:, 0] for out in outputs]
378
+ outputs = [out[:, 1 + self.num_register_tokens :] for out in outputs]
379
+ if reshape:
380
+ B, _, w, h = x.shape
381
+ outputs = [
382
+ out.reshape(B, w // self.patch_size, h // self.patch_size, -1)
383
+ .permute(0, 3, 1, 2)
384
+ .contiguous()
385
+ for out in outputs
386
+ ]
387
+ if return_class_token:
388
+ return tuple(zip(outputs, class_tokens))
389
+ return tuple(outputs)
390
+
391
+ def forward(self, *args, is_training=True, **kwargs):
392
+ ret = self.forward_features(*args, **kwargs)
393
+ if is_training:
394
+ return ret
395
+ else:
396
+ return self.head(ret["x_norm_clstoken"])
397
+
398
+
399
+ def init_weights_vit_timm(module: nn.Module, name: str = ""):
400
+ """ViT weight initialization, original timm impl (for reproducibility)"""
401
+ if isinstance(module, nn.Linear):
402
+ trunc_normal_(module.weight, std=0.02)
403
+ if module.bias is not None:
404
+ nn.init.zeros_(module.bias)
405
+
406
+
407
+ def vit_small(patch_size=16, num_register_tokens=0, **kwargs):
408
+ model = DinoVisionTransformer(
409
+ patch_size=patch_size,
410
+ embed_dim=384,
411
+ depth=12,
412
+ num_heads=6,
413
+ mlp_ratio=4,
414
+ block_fn=partial(Block, attn_class=MemEffAttention),
415
+ num_register_tokens=num_register_tokens,
416
+ **kwargs,
417
+ )
418
+ return model
419
+
420
+
421
+ def vit_base(patch_size=16, num_register_tokens=0, **kwargs):
422
+ model = DinoVisionTransformer(
423
+ patch_size=patch_size,
424
+ embed_dim=768,
425
+ depth=12,
426
+ num_heads=12,
427
+ mlp_ratio=4,
428
+ block_fn=partial(Block, attn_class=MemEffAttention),
429
+ num_register_tokens=num_register_tokens,
430
+ **kwargs,
431
+ )
432
+ return model
433
+
434
+
435
+ def vit_large(patch_size=16, num_register_tokens=0, **kwargs):
436
+ model = DinoVisionTransformer(
437
+ patch_size=patch_size,
438
+ embed_dim=1024,
439
+ depth=24,
440
+ num_heads=16,
441
+ mlp_ratio=4,
442
+ block_fn=partial(Block, attn_class=MemEffAttention),
443
+ num_register_tokens=num_register_tokens,
444
+ **kwargs,
445
+ )
446
+ return model
447
+
448
+
449
+ def vit_giant2(patch_size=16, num_register_tokens=0, **kwargs):
450
+ """
451
+ Close to ViT-giant, with embed-dim 1536 and 24 heads => embed-dim per head 64
452
+ """
453
+ model = DinoVisionTransformer(
454
+ patch_size=patch_size,
455
+ embed_dim=1536,
456
+ depth=40,
457
+ num_heads=24,
458
+ mlp_ratio=4,
459
+ block_fn=partial(Block, attn_class=MemEffAttention),
460
+ num_register_tokens=num_register_tokens,
461
+ **kwargs,
462
+ )
463
+ return model
File without changes