innolitics / dicom-numpy Goto Github PK
View Code? Open in Web Editor NEWProperly generate a 3D numpy array from a set of DICOM files.
License: MIT License
Properly generate a 3D numpy array from a set of DICOM files.
License: MIT License
_sort_by_slice_position
in combine_slices.py
would be useful to dicom_numpy
callers so that instance metadata (e.g. SliceThickness
) can be associated with the volume generated from combine_slices
.
from dicom_numpy import *
Produces the following error:
TypeError: Item in dicom_numpy.__all__ must be str, not function
Some diffusion MRI series combine several scans into a single series, with no acquisition number to distinguish them. This causes issues for dicom-numpy
's generated image transformation.
Because slices are sorted by slice position, a diffusion series can appear to have multiple slices at the same location in space. This means the median space between slices will be 0, causing the image transformation to have all zeros in the slice axis.
To resolve this, slices should first be attempted to be sorted by instance number. If that fails, we can fall back to slice position sorting.
Hi, after combining slices, how could I check the voxel spacing between each slice? Is that same as the pixel spacing in the 2D slice? Thanks!
combine_slices
requires HighBit
and BitsStored
(HighBit
+ 1 as required by standard) to be the same across slices being combined. However, the standard does not require this, only for the values to be uniform within the same slice.
While some CT scans are generated with a uniform HighBit
(either for the maximum pixel value in the series, or just 15 when BitsAllocated
is 16), others are generated with HighBit
using the slice-specific maximum value. Scans 1, 5, and 8 at coronacases.org are generated this way. All these cases validate with the following:
import pydicom
import sys
import glob
import numpy as np
# https://wiki.python.org/moin/BitManipulation
def bitLen(int_type):
length = 0
while (int_type):
int_type >>= 1
length += 1
return(length)
for dcm_file in glob.glob(f'{sys.argv[1]}/**/*.dcm', recursive=True):
ds = pydicom.dcmread(dcm_file)
max_pixel = np.max(ds.pixel_array)
assert ds.HighBit + 1 == ds.BitsStored
bit_length = bitLen(max_pixel)
info = f'{dcm_file} {bit_length} {ds.HighBit} {max_pixel}'
assert bit_length == ds.HighBit or ds.BitsStored == ds.BitsAllocated, info
Hi there, thanks for the very nice software. I have a question. In trying to convert a single dcm to numpy array
I get an output with 2D while it is a 3D image --can you help ?
thanks!
david
IM-0002-0005.dcm.zip
']
import glob
import pydicom
import dicom_numpy
dicomlist = ['/Users/dsb/Downloads/Dicoms/IM-0002-0005
def extract_voxel_data(list_of_dicom_files):
datasets = [pydicom.read_file(f) for f in list_of_dicom_files]
try:
voxel_ndarray, ijk_to_xyz = dicom_numpy.combine_slices(datasets)
except dicom_numpy.DicomImportException as e:
# invalid DICOM data
raise
return voxel_ndarray
outp = extract_voxel_data(dicomlist)
Our documentation linked in the README is out of date. We should:
Can you access the affine transform of a dicom file with the dicom-numpy library?
_sort_by_slice_position
in combine_slices.py
would be useful to dicom_numpy
callers so that instance metadata (e.g. SliceThickness
) can be associated with the volume generated from combine_slices
.
Hello authors,
Thanks for the useful toolkit. I just wonder if the library can interpolate between slices so that the resulting numpy array would be even grid in distance unit along all three axes?
thanks
This is a great work, actually, I am looking for DICOM image stitching module that can stitch two or three images into one 2D image like a panorama. Is there any module or library to do so?
abad26d
The changes introduced in this commit were justified by the fact that in DICOM, the first slice is considered the top of the patient, whereas the reverse is true: the Z axis increases from the feet towards the head, see e.g. https://dicom.innolitics.com/ciods/ct-image/image-plane/00200032
I'm trying to combine DICOM slices and convert the resulting stack into a NiFTI file, but when I render it in 3D Slicer, the volume is flipped along the Z axis.
Hi,
combine_slices() works very well for me for multiple 2D DICOM images, which is great.
However, it's not working correctly for Enhanced MR Images, which have a hierarchical structure. If I call subcategories in DICOM, it won't find most variables. The same happens if I call the function for the whole DICOM file (which is inside a list).
AttributeError: 'FileDataset' object has no attribute 'ImageOrientationPatient'
Other times when I run the same function, it complains about the SliceLocation.
I'm aware that this involves plenty of work, so I would like to suggest the Enhanced MR as improvement.
Kind regards,
João Sousa
After inspecting in detail the combine_slices
code, I found some optimization possibilities:
_sort_by_slice_position
is called twice:_merge_slice_pixel_arrays
),_ijk_to_patient_xyz_transform_matrix
).combine_slices
function._slice_positions
is called 4 times:_validate_slices_form_uniform_grid
- 1 time_slice_spacing
- 1 time_sort_by_slice_position
- 1 time, but _sort_by_slice_position
is called twice --> 2 times_extract_cosines
is called 6 times:_validate_image_orientation
- 1 time_ijk_to_patient_xyz_transform_matrix
- 1 time_slice_positions
- 1 time, but _slice_positions
is called 4 times --> 4 timesAll these calls better be done once (and can be).
However,
_merge_slice_pixel_arrays
and _ijk_to_patient_xyz_transform_matrix
, which should not be used alone, but there are some special tests for _merge_slice_pixel_arrays
.Originally posted by @jond01 in #16 (comment)
Hi, I have recently reviewed the contrib-pydicom code. The script input-output/pydicom_series.py
there has (more or less) the same purpose as dicom-numpy.
I noticed there two points that we may want to include also here:
Read a folder: add an API to read a folder and extract the image array and affine.
A similar idea is in the example from the docs, which receives a list of files:
import pydicom
import dicom_numpy
def extract_voxel_data(list_of_dicom_files):
datasets = [pydicom.dcmread(f) for f in list_of_dicom_files]
try:
voxel_ndarray, ijk_to_xyz = dicom_numpy.combine_slices(datasets)
except dicom_numpy.DicomImportException as e:
# invalid DICOM data
raise
return voxel_ndarray
We may go one level up, and receive only the path of the folder containing these files.
The files within the folder can be filtered to only dicoms with pydicom's built-in is_dicom
function, and further split into distinct series (according to the SeriesInstanceUID
). combine_slices
will be called for each series, and the returned data will be a list of [(voxels0, affine0), (voxels1, affine1), ...]
.
Tighten the dtype of rescaled images: currently, dicom-numpy uses np.float32
every time there is a RescaleSlope
or RescaleIntercept
:
dicom-numpy/dicom_numpy/combine_slices.py
Line 108 in 204e955
dicom-numpy/dicom_numpy/combine_slices.py
Line 112 in edfa364
See: https://groups.google.com/forum/#!topic/comp.protocols.dicom/rCOq1TUcQ2Q
We should probably reference this, and other relevant links, in our documentation.
Hi,
Combining a single slice dataset (extracting its image and affine) -
from pydicom import dcmread
from dicom_numpy import combine_slices
ds0 = dcmread('dcm_file.dcm')
voxels, transform = combine_slices([ds0])
- produces an error in _check_for_missing_slices
, L221:
if not np.allclose(slice_positions_diffs, slice_positions_diffs[0], atol=0, rtol=1e-5):
slice_positions_diffs
is empty because there is 1 slice -> no diff.
I suggest adding the following check - as in _slice_spacing
, L231:
if len(slice_datasets) > 1:
Hi there,
For PET images, it's typical for slices within the same time point to have different values for RescaleSlope and RescaleIntercept because of large differences of uptake from slice to slice. So it might make sense to just rescale the pixel data based on these values and then incorporate everything into the same 3D matrix rather than just giving an exception. This should be reasonably straight forward so if you don't have time I can just fork and file a pull request?
Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.