Skip to content

Commit

Permalink
MRG: Improve coreg visual space (mne-tools#3937)
Browse files Browse the repository at this point in the history
* ENH: Tighter coreg display

* FIX: Fix desc
  • Loading branch information
larsoner authored and agramfort committed Jan 30, 2017
1 parent d568dcd commit 7d653f9
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 92 deletions.
23 changes: 18 additions & 5 deletions mne/gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
# License: BSD (3-clause)

from ..utils import _check_mayavi_version, verbose
from ..utils import _check_mayavi_version, verbose, get_config


def combine_kit_markers():
Expand All @@ -24,9 +24,10 @@ def combine_kit_markers():


@verbose
def coregistration(tabbed=False, split=True, scene_width=500, inst=None,
def coregistration(tabbed=False, split=True, scene_width=None, inst=None,
subject=None, subjects_dir=None, guess_mri_subject=True,
head_opacity=1., head_high_res=True, verbose=None):
scene_height=None, head_opacity=1., head_high_res=True,
verbose=None):
"""Coregister an MRI with a subject's head shape.
The recommended way to use the GUI is through bash with:
Expand All @@ -44,8 +45,10 @@ def coregistration(tabbed=False, split=True, scene_width=500, inst=None,
split : bool
Split the main panels with a movable splitter (good for QT4 but
unnecessary for wx backend).
scene_width : int
scene_width : int | None
Specify a minimum width for the 3d scene (in pixels).
Default is None, which uses ``MNE_COREG_SCENE_WIDTH`` config value
(which defaults to 500).
inst : None | str
Path to an instance file containing the digitizer data. Compatible for
Raw, Epochs, and Evoked files.
Expand All @@ -57,6 +60,10 @@ def coregistration(tabbed=False, split=True, scene_width=500, inst=None,
guess_mri_subject : bool
When selecting a new head shape file, guess the subject's name based
on the filename and change the MRI subject accordingly (default True).
scene_height : int | None
Specify a minimum height for the 3d scene (in pixels).
Default is None, which uses ``MNE_COREG_SCENE_WIDTH`` config value
(which defaults to 400).
head_opacity : float
The default opacity of the head surface in the range [0., 1.]
(default: 1.).
Expand All @@ -74,11 +81,17 @@ def coregistration(tabbed=False, split=True, scene_width=500, inst=None,
subjects for which no MRI is available
<http://www.slideshare.net/mne-python/mnepython-scale-mri>`_.
"""
if scene_width is None:
scene_width = get_config('MNE_COREG_SCENE_WIDTH', 500)
if scene_height is None:
scene_height = get_config('MNE_COREG_SCENE_HEIGHT', 400)
scene_width = int(scene_width)
scene_height = int(scene_height)
_check_mayavi_version()
from ._backend import _check_backend
_check_backend()
from ._coreg_gui import CoregFrame, _make_view
view = _make_view(tabbed, split, scene_width)
view = _make_view(tabbed, split, scene_width, scene_height)
gui = CoregFrame(inst, subject, subjects_dir, guess_mri_subject,
head_opacity, head_high_res)
gui.configure_traits(view=view)
Expand Down
160 changes: 89 additions & 71 deletions mne/gui/_coreg_gui.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
"""Traits-based GUI for head-MRI coregistration."""

# Authors: Christian Brodbeck <[email protected]>
Expand All @@ -22,7 +23,7 @@
Enum, Float, HasTraits, HasPrivateTraits, Instance,
Int, on_trait_change, Property, Str)
from traitsui.api import (View, Item, Group, HGroup, VGroup, VGrid, EnumEditor,
Handler, Label, TextEditor)
Handler, Label, TextEditor, Spring)
from traitsui.menu import Action, UndoButton, CancelButton, NoButtons
from tvtk.pyface.scene_editor import SceneEditor

Expand Down Expand Up @@ -75,15 +76,15 @@ class CoregModel(HasPrivateTraits):
n_scale_params = Enum(0, 1, 3, desc="Scale the MRI to better fit the "
"subject's head shape (a new MRI subject will be "
"created with a name specified upon saving)")
scale_x = Float(1, label="Right (X)")
scale_y = Float(1, label="Anterior (Y)")
scale_z = Float(1, label="Superior (Z)")
rot_x = Float(0, label="Right (X)")
rot_y = Float(0, label="Anterior (Y)")
rot_z = Float(0, label="Superior (Z)")
trans_x = Float(0, label="Right (X)")
trans_y = Float(0, label="Anterior (Y)")
trans_z = Float(0, label="Superior (Z)")
scale_x = Float(1, label="R (X)")
scale_y = Float(1, label="A (Y)")
scale_z = Float(1, label="S (Z)")
rot_x = Float(0, label="R (X)")
rot_y = Float(0, label="A (Y)")
rot_z = Float(0, label="S (Z)")
trans_x = Float(0, label="R (X)")
trans_y = Float(0, label="A (Y)")
trans_z = Float(0, label="S (Z)")

prepare_bem_model = Bool(True, desc="whether to run mne_prepare_bem_model "
"after scaling the MRI")
Expand Down Expand Up @@ -296,15 +297,16 @@ def _get_point_distance(self):
def _get_fid_eval_str(self):
d = (self.lpa_distance * 1000, self.nasion_distance * 1000,
self.rpa_distance * 1000)
txt = ("Fiducials Error: LPA %.1f mm, NAS %.1f mm, RPA %.1f mm" % d)
txt = ("Error (mm): LPA %.1f, NAS %.1f, RPA %.1f" % d)
return txt

@cached_property
def _get_points_eval_str(self):
if self.point_distance is None:
return ""
av_dist = np.mean(self.point_distance)
return "Average Points Error: %.1f mm" % (av_dist * 1000)
av_dist = 1000 * np.mean(self.point_distance)
std_dist = 1000 * np.std(self.point_distance)
return u"Points: μ=%.1f, σ = %.1f" % (av_dist, std_dist)

