Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
F
fslpy
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Deploy
Releases
Container Registry
Model registry
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Michiel Cottaar
fslpy
Commits
5d2dd7cb
Commit
5d2dd7cb
authored
7 years ago
by
Paul McCarthy
Browse files
Options
Downloads
Patches
Plain Diff
Wrappers around dcm2niix.
parent
f62f06d6
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
fsl/data/dicom.py
+91
-103
91 additions, 103 deletions
fsl/data/dicom.py
with
91 additions
and
103 deletions
fsl/data/dicom.py
+
91
−
103
View file @
5d2dd7cb
...
...
@@ -9,28 +9,32 @@ volumetric DICOM data series. The ``DicomImage`` is simply an :class:`.`Image`
which provides accessors for additional DICOM meta data.
The following other functions are provided in this module, which are thin
wrappers around functionality provided by
``pydicom`` and ``dcmstack``
:
wrappers around functionality provided by
Chris Rorden
'
s ``dcm2niix`` program
:
.. autosummary::
:nosignatures:
scanDir
stack
"""
loadNifti
.. note:: These functions will not work if an executable called ``dcm2niix``
cannot be found.
import
os
import
fnmatch
.. see:: https://github.com/rordenlab/dcm2niix/
"""
import
pydicom
as
dicom
from
.
import
dcmstack
import
os.path
as
op
import
subprocess
as
sp
import
glob
import
json
from
.
import
image
as
fslimage
import
fsl.utils.tempdir
as
tempdir
import
fsl.data.image
as
fslimage
class
DicomImage
(
fslimage
.
Image
):
"""
The ``DicomImage`` is a volumetric :class:`.Image` with associated
"""
The ``DicomImage`` is a volumetric :class:`.Image` with
some
associated
DICOM metadata.
The ``Image`` class is used to manage the data and the voxel-to-world
...
...
@@ -39,114 +43,98 @@ class DicomImage(fslimage.Image):
def
__init__
(
self
,
image
,
meta
):
"""
Create a ``DicomImage``.
:arg image: Passed through to :meth:`.Image.__init__`.
:arg meta: Dictionary containing DICOM meta-data.
"""
fslimage
.
Image
.
__init__
(
self
,
image
)
self
.
__meta
=
meta
def
keys
(
self
):
"""
Returns the keys contained in the DICOM metadata dictionary
(``dict.keys``).
"""
return
self
.
__meta
.
keys
()
def
scanDir
(
dcmdir
,
filePattern
=
'
*.dcm
'
,
callback
=
None
):
"""
Recursively scans the given DICOM directory, and returns a dictionary
which contains all of the data series that were found.
def
values
(
self
):
"""
Returns the values contained in the DICOM metadata dictionary
(``dict.values``).
"""
return
self
.
__meta
.
values
()
def
items
(
self
):
"""
Returns the items contained in the DICOM metadata dictionary
(``dict.items``).
"""
return
self
.
__meta
.
items
()
:arg dcmdir: Directory containing DICOM files.
:arg filePattern: Glob-like pattern with which to identify DICOM files.
Defaults to ``
'
*.dcm
'
``.
def
get
(
self
,
*
args
,
**
kwargs
):
"""
Returns the metadata value with the specified key (``dict.get``).
"""
return
self
.
__meta
.
get
(
*
args
,
**
kwargs
)
:arg callback: Function which will get called every time a file is
loaded, and can be used for e.g. updating progress.
Must accept three positional parameters:
- ``path``: Path
- ``n``: Index of current path
- ``ttl``: Total number of paths
After all files have been loaded, this function is called
once more before the files are grouped into data series.
For this final call, ``path is None``, and ``n == ttl``.
def
scanDir
(
dcmdir
):
"""
Uses ``dcm2niix`` to scans the given DICOM directory, and returns a
list of dictionaries, one for each data series that was identified.
Each dictionary is populated with some basic metadata about the series.
:returns: A list containing one element for each identified data
series. Each element itself is a list with one element
for each file, where each element is a tuple containing
the ``pydicom.dataset.FileDataset``, and a ``dict``
containing some basic metadata extracted from the file.
:arg dcmdir: Directory containing DICOM files.
.. see:: ``dcmstack.parse_and_group`` and ``pydicom.dicomio.dcmread``.
:returns: A list of dictionaries, each containing metadata about
one DICOM data series.
"""
def
default_callback
(
path
,
n
,
ttl
):
pass
if
callback
is
None
:
callback
=
default_callback
# Find all the DICOM files in the directory.
# If/when we drop python < 3.5, we can use:
#
# glob.glob(op.join(dcmdir, '**', filePattern), recursive=True)
dcmfiles
=
[]
for
root
,
dirnames
,
filenames
in
os
.
walk
(
dcmdir
):
for
filename
in
fnmatch
.
filter
(
filenames
,
filePattern
):
dcmfiles
.
append
(
os
.
path
.
join
(
root
,
filename
))
# No files found
if
len
(
dcmfiles
)
==
0
:
return
{}
# Tell pydicom to only load the tags that
# are necessary to group files into series,
# and to give us basic metadata.
tags
=
[
'
SeriesInstanceUID
'
,
'
SeriesNumber
'
,
'
SeriesDescription
'
,
'
ProtocolName
'
,
'
ImageOrientationPatient
'
,
'
Rows
'
,
'
Columns
'
,
'
PixelSpacing
'
]
# Load the files one by one
dcms
=
[]
for
i
,
path
in
enumerate
(
dcmfiles
):
callback
(
path
,
i
,
len
(
dcmfiles
))
dcms
.
append
(
dicom
.
dcmread
(
path
,
defer_size
=
64
,
specific_tags
=
tags
))
callback
(
None
,
len
(
dcmfiles
),
len
(
dcmfiles
))
# Group the files into data series
series
=
dcmstack
.
parse_and_group
(
dcms
)
# parse_and_group returns a dict, with
# one entry for each data series, where
# each entry is a list containing
# (pydicom file, metadata, filepath)
#
# We don't care about the dict keys,
series
=
list
(
series
.
values
())
return
series
def
stack
(
series
,
callback
=
None
):
"""
Takes a DICOM data series, as returned by :func:`scanDir`, and converts
it to a ``dcmstack.DicomStack``.
:arg series:
:arg callback:
:returns:
"""
dcmdir
=
op
.
abspath
(
dcmdir
)
cmd
=
'
dcm2niix -b o -ba n -f %s -o . {}
'
.
format
(
dcmdir
)
def
default_callback
(
path
,
n
,
ttl
):
pass
with
tempdir
.
tempdir
()
as
td
:
sp
.
call
(
cmd
.
split
())
files
=
glob
.
glob
(
op
.
join
(
td
,
'
*.json
'
))
if
len
(
files
)
==
0
:
return
[]
# sort numerically by series number
def
sortkey
(
f
):
return
int
(
op
.
splitext
(
op
.
basename
(
f
))[
0
])
files
=
sorted
(
files
,
key
=
sortkey
)
series
=
[]
for
fn
in
files
:
with
open
(
fn
,
'
rt
'
)
as
f
:
meta
=
json
.
load
(
f
)
meta
[
'
DicomDir
'
]
=
dcmdir
series
.
append
(
meta
)
return
series
def
loadNifti
(
series
):
"""
Takes a DICOM series meta data dictionary, as returned by
:func:`scanDir`, and loads the associated data as one or more NIFTI
images.
:arg series: Dictionary as returned by :func:`scanDir`, containing
meta data about one DICOM data series.
:returns: List containing one or more :class:`.DicomImage` objects.
"""
if
callback
is
None
:
callback
=
default_callback
dcmdir
=
series
[
'
DicomDir
'
]
snum
=
series
[
'
SeriesNumber
'
]
cmd
=
'
dcm2niix -b n -f %s -z n -o n {}
'
.
format
(
dcmdir
)
ds
=
dcmstack
.
DicomStack
()
with
tempdir
.
tempdir
()
as
td
:
sp
.
call
(
cmd
.
split
())
for
i
,
(
_
,
meta
,
filename
)
in
enumerate
(
series
):
callback
(
filename
,
i
,
len
(
series
))
ds
.
add_dcm
(
dicom
.
dcmread
(
filename
),
meta
)
files
=
glob
.
glob
(
op
.
join
(
td
,
'
{}.nii
'
.
format
(
snum
)))
return
ds
return
[
DicomImage
(
f
,
series
)
for
f
in
files
]
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment