| import torch | |
| import torch.nn as nn | |
| """ Positional encoding embedding. Code was taken from https://github.com/bmild/nerf. """ | |
| class Embedder: | |
| def __init__(self, **kwargs): | |
| self.kwargs = kwargs | |
| self.create_embedding_fn() | |
| def create_embedding_fn(self): | |
| embed_fns = [] | |
| d = self.kwargs['input_dims'] | |
| out_dim = 0 | |
| if self.kwargs['include_input']: | |
| embed_fns.append(lambda x: x) | |
| out_dim += d | |
| max_freq = self.kwargs['max_freq_log2'] | |
| N_freqs = self.kwargs['num_freqs'] | |
| if self.kwargs['log_sampling']: | |
| freq_bands = 2. ** torch.linspace(0., max_freq, N_freqs) | |
| else: | |
| freq_bands = torch.linspace(2. ** 0., 2. ** max_freq, N_freqs) | |
| for freq in freq_bands: | |
| for p_fn in self.kwargs['periodic_fns']: | |
| if self.kwargs['normalize']: | |
| embed_fns.append(lambda x, p_fn=p_fn, | |
| freq=freq: p_fn(x * freq) / freq) | |
| else: | |
| embed_fns.append(lambda x, p_fn=p_fn, | |
| freq=freq: p_fn(x * freq)) | |
| out_dim += d | |
| self.embed_fns = embed_fns | |
| self.out_dim = out_dim | |
| def embed(self, inputs): | |
| return torch.cat([fn(inputs) for fn in self.embed_fns], -1) | |
| def get_embedder(multires, normalize=False, input_dims=3): | |
| embed_kwargs = { | |
| 'include_input': True, | |
| 'input_dims': input_dims, | |
| 'max_freq_log2': multires - 1, | |
| 'num_freqs': multires, | |
| 'normalize': normalize, | |
| 'log_sampling': True, | |
| 'periodic_fns': [torch.sin, torch.cos], | |
| } | |
| embedder_obj = Embedder(**embed_kwargs) | |
| def embed(x, eo=embedder_obj): return eo.embed(x) | |
| return embed, embedder_obj.out_dim | |
| class Embedding(nn.Module): | |
| def __init__(self, in_channels, N_freqs, logscale=True, normalize=False): | |
| """ | |
| Defines a function that embeds x to (x, sin(2^k x), cos(2^k x), ...) | |
| in_channels: number of input channels (3 for both xyz and direction) | |
| """ | |
| super(Embedding, self).__init__() | |
| self.N_freqs = N_freqs | |
| self.in_channels = in_channels | |
| self.funcs = [torch.sin, torch.cos] | |
| self.out_channels = in_channels * (len(self.funcs) * N_freqs + 1) | |
| self.normalize = normalize | |
| if logscale: | |
| self.freq_bands = 2 ** torch.linspace(0, N_freqs - 1, N_freqs) | |
| else: | |
| self.freq_bands = torch.linspace(1, 2 ** (N_freqs - 1), N_freqs) | |
| def forward(self, x): | |
| """ | |
| Embeds x to (x, sin(2^k x), cos(2^k x), ...) | |
| Different from the paper, "x" is also in the output | |
| See https://github.com/bmild/nerf/issues/12 | |
| Inputs: | |
| x: (B, self.in_channels) | |
| Outputs: | |
| out: (B, self.out_channels) | |
| """ | |
| out = [x] | |
| for freq in self.freq_bands: | |
| for func in self.funcs: | |
| if self.normalize: | |
| out += [func(freq * x) / freq] | |
| else: | |
| out += [func(freq * x)] | |
| return torch.cat(out, -1) | |