Download model.py from eelab-dev/Occlusion_Based_NLOS_model: direct link, hf CLI and curl.
- Browser
- Download file 5.02 kB
-
https://huggingface.co/eelab-dev/Occlusion_Based_NLOS_model/resolve/main/model.py
- Command line
-
hf download hf://eelab-dev/Occlusion_Based_NLOS_model/model.py
-
curl -L -o model.py https://huggingface.co/eelab-dev/Occlusion_Based_NLOS_model/resolve/main/model.py
5.02 kB
| """Standalone architecture extracted from the original lidarx implementation.""" | |
| import torch | |
| from torch import nn | |
| def coarse_projection_features(x: torch.Tensor) -> torch.Tensor: | |
| """Return `projection_coarse_32x8x8` features for `[B, 1, D, H, W]` tensors.""" | |
| if x.ndim != 5: | |
| msg = f"expected input with shape [batch, channel, depth, height, width], got {x.shape}" | |
| raise ValueError(msg) | |
| if x.shape[1] != 1: | |
| msg = f"expected one histogram channel, got {x.shape[1]}" | |
| raise ValueError(msg) | |
| x = x.squeeze(1) | |
| depth = x.shape[1] | |
| height = x.shape[2] | |
| width = x.shape[3] | |
| if depth % 32 != 0 or height % 8 != 0 or width % 8 != 0: | |
| msg = ( | |
| "hist coarse projection requires depth divisible by 32 and " | |
| f"height/width divisible by 8, got {(depth, height, width)}" | |
| ) | |
| raise ValueError(msg) | |
| global_features = torch.cat( | |
| [ | |
| x.mean(dim=(1, 2, 3), keepdim=False).unsqueeze(1), | |
| x.std(dim=(1, 2, 3), unbiased=False, keepdim=False).unsqueeze(1), | |
| x.amin(dim=(1, 2, 3), keepdim=False).unsqueeze(1), | |
| x.amax(dim=(1, 2, 3), keepdim=False).unsqueeze(1), | |
| ], | |
| dim=1, | |
| ) | |
| depth_features = torch.cat( | |
| [ | |
| x.mean(dim=(2, 3)), | |
| x.std(dim=(2, 3), unbiased=False), | |
| ], | |
| dim=1, | |
| ) | |
| row_features = x.mean(dim=(1, 3)) | |
| col_features = x.mean(dim=(1, 2)) | |
| coarse = ( | |
| x.reshape( | |
| x.shape[0], | |
| 32, | |
| depth // 32, | |
| 8, | |
| height // 8, | |
| 8, | |
| width // 8, | |
| ) | |
| .mean(dim=(2, 4, 6)) | |
| .reshape(x.shape[0], -1) | |
| ) | |
| return torch.cat( | |
| [global_features, depth_features, row_features, col_features, coarse], | |
| dim=1, | |
| ) | |
| def _coarse_grid_features( | |
| x: torch.Tensor, | |
| *, | |
| depth_bins: int, | |
| row_bins: int, | |
| col_bins: int, | |
| ) -> torch.Tensor: | |
| depth = x.shape[1] | |
| height = x.shape[2] | |
| width = x.shape[3] | |
| if depth % depth_bins != 0 or height % row_bins != 0 or width % col_bins != 0: | |
| msg = ( | |
| "hist coarse grid requires dimensions divisible by requested bins, " | |
| f"got volume {(depth, height, width)} and bins {(depth_bins, row_bins, col_bins)}" | |
| ) | |
| raise ValueError(msg) | |
| return ( | |
| x.reshape( | |
| x.shape[0], | |
| depth_bins, | |
| depth // depth_bins, | |
| row_bins, | |
| height // row_bins, | |
| col_bins, | |
| width // col_bins, | |
| ) | |
| .mean(dim=(2, 4, 6)) | |
| .reshape(x.shape[0], -1) | |
| ) | |
| def multiscale_coarse_projection_features(x: torch.Tensor) -> torch.Tensor: | |
| """Return coarse histogram features with added low-resolution context views.""" | |
| base = coarse_projection_features(x) | |
| volume = x.squeeze(1) | |
| depth_context = _coarse_grid_features(volume, depth_bins=16, row_bins=8, col_bins=8) | |
| spatial_context = _coarse_grid_features(volume, depth_bins=32, row_bins=4, col_bins=4) | |
| return torch.cat([base, depth_context, spatial_context], dim=1) | |
| class HistCoarseMultiScaleMLP(nn.Module): | |
| """Small supervised MLP over multi-scale coarse histogram projections. | |
| This keeps the strong non-backbone inductive bias of the coarse-only model, | |
| but gives the classifier both the best 32x8x8 block view and two coarser | |
| context views before a lightweight gated residual head. | |
| """ | |
| def __init__(self, hidden_features: int = 128, dropout: float = 0.2) -> None: | |
| super().__init__() | |
| bottleneck_features = max(1, hidden_features // 2) | |
| self.input_norm = nn.LazyBatchNorm1d() | |
| self.input_projection = nn.Sequential( | |
| nn.LazyLinear(hidden_features), | |
| nn.SiLU(inplace=True), | |
| nn.Dropout(dropout), | |
| ) | |
| self.residual = nn.Sequential( | |
| nn.Linear(hidden_features, hidden_features), | |
| nn.SiLU(inplace=True), | |
| nn.Dropout(dropout), | |
| nn.Linear(hidden_features, hidden_features), | |
| ) | |
| self.gate = nn.Sequential( | |
| nn.Linear(hidden_features, hidden_features), | |
| nn.Sigmoid(), | |
| ) | |
| self.head = nn.Sequential( | |
| nn.LayerNorm(hidden_features), | |
| nn.SiLU(inplace=True), | |
| nn.Dropout(dropout), | |
| nn.Linear(hidden_features, bottleneck_features), | |
| nn.SiLU(inplace=True), | |
| nn.Dropout(dropout), | |
| nn.Linear(bottleneck_features, 1), | |
| ) | |
| def forward_features(self, x: torch.Tensor) -> torch.Tensor: | |
| """Return the learned 128-D representation immediately before the classifier.""" | |
| features = self.input_norm(multiscale_coarse_projection_features(x)) | |
| embedding = self.input_projection(features) | |
| return embedding + self.gate(embedding) * self.residual(embedding) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| return self.head(self.forward_features(x)) | |