[docs]classToOrientation(SpatialTransform):"""Reorient the data to a specified orientation. This transform reorders the voxels and modifies the affine matrix to match the specified orientation code. The image intensity values are not modified, and the sample locations in the scanner space are preserved. Common orientation codes include: - ``'RAS'`` (neurological convention): - The first axis goes from Left to Right (R). - The second axis goes from Posterior to Anterior (A). - The third axis goes from Inferior to Superior (S). - ``'LAS'`` (radiological convention): - The first axis goes from Right to Left (L). - The second axis goes from Posterior to Anterior (A). - The third axis goes from Inferior to Superior (S). See `NiBabel docs about image orientation`_ for more information. Args: orientation: A three-letter orientation code. Examples: ``'RAS'``, ``'LAS'``, ``'LPS'``, ``'PLS'``, ``'SLP'``. The code must contain one character for each axis direction: R or L, A or P, and S or I. **kwargs: See :class:`~torchio.transforms.Transform` for additional keyword arguments. .. _NiBabel docs about image orientation: https://nipy.org/nibabel/image_orientation.html """def__init__(self,orientation:str='RAS',**kwargs):super().__init__(**kwargs)ifnotisinstance(orientation,str)orlen(orientation)!=3:message=f'Orientation must be a 3-letter string, got "{orientation}"'raiseValueError(message)valid_codes=set('RLAPIS')orientation=orientation.upper()all_valid=all(axisinvalid_codesforaxisinorientation)ifnotall_valid:message=('Orientation code must be composed of three distinct characters'f' in {valid_codes} but got "{orientation}"')raiseValueError(message)# Check for valid axis directionshas_sagittal='R'inorientationor'L'inorientationhas_coronal='A'inorientationor'P'inorientationhas_axial='S'inorientationor'I'inorientationhas_all=has_sagittalandhas_coronalandhas_axialifnothas_all:message=('Orientation code must include one character for each axis direction:'f' R or L, A or P, and S or I, but got "{orientation}"')raiseValueError(message)self.orientation=orientationdefapply_transform(self,subject:Subject)->Subject:forimageinsubject.get_images(intensity_only=False):current_orientation=''.join(nib.aff2axcodes(image.affine))# If the image is already in the target orientation, skip itifcurrent_orientation==self.orientation:continue# NIfTI images should have channels in 5th dimensionarray=rearrange(image.numpy(),'C W H D -> W H D 1 C')nii=nib.Nifti1Image(array,image.affine)# Compute transform from current orientation to target orientationcurrent_orientation=orientations.io_orientation(nii.affine)target_orientation=orientations.axcodes2ornt(tuple(self.orientation))transform=orientations.ornt_transform(current_orientation,target_orientation,)# Reorder voxelsreoriented_array=orientations.apply_orientation(nii.dataobj,transform)reoriented_array=rearrange(reoriented_array,'W H D 1 C -> C W H D')# Calculate the new affine matrix reflecting the reorientationreoriented_affine=nii.affine@orientations.inv_ornt_aff(transform,nii.shape)# Update the image data and affineimage.set_data(torch.from_numpy(reoriented_array.copy()))image.affine=reoriented_affinereturnsubject