Skip to content
Snippets Groups Projects
Commit ff41d480 authored by Paul McCarthy's avatar Paul McCarthy
Browse files

ModelOpts.coordSpace now defaults to pixdim-flip, as this is what

FIRST outputs. Changed glmodel draw routine - I think, if the
vertex transformation has a positive determinant, the face order
needs to be inverted.
parent bcbea5d9
No related branches found
No related tags found
No related merge requests found
...@@ -392,7 +392,7 @@ _DISPLAY_PROPS = td.TypeDict({ ...@@ -392,7 +392,7 @@ _DISPLAY_PROPS = td.TypeDict({
# props.Widget('showName'), # props.Widget('showName'),
props.Widget('coordSpace', props.Widget('coordSpace',
enabledWhen=lambda o, ri: ri != 'none', enabledWhen=lambda o, ri: ri != 'none',
labels=strings.choices['Nifti1Opts.transform'], labels=strings.choices['ModelOpts.coordSpace'],
dependencies=['refImage'])], dependencies=['refImage'])],
'TensorOpts' : [ 'TensorOpts' : [
......
...@@ -9,8 +9,6 @@ for displaying :class:`.Model` overlays. ...@@ -9,8 +9,6 @@ for displaying :class:`.Model` overlays.
""" """
import copy
import numpy as np import numpy as np
import props import props
...@@ -21,8 +19,6 @@ import fsl.fsleyes.colourmaps as colourmaps ...@@ -21,8 +19,6 @@ import fsl.fsleyes.colourmaps as colourmaps
import fsl.data.image as fslimage import fsl.data.image as fslimage
import fsl.utils.transform as transform import fsl.utils.transform as transform
import volumeopts
class ModelOpts(fsldisplay.DisplayOpts): class ModelOpts(fsldisplay.DisplayOpts):
"""The ``ModelOpts`` class defines settings for displaying :class:`.Model` """The ``ModelOpts`` class defines settings for displaying :class:`.Model`
...@@ -65,10 +61,37 @@ class ModelOpts(fsldisplay.DisplayOpts): ...@@ -65,10 +61,37 @@ class ModelOpts(fsldisplay.DisplayOpts):
""" """
coordSpace = copy.copy(volumeopts.Nifti1Opts.transform) # This property is implicitly tightly-coupled to
# the Nifti1Opts.getTransform method - the choices
# defined in this property are assumed to be valid
# inputs to that method.
coordSpace = props.Choice(('affine', 'pixdim', 'pixdim-flip', 'id'),
default='pixdim-flip')
"""If :attr:`refImage` is not ``None``, this property defines the """If :attr:`refImage` is not ``None``, this property defines the
reference image coordinate space in which the model coordinates are reference image coordinate space in which the model coordinates are
defined (i.e. voxels, scaled voxels, or world coordinates). defined (i.e. voxels, scaled voxels, or world coordinates).
=============== =========================================================
``affine`` The model coordinates are defined in the reference image
world coordinate system.
``id`` The model coordinates are defined in the reference image
voxel coordinate system.
``pixdim`` The model coordinates are defined in the reference image
voxel coordinate system, scaled by the voxel pixdims.
``pixdim-flip`` The model coordinates are defined in the reference image
voxel coordinate system, scaled by the voxel pixdims. If
the reference image transformation matrix has a positive
determinant, the X axis is flipped.
=============== =========================================================
The default value is ``pixdim-flip``, as this is the coordinate system
used in the VTK sub-cortical segmentation model files output by FIRST.
See also the :ref:`note on coordinate systems
<volumeopts-coordinate-systems>`, and the :meth:`.Nifti1Opts.getTransform`
method.
""" """
...@@ -77,12 +100,6 @@ class ModelOpts(fsldisplay.DisplayOpts): ...@@ -77,12 +100,6 @@ class ModelOpts(fsldisplay.DisplayOpts):
to the :class:`.DisplayOpts` constructor. to the :class:`.DisplayOpts` constructor.
""" """
# The Nifti1Opts.transform property has a
# 'custom' option which is not applicable
# to our coordSpace property.
coordSpace = self.getProp('coordSpace')
coordSpace.removeChoice('custom', self)
# Create a random, highly # Create a random, highly
# saturated colour # saturated colour
colour = colourmaps.randomBrightColour() colour = colourmaps.randomBrightColour()
...@@ -162,11 +179,12 @@ class ModelOpts(fsldisplay.DisplayOpts): ...@@ -162,11 +179,12 @@ class ModelOpts(fsldisplay.DisplayOpts):
the :class:`.Model` vertex coordinates into the display coordinate the :class:`.Model` vertex coordinates into the display coordinate
system. system.
If no :attr:`refImage` is selected, this method returns ``None``. If no :attr:`refImage` is selected, this method returns an identity
transformation.
""" """
if self.refImage is None: if self.refImage is None:
return None return np.eye(4)
opts = self.displayCtx.getOpts(self.refImage) opts = self.displayCtx.getOpts(self.refImage)
...@@ -346,11 +364,9 @@ class ModelOpts(fsldisplay.DisplayOpts): ...@@ -346,11 +364,9 @@ class ModelOpts(fsldisplay.DisplayOpts):
lo, hi = self.overlay.getBounds() lo, hi = self.overlay.getBounds()
xform = self.getCoordSpaceTransform() xform = self.getCoordSpaceTransform()
if xform is not None: lohi = transform.transform([lo, hi], xform)
lohi.sort(axis=0)
lohi = transform.transform([lo, hi], xform) lo, hi = lohi[0, :], lohi[1, :]
lohi.sort(axis=0)
lo, hi = lohi[0, :], lohi[1, :]
self.bounds = [lo[0], hi[0], lo[1], hi[1], lo[2], hi[2]] self.bounds = [lo[0], hi[0], lo[1], hi[1], lo[2], hi[2]]
......
...@@ -9,8 +9,9 @@ to render :class:`.Model` overlays. ...@@ -9,8 +9,9 @@ to render :class:`.Model` overlays.
""" """
import numpy as np import numpy as np
import OpenGL.GL as gl import numpy.linalg as npla
import OpenGL.GL as gl
import globject import globject
import fsl.utils.transform as transform import fsl.utils.transform as transform
...@@ -173,10 +174,9 @@ class GLModel(globject.GLObject): ...@@ -173,10 +174,9 @@ class GLModel(globject.GLObject):
vertices = self.overlay.vertices vertices = self.overlay.vertices
indices = self.overlay.indices indices = self.overlay.indices
xform = self.opts.getCoordSpaceTransform()
xform = self.opts.getCoordSpaceTransform() if not np.all(xform == np.eye(4)):
if xform is not None:
vertices = transform.transform(vertices, xform) vertices = transform.transform(vertices, xform)
self.vertices = np.array(vertices, dtype=np.float32) self.vertices = np.array(vertices, dtype=np.float32)
...@@ -330,16 +330,19 @@ class GLModel(globject.GLObject): ...@@ -330,16 +330,19 @@ class GLModel(globject.GLObject):
# plane. # plane.
gl.glStencilFunc(gl.GL_ALWAYS, 0, 0) gl.glStencilFunc(gl.GL_ALWAYS, 0, 0)
# I don't understand why, but if any of the # If the model coordinate transformation
# display system axes are inverted, we need # has a positive determinant, we need to
# to render the back faces first, otherwise # render the back faces first, otherwise
# the cross-section mask will not be created # the cross-section mask will not be
# correctly. # created correctly. Something to do with
# the vertex unwinding order, I guess.
direction = [gl.GL_INCR, gl.GL_DECR] direction = [gl.GL_INCR, gl.GL_DECR]
if np.any(np.array(hi) < 0.0): faceOrder = [gl.GL_FRONT, gl.GL_BACK]
else: faceOrder = [gl.GL_BACK, gl.GL_FRONT]
if npla.det(opts.getCoordSpaceTransform()) > 0:
faceOrder = [gl.GL_BACK, gl.GL_FRONT]
else:
faceOrder = [gl.GL_FRONT, gl.GL_BACK]
for face, direction in zip(faceOrder, direction): for face, direction in zip(faceOrder, direction):
gl.glStencilOp(gl.GL_KEEP, gl.GL_KEEP, direction) gl.glStencilOp(gl.GL_KEEP, gl.GL_KEEP, direction)
......
...@@ -416,16 +416,22 @@ labels = TypeDict({ ...@@ -416,16 +416,22 @@ labels = TypeDict({
'OverlayInfoPanel.MelodicImage' : 'NIFTI1 image ' 'OverlayInfoPanel.MelodicImage' : 'NIFTI1 image '
'(MELODIC analysis)', '(MELODIC analysis)',
'OverlayInfoPanel.MelodicImage.melodicInfo' : 'MELODIC information', 'OverlayInfoPanel.MelodicImage.melodicInfo' : 'MELODIC information',
'OverlayInfoPanel.Model' : 'VTK model',
'OverlayInfoPanel.Model.numVertices' : 'Number of vertices', 'OverlayInfoPanel.Model' : 'VTK model',
'OverlayInfoPanel.Model.numIndices' : 'Number of indices', 'OverlayInfoPanel.Model.numVertices' : 'Number of vertices',
'OverlayInfoPanel.Model.displaySpace' : 'Display space', 'OverlayInfoPanel.Model.numIndices' : 'Number of indices',
'OverlayInfoPanel.Model.refImage' : 'Reference image', 'OverlayInfoPanel.Model.displaySpace' : 'Display space',
'OverlayInfoPanel.Model.coordSpace' : 'Vertices defined in', 'OverlayInfoPanel.Model.refImage' : 'Reference image',
'OverlayInfoPanel.Model.coordSpace.id' : 'Voxels ({})', 'OverlayInfoPanel.Model.coordSpace' : 'Vertices defined in',
'OverlayInfoPanel.Model.coordSpace.pixdim' : 'Scaled voxels ({})', 'OverlayInfoPanel.Model.coordSpace.id' : 'Voxels ({})',
'OverlayInfoPanel.Model.coordSpace.affine' : 'World coordinates ({})', 'OverlayInfoPanel.Model.coordSpace.pixdim' : 'Scaled voxels ({})',
'OverlayInfoPanel.Model.coordSpace.display' : 'Display coordinate system', 'OverlayInfoPanel.Model.coordSpace.pixdim-flip' : 'Scaled voxels forced '
'to radiological '
'orientation ({})',
'OverlayInfoPanel.Model.coordSpace.affine' : 'World coordinates ({})',
'OverlayInfoPanel.Model.coordSpace.display' : 'Display coordinate '
'system',
'OverlayInfoPanel.dataSource' : 'Data source', 'OverlayInfoPanel.dataSource' : 'Data source',
'OverlayInfoPanel.TensorImage' : 'NIFTI1 diffusion ' 'OverlayInfoPanel.TensorImage' : 'NIFTI1 diffusion '
...@@ -651,6 +657,12 @@ choices = TypeDict({ ...@@ -651,6 +657,12 @@ choices = TypeDict({
'ModelOpts.refImage.none' : 'No reference image', 'ModelOpts.refImage.none' : 'No reference image',
'ModelOpts.coordSpace' : {'affine' : 'World coordinates',
'pixdim' : 'Scaled voxels',
'pixdim-flip' : 'Scaled voxels forced to '
'radiological orientation',
'id' : 'Voxels'},
'TensorOpts.tensorResolution.min' : 'Low', 'TensorOpts.tensorResolution.min' : 'Low',
'TensorOpts.tensorResolution.max' : 'High', 'TensorOpts.tensorResolution.max' : 'High',
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment