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
87bd8399
Commit
87bd8399
authored
10 years ago
by
Paul McCarthy
Browse files
Options
Downloads
Patches
Plain Diff
Re-did axis label orientation calculation; hopefully it is better
defined (and correct!).
parent
25fa4934
No related branches found
Branches containing commit
No related tags found
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
fsl/data/image.py
+85
-21
85 additions, 21 deletions
fsl/data/image.py
fsl/fslview/strings.py
+47
-0
47 additions, 0 deletions
fsl/fslview/strings.py
fsl/fslview/views/orthopanel.py
+140
-39
140 additions, 39 deletions
fsl/fslview/views/orthopanel.py
with
272 additions
and
60 deletions
fsl/data/image.py
+
85
−
21
View file @
87bd8399
...
...
@@ -26,22 +26,23 @@ import fsl.data.imagefile as imagefile
log
=
logging
.
getLogger
(
__name__
)
L2R
=
0
R2L
=
1
P2A
=
2
A2P
=
3
I2S
=
4
S2I
=
5
orientationLabels
=
{
L2R
:
(
'
L
'
,
'
R
'
),
R2L
:
(
'
R
'
,
'
L
'
),
P2A
:
(
'
P
'
,
'
A
'
),
A2P
:
(
'
A
'
,
'
P
'
),
S2I
:
(
'
S
'
,
'
I
'
),
I2S
:
(
'
I
'
,
'
S
'
)
}
# Constants which represent the orientation of an axis,
# in either voxel or world space.
ORIENT_UNKNOWN
=
-
1
ORIENT_L2R
=
0
ORIENT_R2L
=
1
ORIENT_P2A
=
2
ORIENT_A2P
=
3
ORIENT_I2S
=
4
ORIENT_S2I
=
5
# Constants from the NIFTI1 specification that define
# the 'space' in which an image is assumed to be.
NIFTI_XFORM_UNKNOWN
=
0
NIFTI_XFORM_SCANNER_ANAT
=
1
NIFTI_XFORM_ALIGNED_ANAT
=
2
NIFTI_XFORM_TALAIRACH
=
3
NIFTI_XFORM_MNI_152
=
4
def
_loadImageFile
(
filename
):
"""
Given the name of an image file, loads it using nibabel.
...
...
@@ -384,15 +385,78 @@ class Image(props.HasProperties):
else
:
return
worldp
def
getOrientation
(
self
,
axis
):
def
getXFormCode
(
self
):
"""
This method returns the code contained in the NIFTI1 header,
indicating the space to which the (transformed) image is oriented.
"""
sform_code
=
self
.
nibImage
.
get_header
()[
'
sform_code
'
]
qform_code
=
self
.
nibImage
.
get_header
()[
'
qform_code
'
]
# if the qform and sform codes don't
# match, I don't know what to do
if
sform_code
!=
qform_code
:
return
NIFTI_XFORM_UNKNOWN
# Invalid values
elif
sform_code
>
4
:
return
NIFTI_XFORM_UNKNOWN
elif
sform_code
<
0
:
return
NIFTI_XFORM_UNKNOWN
# All is well
else
:
return
sform_code
def
getWorldOrientation
(
self
,
axis
):
"""
Returns a code representing the orientation of the specified axis
in world space.
This method returns one of the following values, indicating the
direction in which coordinates along the specified axis increase:
- :attr:`~fsl.data.image.ORIENT_L2R`: Left to right
- :attr:`~fsl.data.image.ORIENT_R2L`: Right to left
- :attr:`~fsl.data.image.ORIENT_A2P`: Anterior to posterior
- :attr:`~fsl.data.image.ORIENT_P2A`: Posterior to anterior
- :attr:`~fsl.data.image.ORIENT_I2S`: Inferior to superior
- :attr:`~fsl.data.image.ORIENT_S2I`: Superior to inferior
- :attr:`~fsl.data.image.ORIENT_UNKNOWN`: Orientation is unknown
The returned value is dictated by the XForm code contained in the
image file header (see the :meth:`getXFormCode` method). Basically,
if the XForm code is
'
unknown
'
, this method will return -1 for all
axes. Otherwise, it is assumed that the image is in RAS orientation
(i.e. the X axis increases from left to right, the Y axis increases
from posterior to anterior, and the Z axis increases from inferior
to superior).
"""
if
self
.
getXFormCode
()
==
NIFTI_XFORM_UNKNOWN
:
return
-
1
if
axis
==
0
:
return
ORIENT_L2R
elif
axis
==
1
:
return
ORIENT_P2A
elif
axis
==
2
:
return
ORIENT_I2S
else
:
return
-
1
def
getVoxelOrientation
(
self
,
axis
):
"""
Returns a code representing the (estimated) orientation of the
specified voxelwise axis.
See the :meth:`getWorldOrientation` method for a description
of the return value.
"""
if
self
.
getXFormCode
()
==
NIFTI_XFORM_UNKNOWN
:
return
-
1
# the aff2axcodes returns one code for each
# axis in the image array (i.e. in voxel space),
# which denotes the real world direction
code
=
nib
.
orientations
.
aff2axcodes
(
self
.
nibImage
.
get_affine
(),
((
R2L
,
L2R
),
(
A2P
,
P2A
),
(
S2I
,
I2S
)))[
axis
]
return
orientationLabels
[
code
]
((
ORIENT_R2L
,
ORIENT_
L2R
),
(
ORIENT_A2P
,
ORIENT_
P2A
),
(
ORIENT_S2I
,
ORIENT_
I2S
)))[
axis
]
return
code
def
_transform
(
self
,
p
,
a
,
axes
):
...
...
This diff is collapsed.
Click to expand it.
fsl/fslview/strings.py
+
47
−
0
View file @
87bd8399
...
...
@@ -5,6 +5,8 @@
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
import
fsl.data.image
as
fslimage
from
views
.
orthopanel
import
OrthoPanel
from
views
.
lightboxpanel
import
LightBoxPanel
from
views
.
timeseriespanel
import
TimeSeriesPanel
...
...
@@ -49,3 +51,48 @@ orthoConfigMenu = '{} display'
lightBoxConfigMenu
=
'
{} display
'
locationPanelOutOfBounds
=
'
Out of bounds
'
imageAxisLowLongLabels
=
{
fslimage
.
ORIENT_A2P
:
'
Anterior
'
,
fslimage
.
ORIENT_P2A
:
'
Posterior
'
,
fslimage
.
ORIENT_L2R
:
'
Left
'
,
fslimage
.
ORIENT_R2L
:
'
Right
'
,
fslimage
.
ORIENT_I2S
:
'
Inferior
'
,
fslimage
.
ORIENT_S2I
:
'
Superior
'
,
fslimage
.
ORIENT_UNKNOWN
:
'
Unknown
'
}
imageAxisHighLongLabels
=
{
fslimage
.
ORIENT_A2P
:
'
Posterior
'
,
fslimage
.
ORIENT_P2A
:
'
Anterior
'
,
fslimage
.
ORIENT_L2R
:
'
Right
'
,
fslimage
.
ORIENT_R2L
:
'
Left
'
,
fslimage
.
ORIENT_I2S
:
'
Superior
'
,
fslimage
.
ORIENT_S2I
:
'
Inferior
'
,
fslimage
.
ORIENT_UNKNOWN
:
'
Unknown
'
}
imageAxisLowShortLabels
=
{
fslimage
.
ORIENT_A2P
:
'
A
'
,
fslimage
.
ORIENT_P2A
:
'
P
'
,
fslimage
.
ORIENT_L2R
:
'
L
'
,
fslimage
.
ORIENT_R2L
:
'
R
'
,
fslimage
.
ORIENT_I2S
:
'
I
'
,
fslimage
.
ORIENT_S2I
:
'
S
'
,
fslimage
.
ORIENT_UNKNOWN
:
'
?
'
}
imageAxisHighShortLabels
=
{
fslimage
.
ORIENT_A2P
:
'
P
'
,
fslimage
.
ORIENT_P2A
:
'
A
'
,
fslimage
.
ORIENT_L2R
:
'
R
'
,
fslimage
.
ORIENT_R2L
:
'
L
'
,
fslimage
.
ORIENT_I2S
:
'
S
'
,
fslimage
.
ORIENT_S2I
:
'
I
'
,
fslimage
.
ORIENT_UNKNOWN
:
'
?
'
}
imageSpaceLabels
=
{
fslimage
.
NIFTI_XFORM_UNKNOWN
:
'
Unknown
'
,
fslimage
.
NIFTI_XFORM_SCANNER_ANAT
:
'
Scanner anatomical
'
,
fslimage
.
NIFTI_XFORM_ALIGNED_ANAT
:
'
Aligned anatomical
'
,
fslimage
.
NIFTI_XFORM_TALAIRACH
:
'
Talairach
'
,
fslimage
.
NIFTI_XFORM_MNI_152
:
'
MNI152
'
}
This diff is collapsed.
Click to expand it.
fsl/fslview/views/orthopanel.py
+
140
−
39
View file @
87bd8399
...
...
@@ -19,6 +19,8 @@ log = logging.getLogger(__name__)
import
wx
import
props
import
fsl.data.image
as
fslimage
import
fsl.fslview.gl
as
fslgl
import
fsl.fslview.gl.wxglslicecanvas
as
slicecanvas
import
canvaspanel
...
...
@@ -221,16 +223,25 @@ class OrthoPanel(canvaspanel.CanvasPanel):
self
.
bindProps
(
'
invertZ_X
'
,
self
.
_zcanvas
,
'
invertX
'
)
self
.
bindProps
(
'
invertZ_Y
'
,
self
.
_zcanvas
,
'
invertY
'
)
llName
=
'
{}_layout
'
.
format
(
self
.
_name
)
self
.
addListener
(
'
layout
'
,
llName
,
self
.
_layoutChanged
)
self
.
addListener
(
'
showColourBar
'
,
llName
,
self
.
_layoutChanged
)
self
.
addListener
(
'
colourBarLocation
'
,
llName
,
self
.
_layoutChanged
)
self
.
addListener
(
'
showLabels
'
,
llName
,
self
.
_toggleLabels
)
# Callbacks for ortho panel layout options
self
.
addListener
(
'
layout
'
,
self
.
_name
,
self
.
_layoutChanged
)
self
.
addListener
(
'
showColourBar
'
,
self
.
_name
,
self
.
_layoutChanged
)
self
.
addListener
(
'
colourBarLocation
'
,
self
.
_name
,
self
.
_layoutChanged
)
self
.
addListener
(
'
showLabels
'
,
self
.
_name
,
self
.
_toggleLabels
)
# Callbacks for image list/selected image changes
self
.
_imageList
.
addListener
(
'
images
'
,
self
.
_name
,
self
.
_imageListChanged
)
self
.
_displayCtx
.
addListener
(
'
selectedImage
'
,
self
.
_name
,
self
.
_imageListChanged
)
self
.
_imageListChanged
()
self
.
_layoutChanged
()
self
.
_toggleLabels
()
# Callbacks for mouse events on the three xcanvases
self
.
_xcanvas
.
Bind
(
wx
.
EVT_LEFT_DOWN
,
self
.
_onMouseEvent
)
self
.
_ycanvas
.
Bind
(
wx
.
EVT_LEFT_DOWN
,
self
.
_onMouseEvent
)
self
.
_zcanvas
.
Bind
(
wx
.
EVT_LEFT_DOWN
,
self
.
_onMouseEvent
)
...
...
@@ -238,20 +249,16 @@ class OrthoPanel(canvaspanel.CanvasPanel):
self
.
_ycanvas
.
Bind
(
wx
.
EVT_MOTION
,
self
.
_onMouseEvent
)
self
.
_zcanvas
.
Bind
(
wx
.
EVT_MOTION
,
self
.
_onMouseEvent
)
# Callback for the display context location - when it
# changes, update the displayed canvas locations
def
move
(
*
a
):
if
self
.
posSync
:
self
.
setPosition
(
*
self
.
_displayCtx
.
location
)
self
.
setPosition
(
*
self
.
_displayCtx
.
location
)
self
.
_displayCtx
.
addListener
(
'
location
'
,
self
.
_name
,
move
)
def
onDestroy
(
ev
):
self
.
_displayCtx
.
removeListener
(
'
location
'
,
self
.
_name
)
ev
.
Skip
()
self
.
Bind
(
wx
.
EVT_WINDOW_DESTROY
,
onDestroy
)
self
.
Bind
(
wx
.
EVT_SIZE
,
self
.
_resize
)
# Callbacks for toggling x/y/z canvas display
def
toggle
(
canvas
,
toggle
):
self
.
_canvasSizer
.
Show
(
canvas
,
toggle
)
if
self
.
layout
.
lower
()
==
'
grid
'
:
...
...
@@ -268,6 +275,55 @@ class OrthoPanel(canvaspanel.CanvasPanel):
lambda
*
a
:
toggle
(
self
.
_zCanvasPanel
,
self
.
showZCanvas
))
# Do some cleaning up if/when this panel is destroyed
self
.
Bind
(
wx
.
EVT_WINDOW_DESTROY
,
self
.
_onDestroy
)
# And finally, call the _resize method to
# re-layout things when this panel is resized
self
.
Bind
(
wx
.
EVT_SIZE
,
self
.
_resize
)
def
_imageListChanged
(
self
,
*
a
):
"""
Called when the image list or selected image is changed.
Adds a listener to the currently selected image, to listen
for changes on its affine transformation matrix.
"""
if
len
(
self
.
_imageList
)
==
0
:
return
for
i
,
img
in
enumerate
(
self
.
_imageList
):
# Update anatomy labels when the image
# transformation matrix changes
if
i
==
self
.
_displayCtx
.
selectedImage
:
img
.
addListener
(
'
transform
'
,
self
.
_name
,
self
.
_toggleLabels
)
else
:
img
.
removeListener
(
'
transform
'
,
self
.
_name
)
def
_onDestroy
(
self
,
ev
):
"""
Called when this panel is destroyed.
The display context and image list will probably live longer than
this OrthoPanel. So when this panel is destroyed, all those
registered listeners are removed.
"""
ev
.
Skip
()
# Do nothing if the destroyed window is not
# this panel (i.e. it is a child of this panel)
if
ev
.
GetEventObject
()
!=
self
:
return
self
.
_displayCtx
.
removeListener
(
'
location
'
,
self
.
_name
)
self
.
_displayCtx
.
removeListener
(
'
selectedImage
'
,
self
.
_name
)
self
.
_imageList
.
removeListener
(
'
images
'
,
self
.
_name
)
# The _imageListChanged method adds
# listeners to individual images,
# so we have to remove them too
for
img
in
self
.
_imageList
:
img
.
removeListener
(
'
transform
'
,
self
.
_name
)
def
_resize
(
self
,
ev
):
"""
...
...
@@ -284,34 +340,79 @@ class OrthoPanel(canvaspanel.CanvasPanel):
def
_toggleLabels
(
self
,
*
a
):
"""
Shows/hides labels depicting anatomical orientation on each canvas.
"""
allLabels
=
[
self
.
_xLeftLabel
,
self
.
_xRightLabel
,
self
.
_xTopLabel
,
self
.
_xBottomLabel
,
self
.
_yLeftLabel
,
self
.
_yRightLabel
,
self
.
_yTopLabel
,
self
.
_yBottomLabel
,
self
.
_zLeftLabel
,
self
.
_zRightLabel
,
self
.
_zTopLabel
,
self
.
_zBottomLabel
]
# Are we showing or hiding the labels?
if
self
.
showLabels
:
show
=
True
else
:
show
=
False
self
.
_xLeftLabel
.
Show
(
show
)
self
.
_xRightLabel
.
Show
(
show
)
self
.
_xTopLabel
.
Show
(
show
)
self
.
_xBottomLabel
.
Show
(
show
)
self
.
_yLeftLabel
.
Show
(
show
)
self
.
_yRightLabel
.
Show
(
show
)
self
.
_yTopLabel
.
Show
(
show
)
self
.
_yBottomLabel
.
Show
(
show
)
self
.
_zLeftLabel
.
Show
(
show
)
self
.
_zRightLabel
.
Show
(
show
)
self
.
_zTopLabel
.
Show
(
show
)
self
.
_zBottomLabel
.
Show
(
show
)
self
.
_xLeftLabel
.
SetLabel
(
'
?
'
)
self
.
_xRightLabel
.
SetLabel
(
'
?
'
)
self
.
_xTopLabel
.
SetLabel
(
'
?
'
)
self
.
_xBottomLabel
.
SetLabel
(
'
?
'
)
self
.
_yLeftLabel
.
SetLabel
(
'
?
'
)
self
.
_yRightLabel
.
SetLabel
(
'
?
'
)
self
.
_yTopLabel
.
SetLabel
(
'
?
'
)
self
.
_yBottomLabel
.
SetLabel
(
'
?
'
)
self
.
_zLeftLabel
.
SetLabel
(
'
?
'
)
self
.
_zRightLabel
.
SetLabel
(
'
?
'
)
self
.
_zTopLabel
.
SetLabel
(
'
?
'
)
self
.
_zBottomLabel
.
SetLabel
(
'
?
'
)
for
lbl
in
allLabels
:
lbl
.
Show
(
show
)
# If we're hiding the labels, do no more
if
not
show
:
return
# Default colour is white - if the orientation labels
# cannot be determined, the background colour will be
# changed to red
colour
=
'
white
'
if
len
(
self
.
_imageList
)
>
0
:
image
=
self
.
_imageList
[
self
.
_displayCtx
.
selectedImage
]
# The image is being displayed as it is stored on
# disk - the image.getOrientation method calculates
# and returns labels for each voxelwise axis.
if
image
.
transform
in
(
'
pixdim
'
,
'
id
'
):
xorient
=
image
.
getVoxelOrientation
(
0
)
yorient
=
image
.
getVoxelOrientation
(
1
)
zorient
=
image
.
getVoxelOrientation
(
2
)
# The image is being displayed in 'real world' space -
# the definition of this space may be present in the
# image meta data
else
:
xorient
=
image
.
getWorldOrientation
(
0
)
yorient
=
image
.
getWorldOrientation
(
1
)
zorient
=
image
.
getWorldOrientation
(
2
)
if
fslimage
.
ORIENT_UNKNOWN
in
(
xorient
,
yorient
,
zorient
):
colour
=
'
red
'
# Imported here to avoid circular import issues
import
fsl.fslview.strings
as
strings
xlo
=
strings
.
imageAxisLowShortLabels
[
xorient
]
ylo
=
strings
.
imageAxisLowShortLabels
[
yorient
]
zlo
=
strings
.
imageAxisLowShortLabels
[
zorient
]
xhi
=
strings
.
imageAxisHighShortLabels
[
xorient
]
yhi
=
strings
.
imageAxisHighShortLabels
[
yorient
]
zhi
=
strings
.
imageAxisHighShortLabels
[
zorient
]
for
lbl
in
allLabels
:
lbl
.
SetForegroundColour
(
colour
)
self
.
_xLeftLabel
.
SetLabel
(
ylo
)
self
.
_xRightLabel
.
SetLabel
(
yhi
)
self
.
_xBottomLabel
.
SetLabel
(
zlo
)
self
.
_xTopLabel
.
SetLabel
(
zhi
)
self
.
_yLeftLabel
.
SetLabel
(
xlo
)
self
.
_yRightLabel
.
SetLabel
(
xhi
)
self
.
_yBottomLabel
.
SetLabel
(
zlo
)
self
.
_yTopLabel
.
SetLabel
(
zhi
)
self
.
_zLeftLabel
.
SetLabel
(
xlo
)
self
.
_zRightLabel
.
SetLabel
(
xhi
)
self
.
_zBottomLabel
.
SetLabel
(
ylo
)
self
.
_zTopLabel
.
SetLabel
(
yhi
)
self
.
_xCanvasPanel
.
Layout
()
self
.
_yCanvasPanel
.
Layout
()
...
...
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