def _get_raw_subject(self):
# subject name guessed based on the inst file name
Expand Down Expand Up @@ -612,7 +614,7 @@ class CoregPanel(HasPrivateTraits):
can_save = DelegatesTo('model')
prepare_bem_model = DelegatesTo('model')
save = Button(label="Save As...")
load_trans = Button
load_trans = Button(label='Load trans...')
queue = Instance(queue.Queue, ())
queue_feedback = Str('')
queue_current = Str('')
Expand All @@ -629,32 +631,40 @@ class CoregPanel(HasPrivateTraits):
VGrid(Item('scale_x', editor=laggy_float_editor,
show_label=True, tooltip="Scale along "
"right-left axis",
enabled_when='n_scale_params > 0'),
enabled_when='n_scale_params > 0',
width=+50),
Item('scale_x_dec',
enabled_when='n_scale_params > 0'),
enabled_when='n_scale_params > 0',
width=-50),
Item('scale_x_inc',
enabled_when='n_scale_params > 0'),
enabled_when='n_scale_params > 0',
width=-50),
Item('scale_step', tooltip="Scaling step",
enabled_when='n_scale_params > 0'),
enabled_when='n_scale_params > 0',
width=+50),
Item('scale_y', editor=laggy_float_editor,
show_label=True,
enabled_when='n_scale_params > 1',
tooltip="Scale along anterior-posterior "
"axis"),
"axis", width=+50),
Item('scale_y_dec',
enabled_when='n_scale_params > 1'),
enabled_when='n_scale_params > 1',
width=-50),
Item('scale_y_inc',
enabled_when='n_scale_params > 1'),
Label('(Step)'),
enabled_when='n_scale_params > 1',
width=-50),
Label('(Step)', width=+50),
Item('scale_z', editor=laggy_float_editor,
show_label=True,
enabled_when='n_scale_params > 1',
tooltip="Scale along anterior-posterior "
"axis"),
"axis", width=+50),
Item('scale_z_dec',
enabled_when='n_scale_params > 1'),
enabled_when='n_scale_params > 1',
width=-50),
Item('scale_z_inc',
enabled_when='n_scale_params > 1'),
enabled_when='n_scale_params > 1',
width=-50),
show_labels=False, columns=4),
HGroup(Item('fits_hsp_points',
enabled_when='n_scale_params',
Expand All @@ -675,55 +685,63 @@ class CoregPanel(HasPrivateTraits):
"minimize the distance of the three "
"fiducials."),
show_labels=False),
'_',
Label("Translation:"),
VGrid(Item('trans_x', editor=laggy_float_editor,
show_label=True, tooltip="Move along "
"right-left axis"),
'trans_x_dec', 'trans_x_inc',
Item('trans_step', tooltip="Movement step"),
"right-left axis", width=+50),
Item('trans_x_dec', width=-50),
Item('trans_x_inc', width=-50),
Item('trans_step', tooltip="Movement step",
width=+50),
Item('trans_y', editor=laggy_float_editor,
show_label=True, tooltip="Move along "
"anterior-posterior axis"),
'trans_y_dec', 'trans_y_inc',
Label('(Step)'),
"anterior-posterior axis", width=+50),
Item('trans_y_dec', width=-50),
Item('trans_y_inc', width=-50),
Label('(Step)', width=+50),
Item('trans_z', editor=laggy_float_editor,
show_label=True, tooltip="Move along "
"anterior-posterior axis"),
'trans_z_dec', 'trans_z_inc',
show_labels=False, columns=4),
Label("Rotation:"),
"anterior-posterior axis", width=+50),
Item('trans_z_dec', width=-50),
Item('trans_z_inc', width=-50),
show_labels=False, show_border=True,
label='Translation', columns=4),
VGrid(Item('rot_x', editor=laggy_float_editor,
show_label=True, tooltip="Rotate along "
"right-left axis"),
'rot_x_dec', 'rot_x_inc',
Item('rot_step', tooltip="Rotation step"),
"right-left axis", width=+50),
Item('rot_x_dec', width=-50),
Item('rot_x_inc', width=-50),
Item('rot_step', tooltip="Rotation step",
width=+50),
Item('rot_y', editor=laggy_float_editor,
show_label=True, tooltip="Rotate along "
"anterior-posterior axis"),
'rot_y_dec', 'rot_y_inc',
Label('(Step)'),
"anterior-posterior axis", width=+50),
Item('rot_y_dec', width=-50),
Item('rot_y_inc', width=-50),
Label('(Step)', width=+50),
Item('rot_z', editor=laggy_float_editor,
show_label=True, tooltip="Rotate along "
"anterior-posterior axis"),
'rot_z_dec', 'rot_z_inc',
show_labels=False, columns=4),
"anterior-posterior axis", width=+50),
Item('rot_z_dec', width=-50),
Item('rot_z_inc', width=-50),
show_labels=False, show_border=True,
label='Rotation', columns=4),
# buttons
HGroup(Item('fit_hsp_points',
enabled_when='has_pts_data',
tooltip="Rotate the head shape (around the "
"nasion) so as to minimize the distance "
"from each head shape point to its closest "
"MRI point"),
"MRI point", width=10),
Item('fit_ap', enabled_when='has_fid_data',
tooltip="Try to match the LPA and the RPA, "
"leaving the Nasion in place"),
"leaving the Nasion in place", width=10),
Item('fit_fid', enabled_when='has_fid_data',
tooltip="Move and rotate the head shape so "
"as to minimize the distance between the "
"MRI and head shape fiducials"),
Item('load_trans', enabled_when='has_fid_data'),
"MRI and head shape fiducials", width=10),
show_labels=False),
HGroup(Item('load_trans', enabled_when='has_fid_data',
width=10), Spring(), show_labels=False),
'_',
Item('fid_eval_str', style='readonly'),
Item('points_eval_str', style='readonly'),
Expand Down Expand Up @@ -1033,7 +1051,6 @@ class NewMriDialog(HasPrivateTraits):
Item('overwrite', enabled_when='can_overwrite', tooltip="If a "
"subject with the chosen name exists, delete the old "
"subject"),
width=500,
buttons=[CancelButton,
Action(name='OK', enabled_when='can_save')])

