MCPcopy Create free account
hub / github.com/Project-MONAI/MONAI / __call__

Method __call__

monai/transforms/post/array.py:806–877  ·  view source on GitHub ↗

Args: points: landmark coordinates as ndarray/Tensor with shape (N, D), ordered as (Y, X) for 2D or (Z, Y, X) for 3D, where N is the number of landmarks and D is the spatial dimensionality. spatial_shape: spatial size as a sequence. If

(self, points: NdarrayOrTensor, spatial_shape: Sequence[int] | None = None)

Source from the content-addressed store, hash-verified

804 self.spatial_shape = None if spatial_shape is None else tuple(int(s) for s in spatial_shape)
805
806 def __call__(self, points: NdarrayOrTensor, spatial_shape: Sequence[int] | None = None) -> NdarrayOrTensor:
807 """
808 Args:
809 points: landmark coordinates as ndarray/Tensor with shape (N, D),
810 ordered as (Y, X) for 2D or (Z, Y, X) for 3D, where N is the number
811 of landmarks and D is the spatial dimensionality.
812 spatial_shape: spatial size as a sequence. If None, uses the value provided at construction.
813
814 Returns:
815 Heatmaps with shape (N, *spatial), one channel per landmark.
816
817 Raises:
818 ValueError: if points shape/dimension or spatial_shape is invalid.
819 """
820 original_points = points
821 points_t = convert_to_tensor(points, dtype=torch.float32, track_meta=False)
822
823 if points_t.ndim != 2:
824 raise ValueError(
825 f"Argument `points` must be a 2D array with shape (num_points, spatial_dims), got shape {points_t.shape}."
826 )
827
828 if points_t.shape[-1] not in (2, 3):
829 raise ValueError("GenerateHeatmap only supports 2D or 3D landmarks.")
830
831 device = points_t.device
832 num_points, spatial_dims = points_t.shape
833
834 target_shape = self._resolve_spatial_shape(spatial_shape, spatial_dims)
835 sigma = self._resolve_sigma(spatial_dims)
836
837 # Create sparse image with impulses at landmark locations
838 heatmap = torch.zeros((num_points, *target_shape), dtype=self.torch_dtype, device=device)
839 bounds_t = torch.as_tensor(target_shape, device=device, dtype=points_t.dtype)
840
841 for idx, center in enumerate(points_t):
842 if not torch.isfinite(center).all():
843 continue
844 if not ((center >= 0).all() and (center < bounds_t).all()):
845 continue
846 # Round to nearest integer for impulse placement, then clamp to valid index range
847 center_int = center.round().long()
848 # Clamp indices to [0, size-1] to avoid out-of-bounds (e.g., 9.7 rounds to 10 in size-10 array)
849 bounds_max = (bounds_t - 1).long()
850 center_int = torch.minimum(torch.maximum(center_int, torch.zeros_like(center_int)), bounds_max)
851 # Place impulse (use maximum in case of overlapping landmarks)
852 current_val = heatmap[idx][tuple(center_int)]
853 heatmap[idx][tuple(center_int)] = torch.maximum(
854 current_val, torch.tensor(1.0, dtype=self.torch_dtype, device=device)
855 )
856
857 # Apply Gaussian blur using GaussianFilter
858 # Reshape to (num_points, 1, *spatial) for per-channel filtering
859 heatmap_input = heatmap.unsqueeze(1) # Add channel dimension
860
861 gaussian_filter = GaussianFilter(
862 spatial_dims=spatial_dims, sigma=sigma, truncated=self.truncated, approx="erf", requires_grad=False
863 ).to(device=device, dtype=self.torch_dtype)

Callers

nothing calls this directly

Calls 6

_resolve_sigmaMethod · 0.95
convert_to_tensorFunction · 0.90
GaussianFilterClass · 0.90
convert_to_dst_typeFunction · 0.90
as_tensorMethod · 0.80

Tested by

no test coverage detected