Abstract
During my internship for my master’s degree in computer vision, I worked on a project that used U-Net to segment tumors throughout the body.
The project was difficult and included several tasks, especially data preparation. During data preparation, I tried different approaches to improve the model results. One of those steps was creating smaller NIfTI files with 128 slices each. However, my dataset contained patients with different numbers of slices, so I needed a consistent way to convert and reorganize the data before creating the smaller NIfTI files.
There are tools that can convert NIfTI files into DICOM series, including 3D Slicer and other medical imaging utilities. At the time, I did not have a simple Python function that fit my workflow, so I manually converted files one by one with 3D Slicer. It took a long time to finish this conversion.
At the time, I did not think about writing my own function to automate the conversion. Later, I decided to build one so that anyone who needs this type of conversion for a similar workflow can use it.
The basic idea is to read the image data from the NIfTI file, split the volume into slices, and save each slice as one DICOM instance in a DICOM series. The important detail is that a DICOM file is not only pixel data; it also needs metadata such as image size, spacing, orientation, slice position, and identifiers.
The inspiration for this conversion came from a function I wrote to convert JPG or PNG images to DICOM files.
In this blog, I will show how to create a simple conversion function and make this workflow easier.
The steps
- Using a pre-existing DICOM file.
- The packages that we need.
- Extract the image data from the NIfTI file.
- Prepare the array to be converted.
- Convert one NIfTI file into a DICOM series.
- Convert multiple NIfTI files into multiple DICOM series.
Using a Pre-existing DICOM File
You may be wondering why we need an existing DICOM file if we are trying to create a DICOM series. A DICOM file contains more than pixel values, so using a pre-existing DICOM file as a template is a simple way to keep the required metadata structure. In a production or clinical workflow, you should also update the metadata carefully so it matches the new image data, including spacing, orientation, slice position, and identifiers.
If you don’t have a DICOM file, don’t worry; the GitHub repository linked below includes a sample DICOM file you can use as a template.
The Packages that We Need
To complete this conversion, you need to install a few Python packages:
- nibabel:
pip install nibabel - pydicom:
pip install pydicom - numpy:
pip install numpy - tqdm:
pip install tqdm(this one is just to print the progress of the conversion)
Extract the Image Data From the NIfTI File
First, we need to extract the image data from the NIfTI file. In this example, the NIfTI image is loaded as a 3D NumPy array, and each 2D slice is taken from one axis of that volume.
To do this, you can use the nibabel library to load the NIfTI file, then call get_fdata to get the image data as a NumPy array.
import nibabel
nifti_file = nibabel.load(nifti_dir)
nifti_array = nifti_file.get_fdata()
Prepare the Array to be Converted
After extracting the image data, we prepare each slice so it can be saved as DICOM pixel data. In this example, we convert the pixel values to 16-bit unsigned integers and update the template DICOM fields to match the new array size. Depending on your data, you may need to rescale or clip intensities before converting them to uint16.
Here is the function I created (it is based on the same idea used for the JPG and PNG conversion).
def convertNsave(arr,file_dir, index=0):
"""
`arr`: parameter will take a numpy array that represents only one slice.
`file_dir`: parameter will take the path to save the slices
`index`: parameter will represent the index of the slice, so this parameter will be used to put
the name of each slice while using a for loop to convert all the slices
"""
dicom_file = pydicom.dcmread('images/dcmimage.dcm')
arr = arr.astype('uint16')
dicom_file.Rows = arr.shape[0]
dicom_file.Columns = arr.shape[1]
dicom_file.PhotometricInterpretation = "MONOCHROME2"
dicom_file.SamplesPerPixel = 1
dicom_file.BitsStored = 16
dicom_file.BitsAllocated = 16
dicom_file.HighBit = 15
dicom_file.PixelRepresentation = 0
dicom_file.PixelData = arr.tobytes()
After setting these parameters, we can save the DICOM file using pydicom’s save function.
dicom_file.save_as(os.path.join(file_dir, f'slice{index}.dcm'))
Note: This function converts one slice. To convert a full NIfTI file, we call it for every slice in the volume. For clinically reliable DICOM output, you should also update per-slice metadata such as InstanceNumber, SOPInstanceUID, ImagePositionPatient, and related spacing/orientation fields.
Convert One NIfTI File into a DICOM Series
As I mentioned in the previous paragraph, the function convertNsave will convert one slice only.
For that, I created the function nifti2dicom_1file so you can convert one NIfTI file directly. In the next step, I will show how to convert multiple NIfTI files.
Here is the function nifti2dicom_1file to convert one file:
def nifti2dicom_1file(nifti_dir, out_dir):
"""
This function is to convert only one nifti file into dicom series
`nifti_dir`: the path to the one nifti file
`out_dir`: the path to output
"""
nifti_file = nibabel.load(nifti_dir)
nifti_array = nifti_file.get_fdata()
number_slices = nifti_array.shape[2]
for slice_ in tqdm(range(number_slices)):
convertNsave(nifti_array[:,:,slice_], out_dir, slice_)As you can see, the function convertNsave is at the heart of this workflow. The loop passes through all slices in the NIfTI volume and saves each one as a DICOM file.
Convert Multiple NIfTI Files into Multiple DICOM Series
To convert multiple NIfTI files, we can call nifti2dicom_1file multiple times inside a loop. Here is the script to do that:
def nifti2dicom_mfiles(nifti_dir, out_dir=''):
"""
This function is to convert multiple nifti files into dicom files
`nifti_dir`: You enter the global path to all of the nifti files here.
`out_dir`: Put the path to where you want to save all the dicoms here.
PS: Each nifti file's folders will be created automatically, so you do not need to create an empty folder for each patient.
"""
files = os.listdir(nifti_dir)
for file in files:
in_path = os.path.join(nifti_dir, file)
out_path = os.path.join(out_dir, file)
os.mkdir(out_path)
nifti2dicom_1file(in_path, out_path)
That’s the minimal code needed for this conversion workflow. I’ll also provide the GitHub link so you can clone the repository and use the functions directly.