Align `points_src` to `points_tgt` with respect to a shared xyz scale and z shift. It is similar to `align_affine` but scale and shift are applied to different dimensions. ### Parameters: - `points_src: torch.Tensor` of shape (..., N, 3) - `points_tgt: torch.Tensor` of shape (
(points_src: torch.Tensor, points_tgt: torch.Tensor, weight: Optional[torch.Tensor], trunc: Optional[Union[float, torch.Tensor]] = None)
| 249 | |
| 250 | |
| 251 | def align_points_scale_z_shift(points_src: torch.Tensor, points_tgt: torch.Tensor, weight: Optional[torch.Tensor], trunc: Optional[Union[float, torch.Tensor]] = None): |
| 252 | """ |
| 253 | Align `points_src` to `points_tgt` with respect to a shared xyz scale and z shift. |
| 254 | It is similar to `align_affine` but scale and shift are applied to different dimensions. |
| 255 | |
| 256 | ### Parameters: |
| 257 | - `points_src: torch.Tensor` of shape (..., N, 3) |
| 258 | - `points_tgt: torch.Tensor` of shape (..., N, 3) |
| 259 | - `weights: torch.Tensor` of shape (..., N) |
| 260 | |
| 261 | ### Returns: |
| 262 | - `scale: torch.Tensor` of shape (...). |
| 263 | - `shift: torch.Tensor` of shape (..., 3). x and y shifts are zeros. |
| 264 | """ |
| 265 | dtype, device = points_src.dtype, points_src.device |
| 266 | |
| 267 | # Flatten batch dimensions for simplicity |
| 268 | batch_shape, n = points_src.shape[:-2], points_src.shape[-2] |
| 269 | batch_size = math.prod(batch_shape) |
| 270 | points_src, points_tgt, weight = points_src.reshape(batch_size, n, 3), points_tgt.reshape(batch_size, n, 3), weight.reshape(batch_size, n) |
| 271 | |
| 272 | # Take anchors |
| 273 | anchor_where_batch, anchor_where_n = torch.where(weight > 0) |
| 274 | with torch.no_grad(): |
| 275 | zeros = torch.zeros(anchor_where_batch.shape[0], device=device, dtype=dtype) |
| 276 | points_src_anchor = torch.stack([zeros, zeros, points_src[anchor_where_batch, anchor_where_n, 2]], dim=-1) # (anchors, 3) |
| 277 | points_tgt_anchor = torch.stack([zeros, zeros, points_tgt[anchor_where_batch, anchor_where_n, 2]], dim=-1) # (anchors, 3) |
| 278 | |
| 279 | points_src_anchored = points_src[anchor_where_batch, :, :] - points_src_anchor[..., None, :] # (anchors, n, 3) |
| 280 | points_tgt_anchored = points_tgt[anchor_where_batch, :, :] - points_tgt_anchor[..., None, :] # (anchors, n, 3) |
| 281 | weight_anchored = weight[anchor_where_batch, :, None].expand(-1, -1, 3) # (anchors, n, 3) |
| 282 | |
| 283 | # Solve optimal scale and shift for each anchor |
| 284 | MAX_ELEMENTS = 2 ** 20 |
| 285 | scale, loss, index = split_batch_fwd(align, MAX_ELEMENTS // n, points_src_anchored.flatten(-2), points_tgt_anchored.flatten(-2), weight_anchored.flatten(-2), trunc) # (anchors,) |
| 286 | |
| 287 | loss, index_anchor = scatter_min(size=batch_size, dim=0, index=anchor_where_batch, src=loss) # (batch_size,) |
| 288 | |
| 289 | # Reproduce by indexing for shorter compute graph |
| 290 | index_2 = index[index_anchor] # (batch_size,) [0, 3n) |
| 291 | index_1 = anchor_where_n[index_anchor] * 3 + index_2 % 3 # (batch_size,) [0, 3n) |
| 292 | |
| 293 | zeros = torch.zeros((batch_size, n), device=device, dtype=dtype) |
| 294 | points_tgt_00z, points_src_00z = torch.stack([zeros, zeros, points_tgt[..., 2]], dim=-1), torch.stack([zeros, zeros, points_src[..., 2]], dim=-1) |
| 295 | tgt_1, src_1 = torch.gather(points_tgt_00z.flatten(-2), dim=1, index=index_1[..., None]).squeeze(-1), torch.gather(points_src_00z.flatten(-2), dim=1, index=index_1[..., None]).squeeze(-1) |
| 296 | tgt_2, src_2 = torch.gather(points_tgt.flatten(-2), dim=1, index=index_2[..., None]).squeeze(-1), torch.gather(points_src.flatten(-2), dim=1, index=index_2[..., None]).squeeze(-1) |
| 297 | |
| 298 | scale = (tgt_2 - tgt_1) / torch.where(src_2 != src_1, src_2 - src_1, 1.0) |
| 299 | shift = torch.gather(points_tgt_00z, dim=1, index=(index_1 // 3)[..., None, None].expand(-1, -1, 3)).squeeze(-2) - scale[..., None] * torch.gather(points_src_00z, dim=1, index=(index_1 // 3)[..., None, None].expand(-1, -1, 3)).squeeze(-2) |
| 300 | scale, shift = scale.reshape(batch_shape), shift.reshape(*batch_shape, 3) |
| 301 | |
| 302 | return scale, shift |
| 303 | |
| 304 | |
| 305 | def align_points_scale_xyz_shift(points_src: torch.Tensor, points_tgt: torch.Tensor, weight: Optional[torch.Tensor], trunc: Optional[Union[float, torch.Tensor]] = None, max_iters: int = 30, eps: float = 1e-6): |
nothing calls this directly
no test coverage detected