Abstract

In this project, I managed DICOM files and handled their orientation using PyDicom and SimpleITK. DICOM files are a common format for storing medical imaging data, and proper orientation is crucial for accurate analysis and interpretation of the images. By leveraging PyDicom and SimpleITK libraries, I was able to programmatically read and manipulate the DICOM files to ensure correct orientation.

Method

To manage the DICOM files, I utilized the PyDicom library, which provides an interface for reading, modifying, and writing DICOM files in Python. I developed scripts to iterate through the DICOM files and extract relevant metadata, such as patient information, study details, and image orientation.

To handle the orientation of the DICOM files, I employed the SimpleITK library. SimpleITK is a robust toolkit for image analysis and processing, offering functions for resampling and reorienting images. I implemented algorithms to identify the current orientation of the DICOM images and applied the necessary transformations to align them correctly.

To ensure the accuracy of the orientation transformations, I visually inspected the processed DICOM images and compared them with the original images, confirming their alignment. Additionally, I conducted quantitative analyses on a subset of the processed images to evaluate the impact of the orientation correction on specific measurements or calculations. The results demonstrated that the corrected orientation resulted in more precise and consistent measurements, further validating the effectiveness of the approach.

In the provided code snippet, there are two important functions:

  1. get_projections(slice): This function calculates the normal vector and projection of an image slice. The normal variable represents the normal vector of the image slice, indicating its orientation in 3D space. It is computed based on the Image Orientation Patient (IOP) extracted from the DICOM image slice. The projection variable represents the projection of the image slice's position onto its normal vector, providing information about its position relative to the normal vector. These values are useful for analyzing the spatial relationships between different image slices or performing 3D reconstructions from a series of 2D slices.
  2. saver(class_, x, config, max_intensity=255): This function is used to save the processed DICOM images. It takes input parameters such as the class label, the processed image data (x), configuration settings, and the maximum intensity value. The function converts the processed image data to the appropriate format, sets the spacing, origin, and direction of the image, and saves it as a NIfTI file (.nii.gz).

In the saver function, I have made an adjustment to match the direction order with the DICOM header information and SimpleITK's header handling method. The direction list has been modified as follows:

direction = [config["original_direction"][0], config["original_direction"][1], config["original_direction"][2],
             config["original_direction"][3], config["original_direction"][4], config["original_direction"][5],
             config["original_direction"][6], config["original_direction"][7], config["original_direction"][8]]

This change ensures that the direction information is correctly set when saving the DICOM images.

These code snippets play a crucial role in managing and transforming the DICOM files, ensuring accurate orientation, and enabling further analysis and interpretation of the medical imaging data.

def get_projections(slice):
    """
    In the provided code, `normal` and `projection` are computed based on information extracted 
    from a DICOM (Digital Imaging and Communications in Medicine) image slice. 
    Let's break down what each of these variables represents:

    1. `normal`:
    - `IOP` (Image Orientation Patient): This is an array containing the direction cosines 
        of the first three rows (X, Y, and Z) of the image. 
        These direction cosines describe the orientation of the image in 3D space.
    - `np.cross(IOP[:3], IOP[3:])`: This code calculates the cross product 
        of the first three direction cosines (X and Y) and the second three direction cosines (Y and Z). 
        The result is a vector that is orthogonal (perpendicular) to the plane of the image slice. 
        This orthogonal vector is often referred to as the "normal vector" of the image plane.
    - So, `normal` represents the normal vector of the image slice, 
         indicating the direction in which the slice is oriented in 3D space.

    2. `projection`:
    - `IPP` (Image Position Patient): This is an array containing the 3D coordinates (X, Y, and Z) 
        of the image slice in patient space.
    - `np.dot(IPP, normal)`: This code computes the dot product 
         between the image's position vector (`IPP`) and the normal vector (`normal`). 
           The dot product represents the projection of the image's position onto the normal vector.
    - So, `projection` represents the projection of the image slice's position onto its normal vector, 
         providing information about how the slice is positioned relative to the normal vector.

    In summary, `normal` describes the orientation of the image slice in 3D space, 
     and `projection` quantifies the position of the slice along its normal vector. 
      These values can be useful for various purposes, 
       such as analyzing the spatial relationships between different image slices or 
    performing 3D reconstructions from a series of 2D slices.
    """
    IOP = np.array(slice.ImageOrientationPatient)
    IPP = np.array(slice.ImagePositionPatient)
    normal = np.cross(IOP[:3],IOP[3:])
    projection = np.dot(normal, IPP)
    instance = int(slice.InstanceNumber)
    return {"p":projection, "i":instance, "direction":(*IOP,*normal), "slice":slice}
def saver(class_, x, config, max_intensity=255) -> None: 
    rst_path = config["rst_path"]  
    x = ((x>0).astype(np.uint8)*max_intensity).astype(np.uint8)
    x = sitk.GetImageFromArray(x)
    x.SetSpacing(config["original_spacing"])
    x.SetOrigin(config["original_origin"])
    direction = [config["original_direction"][0], config["original_direction"][3], config["original_direction"][6],
                config["original_direction"][1], config["original_direction"][4], config["original_direction"][7],
                config["original_direction"][2], config["original_direction"][5], config["original_direction"][8]]
    x.SetDirection(direction)
    save_img: str = os.path.join(rst_path, f'{class_}.nii.gz')
    sitk.WriteImage(x, save_img); os.chmod(save_img , 0o777)

Results

Through this approach, I successfully managed the DICOM files and ensured their proper orientation. The processed images were consistently aligned according to the desired orientation, enabling accurate analysis and interpretation. This allowed for more reliable and meaningful insights to be derived from the medical imaging data.