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
Branches
Tags
1 merge request!67Load dicom
...@@ -12,16 +12,16 @@ Example: ...@@ -12,16 +12,16 @@ Example:
import difflib import difflib
import os import os
import re
import struct
from pathlib import Path from pathlib import Path
import dask.array as da
import h5py import h5py
import nibabel as nib import nibabel as nib
import numpy as np import numpy as np
import olefile import olefile
import struct import pydicom
import re
import dask.array as da
from pathlib import Path
import tifffile import tifffile
from PIL import Image, UnidentifiedImageError from PIL import Image, UnidentifiedImageError
...@@ -30,6 +30,7 @@ from qim3d.io.logger import log ...@@ -30,6 +30,7 @@ from qim3d.io.logger import log
from qim3d.utils.internal_tools import sizeof, stringify_path from qim3d.utils.internal_tools import sizeof, stringify_path
from qim3d.utils.system import Memory from qim3d.utils.system import Memory
class DataLoader: class DataLoader:
"""Utility class for loading data from different file formats. """Utility class for loading data from different file formats.
...@@ -438,6 +439,39 @@ class DataLoader: ...@@ -438,6 +439,39 @@ class DataLoader:
else: else:
return vol 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): def load(self, path):
""" """
Load a file or directory based on the given path. Load a file or directory based on the given path.
...@@ -474,6 +508,8 @@ class DataLoader: ...@@ -474,6 +508,8 @@ class DataLoader:
return self.load_nifti(path) return self.load_nifti(path)
elif path.endswith((".vol",".vgi")): elif path.endswith((".vol",".vgi")):
return self.load_vol(path) return self.load_vol(path)
elif path.endswith((".dcm",".DCM")):
return self.load_dicom(path)
else: else:
try: try:
return self.load_pil(path) return self.load_pil(path)
...@@ -482,6 +518,10 @@ class DataLoader: ...@@ -482,6 +518,10 @@ class DataLoader:
# Load a directory # Load a directory
elif os.path.isdir(path): 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) return self.load_tiff_stack(path)
# Fails # Fails
......
...@@ -21,13 +21,17 @@ Example: ...@@ -21,13 +21,17 @@ Example:
``` ```
""" """
import datetime
import os import os
import h5py import h5py
import nibabel as nib import nibabel as nib
import numpy as np import numpy as np
import PIL import PIL
import pydicom
import tifffile import tifffile
from pydicom.dataset import FileDataset, FileMetaDataset
from pydicom.uid import UID
from qim3d.io.logger import log from qim3d.io.logger import log
from qim3d.utils.internal_tools import sizeof, stringify_path from qim3d.utils.internal_tools import sizeof, stringify_path
...@@ -179,6 +183,59 @@ class DataSaver: ...@@ -179,6 +183,59 @@ class DataSaver:
with h5py.File(path, "w") as f: with h5py.File(path, "w") as f:
f.create_dataset("dataset", data=data, compression="gzip" if self.compression else None) 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): def save_PIL(self, path, data):
""" Save data to a PIL file to the given path. """ Save data to a PIL file to the given path.
...@@ -271,6 +328,8 @@ class DataSaver: ...@@ -271,6 +328,8 @@ class DataSaver:
return self.save_h5(path, data) return self.save_h5(path, data)
elif path.endswith((".vol",".vgi")): elif path.endswith((".vol",".vgi")):
return self.save_vol(path, data) return self.save_vol(path, data)
elif path.endswith((".dcm",".DCM")):
return self.save_dicom(path, data)
elif path.endswith((".jpeg",".jpg", ".png")): elif path.endswith((".jpeg",".jpg", ".png")):
return self.save_PIL(path, data) return self.save_PIL(path, data)
else: else:
......
...@@ -10,6 +10,7 @@ Pillow>=10.0.1, ...@@ -10,6 +10,7 @@ Pillow>=10.0.1,
plotly>=5.14.1, plotly>=5.14.1,
scipy>=1.11.2, scipy>=1.11.2,
seaborn>=0.12.2, seaborn>=0.12.2,
pydicom>=2.4.4,
setuptools>=68.0.0, setuptools>=68.0.0,
tifffile>=2023.4.12, tifffile>=2023.4.12,
torch>=2.0.1, torch>=2.0.1,
......
...@@ -42,6 +42,7 @@ setup( ...@@ -42,6 +42,7 @@ setup(
"h5py>=3.9.0", "h5py>=3.9.0",
"localthickness>=0.1.2", "localthickness>=0.1.2",
"matplotlib>=3.8.0", "matplotlib>=3.8.0",
"pydicom>=2.4.4",
"monai>=1.2.0", "monai>=1.2.0",
"numpy>=1.26.0", "numpy>=1.26.0",
"outputformat>=0.1.3", "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