Expand Down Expand Up @@ -1082,7 +1099,7 @@ def update_dialog(self):
self.can_overwrite = False


def _make_view(tabbed=False, split=False, scene_width=500):
def _make_view(tabbed=False, split=False, scene_width=500, scene_height=400):
"""Create a view for the CoregFrame.
Parameters
Expand All @@ -1101,14 +1118,14 @@ def _make_view(tabbed=False, split=False, scene_width=500):
view : traits View
View object for the CoregFrame.
"""
view_options = VGroup(
Item('headview', style='custom'), 'view_options', show_border=True,
show_labels=False, label='View')

scene = VGroup(
Item('scene', show_label=False,
editor=SceneEditor(scene_class=MayaviScene),
dock='vertical', width=scene_width), view_options)
dock='vertical', width=scene_width, height=scene_height),
VGroup(
Item('headview', style='custom'),
'view_options',
show_border=True, show_labels=False, label='View'))

data_panel = VGroup(
VGroup(Item('subject_panel', style='custom'), label="MRI Subject",
Expand All @@ -1126,20 +1143,19 @@ def _make_view(tabbed=False, split=False, scene_width=500):
HGroup('guess_mri_subject',
Label('Guess MRI Subject from File Name'),
show_labels=False),
HGroup(Item('distance', show_label=True), 'omit_points',
'reset_omit_points', show_labels=False),
HGroup(Item('distance', show_label=False, width=20),
'omit_points', 'reset_omit_points', show_labels=False),
Item('omitted_info', style='readonly', show_label=False),
label='Head Shape Source (Raw/Epochs/Evoked)', show_border=True,
show_labels=False), show_labels=False, label="Data Source")
show_labels=False),
show_labels=False, label="Data Source")

coreg_panel = VGroup(
Item('coreg_panel', style='custom'), label="Coregistration",
show_border=True, show_labels=False, enabled_when="fid_panel.locked")
Item('coreg_panel', style='custom', width=1),
label="Coregistration", show_border=True, show_labels=False,
enabled_when="fid_panel.locked")

if split:
main_layout = 'split'
else:
main_layout = 'normal'
main_layout = 'split' if split else 'normal'

if tabbed:
main = HGroup(scene,
Expand All @@ -1150,8 +1166,10 @@ def _make_view(tabbed=False, split=False, scene_width=500):
main = HGroup(data_panel, scene, coreg_panel, show_labels=False,
layout=main_layout)

# Here we set the width and height to impossibly small numbers to force the
# window to be as tight as possible
view = View(main, resizable=True, handler=CoregFrameHandler(),
buttons=NoButtons)
buttons=NoButtons, width=scene_width, height=scene_height)
return view


Expand Down Expand Up @@ -1182,12 +1200,12 @@ class CoregFrame(HasTraits):
guess_mri_subject = DelegatesTo('model')

# Omit Points
distance = Float(5., label="Distance [mm]", desc="Maximal distance for "
"head shape points from MRI in mm")
omit_points = Button(label='Omit Points', desc="Omit head shape points "
distance = Float(5., desc="maximal distance for head shape points from "
"MRI in mm")
omit_points = Button(label='Omit [mm]', desc="to omit head shape points "
"for the purpose of the automatic coregistration "
"procedure.")
reset_omit_points = Button(label='Reset Omission', desc="Reset the "
reset_omit_points = Button(label='Reset', desc="to reset the "
"omission of head shape points to include all.")
omitted_info = Property(Str, depends_on=['model.hsp.n_omitted'])

Expand Down
Loading

0 comments on commit 7d653f9

Please sign in to comment.