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

Function compute_shape_offset

monai/data/utils.py:850–910  ·  view source on GitHub ↗

Given input and output affine, compute appropriate shapes in the output space based on the input array's shape. This function also returns the offset to put the shape in a good position with respect to the world coordinate system. Args: spatial_shape: input array's shap

(
    spatial_shape: np.ndarray | Sequence[int],
    in_affine: NdarrayOrTensor,
    out_affine: NdarrayOrTensor,
    scale_extent: bool = False,
)

Source from the content-addressed store, hash-verified

848
849
850def compute_shape_offset(
851 spatial_shape: np.ndarray | Sequence[int],
852 in_affine: NdarrayOrTensor,
853 out_affine: NdarrayOrTensor,
854 scale_extent: bool = False,
855) -> tuple[np.ndarray, np.ndarray]:
856 """
857 Given input and output affine, compute appropriate shapes
858 in the output space based on the input array's shape.
859 This function also returns the offset to put the shape
860 in a good position with respect to the world coordinate system.
861
862 Args:
863 spatial_shape: input array's shape
864 in_affine (matrix): 2D affine matrix
865 out_affine (matrix): 2D affine matrix
866 scale_extent: whether the scale is computed based on the spacing or the full extent of voxels, for example, for
867 a factor of 0.5 scaling:
868
869 option 1, "o" represents a voxel, scaling the distance between voxels::
870
871 o--o--o
872 o-----o
873
874 option 2, each voxel has a physical extent, scaling the full voxel extent::
875
876 | voxel 1 | voxel 2 | voxel 3 | voxel 4 |
877 | voxel 1 | voxel 2 |
878
879 Option 1 may reduce the number of locations that requiring interpolation. Option 2 is more resolution
880 agnostic, that is, resampling coordinates depend on the scaling factor, not on the number of voxels.
881 Default is False, using option 1 to compute the shape and offset.
882
883 """
884 shape = np.array(tuple(spatial_shape), copy=True, dtype=float)
885 sr = len(shape)
886 in_affine_ = convert_data_type(to_affine_nd(sr, in_affine), np.ndarray)[0]
887 out_affine_ = convert_data_type(to_affine_nd(sr, out_affine), np.ndarray)[0]
888 in_coords = [(-0.5, dim - 0.5) if scale_extent else (0.0, dim - 1.0) for dim in shape]
889 corners: np.ndarray = np.asarray(np.meshgrid(*in_coords, indexing="ij")).reshape((len(shape), -1))
890 corners = np.concatenate((corners, np.ones_like(corners[:1])))
891 try:
892 corners_out = np.linalg.solve(out_affine_, in_affine_) @ corners
893 except np.linalg.LinAlgError as e:
894 raise ValueError(f"Affine {out_affine_} is not invertible") from e
895 corners = in_affine_ @ corners
896 all_dist = corners_out[:-1].copy()
897 corners_out = corners_out[:-1] / corners_out[-1]
898 out_shape = np.round(np.ptp(corners_out, axis=1)) if scale_extent else np.round(np.ptp(corners_out, axis=1) + 1.0)
899 offset = None
900 for i in range(corners.shape[1]):
901 min_corner = np.min(all_dist - all_dist[:, i : i + 1], 1)
902 if np.allclose(min_corner, 0.0, rtol=AFFINE_TOL):
903 offset = corners[:-1, i] # corner is the smallest, shift the corner to origin
904 break
905 if offset is None: # otherwise make output image center aligned with the input image center
906 offset = in_affine_[:-1, :-1] @ (shape / 2.0) + in_affine_[:-1, -1] - out_affine_[:-1, :-1] @ (out_shape / 2.0)
907 if scale_extent:

Callers 7

spatial_resampleFunction · 0.90
__call__Method · 0.90
test_list_inputMethod · 0.90

Calls 5

convert_data_typeFunction · 0.90
to_affine_ndFunction · 0.85
arrayMethod · 0.80
astypeMethod · 0.80
appendMethod · 0.45

Tested by 5

test_list_inputMethod · 0.72

Used in the wild real call sites across dependent graphs

searching dependent graphs…