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
5d7de43e
Commit
5d7de43e
authored
9 years ago
by
Paul McCarthy
Browse files
Options
Downloads
Patches
Plain Diff
Nifti1 related stuff has been moved out of the Image class, and into
a general base class. Will be used by TensorImage.
parent
e970ca1e
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
fsl/data/image.py
+159
-133
159 additions, 133 deletions
fsl/data/image.py
with
159 additions
and
133 deletions
fsl/data/image.py
+
159
−
133
View file @
5d7de43e
...
...
@@ -37,6 +37,9 @@ import os
import
os.path
as
op
import
subprocess
as
sp
import
numpy
as
np
import
nibabel
as
nib
import
props
import
fsl.utils.transform
as
transform
...
...
@@ -47,58 +50,38 @@ import fsl.data.constants as constants
log
=
logging
.
getLogger
(
__name__
)
class
Image
(
props
.
HasProperties
):
"""
Class which represents a 3D/4D image. Internally, the image is
loaded/stored using :mod:`nibabel`
.
class
Nifti1
(
object
):
"""
The ``Nifti1`` class is intended to be used as a base class for
things which either are, or are associated with, a NIFTI1 image
.
In addition to the :attr:`name`, :attr:`data`, and :attr:`saved`
properties, the following attributes are present on an ``Image`` instance:
When a ``Nifti1`` instance is created, it adds the following attributes
to itself:
================= ====================================================
``nibImage`` The :mod:`nibabel` image object.
``dataSource`` The name of the file that the image was loaded from.
``dataSource`` The name of the file that the image was loaded from.
``tempFile`` The name of the temporary file which was created (in
the event that the image was large and was gzipped -
see :func:`loadImage`).
``shape`` A list/tuple containing the number of voxels along
each image dimension.
``pixdim`` A list/tuple containing the size of one voxel along
each image dimension.
each image dimension.
``voxToWorldMat`` A 4*4 array specifying the affine transformation
for transforming voxel coordinates into real world
coordinates.
``worldToVoxMat`` A 4*4 array specifying the affine transformation
for transforming real world coordinates into voxel
coordinates.
================= ====================================================
"""
name
=
props
.
String
()
"""
The name of this image.
"""
data
=
props
.
Object
()
"""
The image data. This is a read-only :mod:`numpy` array - all changes
to the image data must be via the :meth:`applyChange` method.
"""
saved
=
props
.
Boolean
(
default
=
False
)
"""
A read-only property (not enforced) which is ``True`` if the image,
as stored in memory, is saved to disk, ``False`` otherwise.
================= ====================================================
"""
def
__init__
(
self
,
image
,
xform
=
None
,
name
=
None
,
header
=
None
,
loadData
=
True
):
"""
Create a
n
``
Image
`` object
with the given image data or file name
.
"""
Create a ``
Nifti1
`` object.
:arg image: A string containing the name of an image file to load,
or a :mod:`numpy` array, or a :mod:`nibabel` image
...
...
@@ -108,8 +91,6 @@ class Image(props.HasProperties):
which transforms voxel coordinates into real world
coordinates.
:arg name: A name for the image.
:arg header: If not ``None``, assumed to be a
:class:`nibabel.nifti1.Nifti1Header` to be used as the
image header. Not applied to images loaded from file,
...
...
@@ -119,9 +100,9 @@ class Image(props.HasProperties):
not loaded - this is useful if you
'
re only interested
in the header data, as the file will be loaded much
more quickly. The image data may subsequently be loaded
via the :meth:`loadData` method.
via the :meth:`loadData` method.
"""
self
.
nibImage
=
None
self
.
dataSource
=
None
self
.
tempFile
=
None
...
...
@@ -133,54 +114,35 @@ class Image(props.HasProperties):
if
header
is
not
None
:
header
=
header
.
copy
()
import
numpy
as
np
import
nibabel
as
nib
# The image parameter may be the name of an image file
if
isinstance
(
image
,
basestring
):
nibImage
,
filename
=
loadImage
(
addExt
(
image
))
self
.
nibImage
=
nibImage
self
.
dataSource
=
op
.
abspath
(
image
)
self
.
dataSource
=
op
.
abspath
(
image
)
# if the returned file name is not the same as
# the provided file name, that means that the
# image was opened from a temporary file
if
filename
!=
image
:
filepref
=
removeExt
(
op
.
basename
(
self
.
dataSource
))
self
.
tempFile
=
nibImage
.
get_filename
()
else
:
filepref
=
removeExt
(
op
.
basename
(
self
.
dataSource
))
if
name
is
None
:
name
=
filepref
self
.
name
=
name
self
.
saved
=
True
# Or a numpy array - we wrap it in a nibabel image,
# with an identity transformation (each voxel maps
# to 1mm^3 in real world space)
elif
isinstance
(
image
,
np
.
ndarray
):
if
xform
is
None
:
if
header
is
None
:
xform
=
np
.
identity
(
4
)
else
:
xform
=
header
.
get_best_affine
()
if
name
is
None
:
name
=
'
Numpy array
'
self
.
nibImage
=
nib
.
nifti1
.
Nifti1Image
(
image
,
xform
,
header
=
header
)
self
.
name
=
name
# otherwise, we assume that it is a nibabel image
else
:
if
name
is
None
:
name
=
'
Nibabel image
'
self
.
nibImage
=
image
self
.
name
=
name
self
.
shape
=
self
.
nibImage
.
get_shape
()
self
.
pixdim
=
self
.
nibImage
.
get_header
().
get_zooms
()
...
...
@@ -194,34 +156,8 @@ class Image(props.HasProperties):
if
len
(
self
.
shape
)
<
3
or
len
(
self
.
shape
)
>
4
:
raise
RuntimeError
(
'
Only 3D or 4D images are supported
'
)
log
.
memory
(
'
{}.init ({})
'
.
format
(
type
(
self
).
__name__
,
id
(
self
)))
def
__del__
(
self
):
"""
Prints a log message.
"""
log
.
memory
(
'
{}.del ({})
'
.
format
(
type
(
self
).
__name__
,
id
(
self
)))
def
__hash__
(
self
):
"""
Returns a number which uniquely idenfities this ``Image`` instance
(the result of ``id(self)``).
"""
return
id
(
self
)
def
__str__
(
self
):
"""
Return a string representation of this ``Image`` instance.
"""
return
'
{}({}, {})
'
.
format
(
self
.
__class__
.
__name__
,
self
.
name
,
self
.
dataSource
)
def
__repr__
(
self
):
"""
See the :meth:`__str__` method.
"""
return
self
.
__str__
()
def
loadData
(
self
):
"""
Loads the image data from the file. This method only needs to
be called if the ``loadData`` parameter passed to :meth:`__init__`
...
...
@@ -243,7 +179,7 @@ class Image(props.HasProperties):
log
.
debug
(
'
Loaded image data ({}) - original
'
'
shape {}, squeezed shape {}
'
.
format
(
self
.
nam
e
,
self
.
dataSourc
e
,
shape
,
data
.
shape
))
...
...
@@ -252,58 +188,12 @@ class Image(props.HasProperties):
self
.
pixdim
=
self
.
pixdim
[:
len
(
data
.
shape
)]
def
applyChange
(
self
,
offset
,
newVals
,
vol
=
None
):
"""
Changes the image data according to the given new values.
Any listeners registered on the :attr:`data` property will be
notified of the change.
:arg offset: A tuple of three values, containing the xyz
offset of the image region to be changed.
:arg newVals: A 3D numpy array containing the new image values.
:arg vol: If this is a 4D image, the volume index.
"""
if
self
.
is4DImage
()
and
vol
is
None
:
raise
ValueError
(
'
Volume must be specified for 4D images
'
)
data
=
self
.
data
xlo
,
ylo
,
zlo
=
offset
xhi
=
xlo
+
newVals
.
shape
[
0
]
yhi
=
ylo
+
newVals
.
shape
[
1
]
zhi
=
zlo
+
newVals
.
shape
[
2
]
try
:
data
.
flags
.
writeable
=
True
if
self
.
is4DImage
():
data
[
xlo
:
xhi
,
ylo
:
yhi
,
zlo
:
zhi
,
vol
]
=
newVals
else
:
data
[
xlo
:
xhi
,
ylo
:
yhi
,
zlo
:
zhi
]
=
newVals
data
.
flags
.
writeable
=
False
except
:
data
.
flags
.
writeable
=
False
raise
# Force a notification on the 'data' property
# by assigning its value back to itself
self
.
data
=
data
self
.
saved
=
False
def
save
(
self
):
"""
Convenience method to save any changes made to the :attr:`data` of
this :class:`Image` instance.
See the :func:`saveImage` function.
"""
return
saveImage
(
self
)
# TODO: Remove this method, and use the shape attribute directly
def
is4DImage
(
self
):
"""
Returns ``True`` if this image is 4D, ``False`` otherwise.
"""
return
len
(
self
.
shape
)
>
3
and
self
.
shape
[
3
]
>
1
return
len
(
self
.
shape
)
>
3
and
self
.
shape
[
3
]
>
1
def
getXFormCode
(
self
,
code
=
None
):
"""
This method returns the code contained in the NIFTI1 header,
indicating the space to which the (transformed) image is oriented.
...
...
@@ -372,7 +262,143 @@ class Image(props.HasProperties):
(
constants
.
ORIENT_A2P
,
constants
.
ORIENT_P2A
),
(
constants
.
ORIENT_S2I
,
constants
.
ORIENT_I2S
)))[
axis
]
return
code
return
code
class
Image
(
Nifti1
,
props
.
HasProperties
):
"""
Class which represents a 3D/4D NIFTI1 image. Internally, the image
is loaded/stored using :mod:`nibabel`.
In addition to the :attr:`data`, and :attr:`saved` properties, and
the attributes added by the :meth:`Nifti1.__init__` method, the
following attributes are present on an ``Image`` instance:
================= ====================================================
``name`` the name of this ``Image`` - defaults to the image
file name, sans-suffix.
================= ====================================================
"""
data
=
props
.
Object
()
"""
The image data. This is a read-only :mod:`numpy` array - all changes
to the image data must be via the :meth:`applyChange` method.
"""
saved
=
props
.
Boolean
(
default
=
False
)
"""
A read-only property (not enforced) which is ``True`` if the image,
as stored in memory, is saved to disk, ``False`` otherwise.
"""
def
__init__
(
self
,
image
,
name
=
None
,
**
kwargs
):
"""
Create an ``Image`` object with the given image data or file name.
:arg image: A string containing the name of an image file to load,
or a :mod:`numpy` array, or a :mod:`nibabel` image
object.
:arg name: A name for the image.
All other arguments are passed through to :meth:`Nifti1.__init__`.
"""
Nifti1
.
__init__
(
self
,
image
,
**
kwargs
)
# Figure out the name of this image.
# It might have been explicitly passed in
if
name
is
not
None
:
self
.
name
=
name
# Or, if this image was loaded
# from disk, use the file name
elif
isinstance
(
image
,
basestring
):
self
.
name
=
removeExt
(
op
.
basename
(
self
.
dataSource
))
self
.
saved
=
True
# Or the image was created from a numpy array
elif
isinstance
(
image
,
np
.
ndarray
):
self
.
name
=
'
Numpy array
'
# Or image from a nibabel image
else
:
self
.
name
=
'
Nibabel image
'
log
.
memory
(
'
{}.init ({})
'
.
format
(
type
(
self
).
__name__
,
id
(
self
)))
def
__del__
(
self
):
"""
Prints a log message.
"""
log
.
memory
(
'
{}.del ({})
'
.
format
(
type
(
self
).
__name__
,
id
(
self
)))
def
__hash__
(
self
):
"""
Returns a number which uniquely idenfities this ``Image`` instance
(the result of ``id(self)``).
"""
return
id
(
self
)
def
__str__
(
self
):
"""
Return a string representation of this ``Image`` instance.
"""
return
'
{}({}, {})
'
.
format
(
self
.
__class__
.
__name__
,
self
.
name
,
self
.
dataSource
)
def
__repr__
(
self
):
"""
See the :meth:`__str__` method.
"""
return
self
.
__str__
()
def
applyChange
(
self
,
offset
,
newVals
,
vol
=
None
):
"""
Changes the image data according to the given new values.
Any listeners registered on the :attr:`data` property will be
notified of the change.
:arg offset: A tuple of three values, containing the xyz
offset of the image region to be changed.
:arg newVals: A 3D numpy array containing the new image values.
:arg vol: If this is a 4D image, the volume index.
"""
if
self
.
is4DImage
()
and
vol
is
None
:
raise
ValueError
(
'
Volume must be specified for 4D images
'
)
data
=
self
.
data
xlo
,
ylo
,
zlo
=
offset
xhi
=
xlo
+
newVals
.
shape
[
0
]
yhi
=
ylo
+
newVals
.
shape
[
1
]
zhi
=
zlo
+
newVals
.
shape
[
2
]
try
:
data
.
flags
.
writeable
=
True
if
self
.
is4DImage
():
data
[
xlo
:
xhi
,
ylo
:
yhi
,
zlo
:
zhi
,
vol
]
=
newVals
else
:
data
[
xlo
:
xhi
,
ylo
:
yhi
,
zlo
:
zhi
]
=
newVals
data
.
flags
.
writeable
=
False
except
:
data
.
flags
.
writeable
=
False
raise
# Force a notification on the 'data' property
# by assigning its value back to itself
self
.
data
=
data
self
.
saved
=
False
def
save
(
self
):
"""
Convenience method to save any changes made to the :attr:`data` of
this :class:`Image` instance.
See the :func:`saveImage` function.
"""
return
saveImage
(
self
)
# TODO The wx.FileDialog does not
...
...
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