Last active
January 12, 2024 14:09
-
-
Save sarthakpati/68226fee5eaf6affb5647d2e83f499a0 to your computer and use it in GitHub Desktop.
Downsample DICOM images and write out as DICOM or fall back to NIfTI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os, time, traceback, pathlib | |
import SimpleITK as sitk | |
def downsample_dicom( | |
dicom_path: str, output_path: str, downsample_factor: int = 0.5 | |
) -> None: | |
""" | |
Downsample DICOM images and save them to the output path. | |
Args: | |
dicom_path (str): The path to the DICOM images. | |
output_path (str): The path to save the downsampled DICOM images. | |
downsample_factor (int, optional): The downsample factor. Defaults to 0.5. | |
""" | |
if not output_path.endswith(".nii.gz"): | |
pathlib.Path(output_path).mkdir(parents=True, exist_ok=True) | |
dicom_detected = False | |
if os.path.isdir(dicom_path): | |
# Read DICOM images | |
reader = sitk.ImageSeriesReader() | |
dicom_names = reader.GetGDCMSeriesFileNames(dicom_path) | |
reader.SetFileNames(dicom_names) | |
reader.MetaDataDictionaryArrayUpdateOn() | |
image = reader.Execute() | |
dicom_detected = True | |
else: | |
# Read image using generic SimpleITK reader | |
image = sitk.ReadImage(dicom_path) | |
# Downsample DICOM images | |
downsampled_image = sitk.Resample( | |
image, | |
[ | |
int(image.GetWidth() * downsample_factor), | |
int(image.GetHeight() * downsample_factor), | |
int(image.GetDepth() * downsample_factor), | |
], | |
sitk.Transform(), | |
sitk.sitkLinear, | |
image.GetOrigin(), | |
image.GetSpacing(), | |
image.GetDirection(), | |
0, | |
image.GetPixelID(), | |
) | |
if dicom_detected: | |
# Save downsampled DICOM images | |
writer = sitk.ImageFileWriter() | |
tags_to_copy = [ | |
# "0010|0010", # Patient Name | |
"0010|0020", # Patient ID | |
"0010|0030", # Patient Birth Date | |
"0020|000D", # Study Instance UID, for machine consumption | |
"0020|0010", # Study ID, for human consumption | |
"0008|0020", # Study Date | |
"0008|0030", # Study Time | |
"0008|0050", # Accession Number | |
"0008|0060", # Modality | |
] | |
# 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") | |
direction = downsampled_image.GetDirection() | |
series_tag_values = [ | |
(k, reader.GetMetaData(0, k.lower())) | |
for k in tags_to_copy | |
if reader.HasMetaDataKey(0, k.lower()) | |
] + [ | |
("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", | |
reader.GetMetaData(0, "0008|103e") | |
if reader.HasMetaDataKey(0, "0008|103e") | |
else "" + " Processed-SimpleITK", | |
), # Series Description is an optional tag, so may not exist | |
] | |
try: | |
for i in range(downsampled_image.GetDepth()): | |
image_slice = downsampled_image[:, :, i] | |
# Tags shared by the series. | |
for tag, value in series_tag_values: | |
image_slice.SetMetaData(tag, value) | |
# 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")) | |
# Image Position (Patient) | |
image_slice.SetMetaData( | |
"0020|0032", | |
"\\".join( | |
map(str, downsampled_image.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(output_path, str(i) + ".dcm")) | |
writer.Execute(image_slice) | |
except Exception: | |
print("ERROR: Could not write DICOM image, falling back to NIfTI") | |
print("Traceback:", traceback.format_exc()) | |
sitk.WriteImage( | |
downsampled_image, | |
os.path.join(output_path, f"downsampled_{downsample_factor}.nii.gz"), | |
) | |
else: | |
# Save downsampled image | |
if not output_path.endswith(".nii.gz"): | |
output_path = os.path.join(output_path, f"downsampled_{downsample_factor}.nii.gz") | |
sitk.WriteImage( | |
downsampled_image, | |
os.path.join(output_path), | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment