souging/TRELLIS_TextTo3D
0
1from typing import *2import torch3import torch.nn as nn4from . import SparseTensor5 6__all__ = [7 'SparseDownsample',8 'SparseUpsample',9 'SparseSubdivide'10]11 12 13class SparseDownsample(nn.Module):14 """15 Downsample a sparse tensor by a factor of `factor`.16 Implemented as average pooling.17 """18 def __init__(self, factor: Union[int, Tuple[int, ...], List[int]]):19 super(SparseDownsample, self).__init__()20 self.factor = tuple(factor) if isinstance(factor, (list, tuple)) else factor21 22 def forward(self, input: SparseTensor) -> SparseTensor:23 DIM = input.coords.shape[-1] - 124 factor = self.factor if isinstance(self.factor, tuple) else (self.factor,) * DIM25 assert DIM == len(factor), 'Input coordinates must have the same dimension as the downsample factor.'26 27 coord = list(input.coords.unbind(dim=-1))28 for i, f in enumerate(factor):29 coord[i+1] = coord[i+1] // f30 31 MAX = [coord[i+1].max().item() + 1 for i in range(DIM)]32 OFFSET = torch.cumprod(torch.tensor(MAX[::-1]), 0).tolist()[::-1] + [1]33 code = sum([c * o for c, o in zip(coord, OFFSET)])34 code, idx = code.unique(return_inverse=True)35 36 new_feats = torch.scatter_reduce(37 torch.zeros(code.shape[0], input.feats.shape[1], device=input.feats.device, dtype=input.feats.dtype),38 dim=0,39 index=idx.unsqueeze(1).expand(-1, input.feats.shape[1]),40 src=input.feats,41 reduce='mean'42 )43 new_coords = torch.stack(44 [code // OFFSET[0]] +45 [(code // OFFSET[i+1]) % MAX[i] for i in range(DIM)],46 dim=-147 )48 out = SparseTensor(new_feats, new_coords, input.shape,)49 out._scale = tuple([s // f for s, f in zip(input._scale, factor)])50 out._spatial_cache = input._spatial_cache51 52 out.register_spatial_cache(f'upsample_{factor}_coords', input.coords)53 out.register_spatial_cache(f'upsample_{factor}_layout', input.layout)54 out.register_spatial_cache(f'upsample_{factor}_idx', idx)55 56 return out57 58 59class SparseUpsample(nn.Module):60 """61 Upsample a sparse tensor by a factor of `factor`.62 Implemented as nearest neighbor interpolation.63 """64 def __init__(self, factor: Union[int, Tuple[int, int, int], List[int]]):65 super(SparseUpsample, self).__init__()66 self.factor = tuple(factor) if isinstance(factor, (list, tuple)) else factor67 68 def forward(self, input: SparseTensor) -> SparseTensor:69 DIM = input.coords.shape[-1] - 170 factor = self.factor if isinstance(self.factor, tuple) else (self.factor,) * DIM71 assert DIM == len(factor), 'Input coordinates must have the same dimension as the upsample factor.'72 73 new_coords = input.get_spatial_cache(f'upsample_{factor}_coords')74 new_layout = input.get_spatial_cache(f'upsample_{factor}_layout')75 idx = input.get_spatial_cache(f'upsample_{factor}_idx')76 if any([x is None for x in [new_coords, new_layout, idx]]):77 raise ValueError('Upsample cache not found. SparseUpsample must be paired with SparseDownsample.')78 new_feats = input.feats[idx]79 out = SparseTensor(new_feats, new_coords, input.shape, new_layout)80 out._scale = tuple([s * f for s, f in zip(input._scale, factor)])81 out._spatial_cache = input._spatial_cache82 return out83 84class SparseSubdivide(nn.Module):85 """86 Upsample a sparse tensor by a factor of `factor`.87 Implemented as nearest neighbor interpolation.88 """89 def __init__(self):90 super(SparseSubdivide, self).__init__()91 92 def forward(self, input: SparseTensor) -> SparseTensor:93 DIM = input.coords.shape[-1] - 194 # upsample scale=2^DIM95 n_cube = torch.ones([2] * DIM, device=input.device, dtype=torch.int)96 n_coords = torch.nonzero(n_cube)97 n_coords = torch.cat([torch.zeros_like(n_coords[:, :1]), n_coords], dim=-1)98 factor = n_coords.shape[0]99 assert factor == 2 ** DIM100 # print(n_coords.shape)101 new_coords = input.coords.clone()102 new_coords[:, 1:] *= 2103 new_coords = new_coords.unsqueeze(1) + n_coords.unsqueeze(0).to(new_coords.dtype)104 105 new_feats = input.feats.unsqueeze(1).expand(input.feats.shape[0], factor, *input.feats.shape[1:])106 out = SparseTensor(new_feats.flatten(0, 1), new_coords.flatten(0, 1), input.shape)107 out._scale = input._scale * 2108 out._spatial_cache = input._spatial_cache109 return out110 111 