Skip to content
Snippets Groups Projects
Commit 984151df authored by fima's avatar fima :beers:
Browse files

Merge branch 'load_dicom' into 'main'

Load dicom

See merge request !67
parents 0282cba5 04d39fa7
No related branches found
No related tags found
1 merge request!67Load dicom
......@@ -12,16 +12,16 @@ Example:
import difflib
import os
import re
import struct
from pathlib import Path
import dask.array as da
import h5py
import nibabel as nib
import numpy as np
import olefile
import struct
import re
import dask.array as da
from pathlib import Path
import pydicom
import tifffile
from PIL import Image, UnidentifiedImageError
......@@ -30,6 +30,7 @@ from qim3d.io.logger import log
from qim3d.utils.internal_tools import sizeof, stringify_path
from qim3d.utils.system import Memory
class DataLoader:
"""Utility class for loading data from different file formats.
......@@ -438,6 +439,39 @@ class DataLoader:
else:
return vol
def load_dicom(self, path):
""" Load a DICOM file
Args:
path (str): Path to file
"""
dcm_data = pydicom.dcmread(path)
if self.return_metadata:
return dcm_data.pixel_array, dcm_data
else:
return dcm_data.pixel_array
def load_dicom_dir(self, path):
""" Load a directory of DICOM files into a numpy 3d array
Args:
path (str): Directory path
"""
# loop over all .dcm files in the directory
files = [f for f in os.listdir(path) if f.endswith('.dcm')]
files.sort()
# dicom_list contains the dicom objects with metadata
dicom_list = [pydicom.dcmread(os.path.join(path, f)) for f in files]
# vol contains the pixel data
vol = np.stack([dicom.pixel_array for dicom in dicom_list], axis=0)
if self.return_metadata:
return vol, dicom_list
else:
return vol
def load(self, path):
"""
Load a file or directory based on the given path.
......@@ -474,6 +508,8 @@ class DataLoader:
return self.load_nifti(path)
elif path.endswith((".vol",".vgi")):
return self.load_vol(path)
elif path.endswith((".dcm",".DCM")):
return self.load_dicom(path)
else:
try:
return self.load_pil(path)
......@@ -482,6 +518,10 @@ class DataLoader:
# Load a directory
elif os.path.isdir(path):
# load dicom if directory contains dicom files else load tiff stack as default
if any([f.endswith('.dcm') for f in os.listdir(path)]):
return self.load_dicom_dir(path)
else:
return self.load_tiff_stack(path)
# Fails
......
......@@ -21,13 +21,17 @@ Example:
```
"""
import datetime
import os
import h5py
import nibabel as nib
import numpy as np
import PIL
import pydicom
import tifffile
from pydicom.dataset import FileDataset, FileMetaDataset
from pydicom.uid import UID
from qim3d.io.logger import log
from qim3d.utils.internal_tools import sizeof, stringify_path
......@@ -179,6 +183,59 @@ class DataSaver:
with h5py.File(path, "w") as f:
f.create_dataset("dataset", data=data, compression="gzip" if self.compression else None)
def save_dicom(self, path, data):
""" Save data to a DICOM file to the given path.
Args:
path (str): The path to save file to
data (numpy.ndarray): The data to be saved
"""
# based on https://pydicom.github.io/pydicom/stable/auto_examples/input_output/plot_write_dicom.html
# Populate required values for file meta information
file_meta = FileMetaDataset()
file_meta.MediaStorageSOPClassUID = UID('1.2.840.10008.5.1.4.1.1.2')
file_meta.MediaStorageSOPInstanceUID = UID("1.2.3")
file_meta.ImplementationClassUID = UID("1.2.3.4")
# Create the FileDataset instance (initially no data elements, but file_meta
# supplied)
ds = FileDataset(path, {},
file_meta=file_meta, preamble=b"\0" * 128)
ds.PatientName = "Test^Firstname"
ds.PatientID = "123456"
ds.StudyInstanceUID = "1.2.3.4.5"
ds.SamplesPerPixel = 1
ds.PixelRepresentation = 0
ds.BitsStored = 16
ds.BitsAllocated = 16
ds.PhotometricInterpretation = "MONOCHROME2"
ds.Rows = data.shape[1]
ds.Columns = data.shape[2]
ds.NumberOfFrames = data.shape[0]
# Set the transfer syntax
ds.is_little_endian = True
ds.is_implicit_VR = True
# Set creation date/time
dt = datetime.datetime.now()
ds.ContentDate = dt.strftime('%Y%m%d')
timeStr = dt.strftime('%H%M%S.%f') # long format with micro seconds
ds.ContentTime = timeStr
# Needs to be here because of bug in pydicom
ds.file_meta.TransferSyntaxUID = pydicom.uid.ImplicitVRLittleEndian
# Reshape the data into a 1D array and convert to uint16
data_1d = data.ravel().astype(np.uint16)
# Convert the data to bytes
data_bytes = data_1d.tobytes()
# Add the data to the DICOM file
ds.PixelData = data_bytes
ds.save_as(path)
def save_PIL(self, path, data):
""" Save data to a PIL file to the given path.
......@@ -271,6 +328,8 @@ class DataSaver:
return self.save_h5(path, data)
elif path.endswith((".vol",".vgi")):
return self.save_vol(path, data)
elif path.endswith((".dcm",".DCM")):
return self.save_dicom(path, data)
elif path.endswith((".jpeg",".jpg", ".png")):
return self.save_PIL(path, data)
else:
......
......@@ -10,6 +10,7 @@ Pillow>=10.0.1,
plotly>=5.14.1,
scipy>=1.11.2,
seaborn>=0.12.2,
pydicom>=2.4.4,
setuptools>=68.0.0,
tifffile>=2023.4.12,
torch>=2.0.1,
......
......@@ -42,6 +42,7 @@ setup(
"h5py>=3.9.0",
"localthickness>=0.1.2",
"matplotlib>=3.8.0",
"pydicom>=2.4.4",
"monai>=1.2.0",
"numpy>=1.26.0",
"outputformat>=0.1.3",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment