How to convert .nii image to dicom?

I have downloaded a brain image dataset, where images are in .nii format. Please tell me how to convert these images to .dicom format, without any loss of image information.

You could use Slicer, see docs.

For SimpleITK we have an example that takes a 3d NumPy array and writes out a Dicom series. You can see it here:

https://simpleitk.readthedocs.io/en/master/link_DicomSeriesFromArray_docs.html

In your case you’d just remove the code that creates the NumPy array and replace it with a call to SimpleITK’s ReadImage function.

Thank you @dchen . Did that. But getting the following exception.

RuntimeError: Exception thrown in SimpleITK new_Image: D:\SimpleITK\Code\Common\src\sitkImageExplicit.cxx:102:
sitk::ERROR: Unsupported number of dimensions specified by size: [ 9830400 ]!
The maximum supported Image dimension is 5.

Can you show your code? Looks like you’re passing the wrong parameters to the SimpleITK Image constructor.

Yes, I do not know how to pass arguments and what arguments should be passed.
The code is the same as given [here]. (Dicom Series From Array — SimpleITK 2.0rc2 documentation)
Only value of new_arr is changed.

import SimpleITK as sitk

import sys
import time
import os
import numpy as np

pixel_dtypes = {"int16": np.int16, "float64": np.float64}


def writeSlices(series_tag_values, new_img, out_dir, i):
    image_slice = new_img[:, :, i]

    # Tags shared by the series.
    list(
        map(
            lambda tag_value: image_slice.SetMetaData(
                tag_value[0], tag_value[1]
            ),
            series_tag_values,
        )
    )

    # Slice specific tags.
    #   Instance Creation Date
    image_slice.SetMetaData("0008|0012", time.strftime("%Y%m%d"))
    #   Instance Creation Time
    image_slice.SetMetaData("0008|0013", time.strftime("%H%M%S"))

    # Setting the type to CT so that the slice location is preserved and
    # the thickness is carried over.
    image_slice.SetMetaData("0008|0060", "CT")

    # (0020, 0032) image position patient determines the 3D spacing between
    # slices.
    #   Image Position (Patient)
    image_slice.SetMetaData(
        "0020|0032",
        "\\".join(map(str, new_img.TransformIndexToPhysicalPoint((0, 0, i)))),
    )
    #   Instance Number
    image_slice.SetMetaData("0020|0013", str(i))

    # Write to the output directory and add the extension dcm, to force
    # writing in DICOM format.
    writer.SetFileName(os.path.join(out_dir, str(i) + ".dcm"))
    writer.Execute(image_slice)


if len(sys.argv) < 3:
    print(
        "Usage: python "
        + __file__
        + " <output_directory> ["
        + ", ".join(pixel_dtypes)
        + "]"
    )
    sys.exit(1)

# Create a new series from a numpy array
try:
    pixel_dtype = pixel_dtypes[sys.argv[2]]
except KeyError:
    pixel_dtype = pixel_dtypes["int16"]

# new_arr = np.random.uniform(-10, 10, size=(3, 4, 5)).astype(pixel_dtype)
new_arr =  sitk.ReadImage('C:/Users/Desktop/NIfTI/rest.nii', sitk.sitkFloat32)
new_img = sitk.GetImageFromArray(new_arr)
new_img.SetSpacing([2.5, 3.5, 4.5])

# Write the 3D image as a series
# IMPORTANT: There are many DICOM tags that need to be updated when you modify
#            an original image. This is a delicate operation and requires
#            knowledge of the DICOM standard. This example only modifies some.
#            For a more complete list of tags that need to be modified see:
#                  http://gdcm.sourceforge.net/wiki/index.php/Writing_DICOM
#            If it is critical for your work to generate valid DICOM files,
#            It is recommended to use David Clunie's Dicom3tools to validate
#            the files:
#                  http://www.dclunie.com/dicom3tools.html

writer = sitk.ImageFileWriter()
# Use the study/series/frame of reference information given in the meta-data
# dictionary and not the automatically generated information from the file IO
writer.KeepOriginalImageUIDOn()

modification_time = time.strftime("%H%M%S")
modification_date = time.strftime("%Y%m%d")

# Copy some of the tags and add the relevant tags indicating the change.
# For the series instance UID (0020|000e), each of the components is a number,
# cannot start with zero, and separated by a '.' We create a unique series ID
# using the date and time. Tags of interest:
direction = new_img.GetDirection()
series_tag_values = [
    ("0008|0031", modification_time),  # Series Time
    ("0008|0021", modification_date),  # Series Date
    ("0008|0008", "DERIVED\\SECONDARY"),  # Image Type
    (
        "0020|000e",
        "1.2.826.0.1.3680043.2.1125."
        + modification_date
        + ".1"
        + modification_time,
    ),  # Series Instance UID
    (
        "0020|0037",
        "\\".join(
            map(
                str,
                (
                    direction[0],
                    direction[3],
                    direction[6],
                    direction[1],
                    direction[4],
                    direction[7],
                ),
            )
        ),
    ),  # Image Orientation
    # (Patient)
    ("0008|103e", "Created-SimpleITK"),  # Series Description
]

if pixel_dtype == np.float64:
    # If we want to write floating point values, we need to use the rescale
    # slope, "0028|1053", to select the number of digits we want to keep. We
    # also need to specify additional pixel storage and representation
    # information.
    rescale_slope = 0.001  # keep three digits after the decimal point
    series_tag_values = series_tag_values + [
        ("0028|1053", str(rescale_slope)),  # rescale slope
        ("0028|1052", "0"),  # rescale intercept
        ("0028|0100", "16"),  # bits allocated
        ("0028|0101", "16"),  # bits stored
        ("0028|0102", "15"),  # high bit
        ("0028|0103", "1"),
    ]  # pixel representation

# Write slices to output directory
list(
    map(
        lambda i: writeSlices(series_tag_values, new_img, sys.argv[1], i),
        range(new_img.GetDepth()),
    )
)

# Re-read the series
# Read the original series. First obtain the series file names using the
# image series reader.
data_directory = sys.argv[1]
series_IDs = sitk.ImageSeriesReader.GetGDCMSeriesIDs(data_directory)
if not series_IDs:
    print(
        'ERROR: given directory "'
        + data_directory
        + '" does not contain a DICOM series.'
    )
    sys.exit(1)
series_file_names = sitk.ImageSeriesReader.GetGDCMSeriesFileNames(
    data_directory, series_IDs[0]
)

series_reader = sitk.ImageSeriesReader()
series_reader.SetFileNames(series_file_names)

# Configure the reader to load all of the DICOM tags (public+private):
# By default tags are not loaded (saves time).
# By default if tags are loaded, the private tags are not loaded.
# We explicitly configure the reader to load tags, including the
# private ones.
series_reader.LoadPrivateTagsOn()
image3D = series_reader.Execute()
print(image3D.GetSpacing(), "vs", new_img.GetSpacing())
sys.exit(0)

You need to remove those two lines. Those were part of creating the SimpleITK image from the NumPy array.

Also the ReadImage call should go into new_img, not new_arr.

1 Like

Now I am getting a different Exception

RuntimeError: Exception thrown in SimpleITK ImageFileWriter_Execute: D:\ITK\Modules\IO\ImageBase\src\itkImageIOBase.cxx:703:
ITK ERROR: GDCMImageIO(000002773ACB8190): Could not open file: -f\0.dcm for writing.
Reason: No such file or directory

How are you running the script? It’s expecting the output directory as the first argument, like so:

DicomSeriesFromArray.py output_directory

It looks like it is trying to write to an output directory of “-f”, which is probably incorrect.

I am running it on Jupyter Notebook. What is the correct process of running?

Change this line so that data_directory is set to the directory you want the Dicom slices to be written to. The script was intended to by invoke on the command like with the data_directory given as an argument.

In your case, I guess sys.argv is coming from how you start up the Jupiter notebook.

sys.argv is called in many other places in the code. For example,

if len(sys.argv) < 3:
    print(
        "Usage: python "
        + __file__
        + " <output_directory> ["
        + ", ".join(pixel_dtypes)
        + "]"
    )
    sys.exit(1)
try:
    pixel_dtype = pixel_dtypes[sys.argv[2]]
except KeyError:
    pixel_dtype = pixel_dtypes["int16"]

and

list(
    map(
        lambda i: writeSlices(series_tag_values, new_img, sys.argv[1], i),
        range(new_img.GetDepth()),
    )
)

How should I change these?

The first two sections you can just get rid of. Just delete that code.

That 3rd section is key. Change sys.argv[1] to your output directory.

Done that. Now I am getting the following Exception

RuntimeError: Exception thrown in SimpleITK ImageFileWriter_Execute: D:\ITK\Modules\IO\GDCM\src\itkGDCMImageIO.cxx:1222:
ITK ERROR: GDCMImageIO(000002773ACBA180): A Floating point buffer was passed but the stored pixel type was not specified.This is currently not supported

You’ve loaded the Nifti as a Float32 file, maybe try sitk.sitkInt16? It looks like you’ve hit some sort of issue with the pixel type and GDCM Dicom writer.

Thank you @dchen. Finally I could run the code without error, and got to see the dicom files.
However, each dicom slice is appearing as a different file, and I have to click on each file to see each slice. Is there a way, these slices can be combined into a single series?