Skip to content
Snippets Groups Projects
Commit d119bc2f authored by David Flitney's avatar David Flitney
Browse files

Updates for Steve to make the Signal ICs obvious

parent 04d914d0
No related branches found
No related merge requests found
......@@ -22,6 +22,7 @@
"""
import os
import re
os.environ['ETS_TOOLKIT'] = 'qt4'
os.environ['QT_API'] = 'pyside'
......@@ -65,6 +66,21 @@ from mpl_toolkits.axes_grid1 import make_axes_locatable
from nibabel.spatialimages import ImageFileError
#import gc
from configobj import ConfigObj
import validate
validator = validate.Validator()
opts= """
plot_height = float(default=400)
plot_width = float(default=0.8)
height = float(default=0.8)
width = float(default=0.8)
ncols = integer(default=0)
"""
settings_file = os.path.join(os.path.expanduser('~'), '.melviewrc')
settings = ConfigObj(settings_file, configspec = opts.split("\n"))
settings.validate(validator, copy=True)
class _MPLFigureEditor(Editor):
scrollable = True
......@@ -174,39 +190,42 @@ class Classification(HasTraits):
from traitsui.api import TableEditor
from traitsui.table_column import ObjectColumn
#from pyface.ui.wx.grid.checkbox_renderer \
# import CheckboxRenderer
class CheckboxColumn(ObjectColumn):
def __init__ ( self, **traits ):
""" Initializes the object.
"""
super( CheckboxColumn, self ).__init__( **traits )
# force the renderer to be a checkbox renderer
# self.renderer = CheckboxRenderer()
#---------------------------------------------------------------------------
# Returns the cell background color for the column for a specified object:
#---------------------------------------------------------------------------
from enthought.traits.api import Color
from enthought.traits.ui.api import TabularEditor
class ClassificationColumn(ObjectColumn):
def get_cell_color ( self, object ):
""" Returns the cell background color for the column for a specified
object.
"""
# we override this from the parent class to ALWAYS provide the
# standard color
return self.cell_color_
#---------------------------------------------------------------------------
# Returns whether the column is editable for a specified object:
#---------------------------------------------------------------------------
def is_editable ( self, object ):
""" Returns whether the column is editable for a specified object.
"""
# Although a checkbox column is always editable, we return this
# to keep a standard editor from appearing. The editing is handled
# in the renderer's handlers.
return False
return [ 'light blue', 'yellow' ][ object.class_name in ['Signal']]
#class CheckboxColumn(ObjectColumn):
#
# def __init__ ( self, **traits ):
# """ Initializes the object.
# """
# super( CheckboxColumn, self ).__init__( **traits )
# #---------------------------------------------------------------------------
# # Returns the cell background color for the column for a specified object:
# #---------------------------------------------------------------------------
#
# def get_cell_color ( self, object ):
# """ Returns the cell background color for the column for a specified
# object.
# """
# # we override this from the parent class to ALWAYS provide the
# # standard color
# return self.cell_color_
# #---------------------------------------------------------------------------
# # Returns whether the column is editable for a specified object:
# #---------------------------------------------------------------------------
#
# def is_editable ( self, object ):
# """ Returns whether the column is editable for a specified object.
# """
# # Although a checkbox column is always editable, we return this
# # to keep a standard editor from appearing. The editing is handled
# # in the renderer's handlers.
# return False
class MelodicWindowHandler(Handler):
......@@ -282,10 +301,117 @@ class MelodicWindow(HasTraits):
lbox = Instance(Figure, ())
plots = Instance(Figure, ())
def montageXZ(self, X, colormap=cm.gist_gray):
nics = Int(1)
ic = Int(0)
tr = Float(2.0)
lut_min = Float(0)
lut_max = Float(10)
background_min = Float(0)
background_max = Float(10)
ncols = Int(settings['ncols'])
needs_saving = Bool(False)
show_stats = Bool(True)
ignore_blank_slices = Bool(True)
filedir = Str
filename = Str
file_wildcard = Str("Text file (*.txt)|*.txt|Data file (*.dat)|*.dat|All files|*")
# quit = Action(name="Quit", action='do_quit')
load = Action(name = "Load classifications...", action = "do_load")
save = Action(name = "Save classifications", action = "do_save",
enabled_when='needs_saving and filename is not ""')
save_as = Action(name = "Save as...", action = "do_save_as", enabled_when='needs_saving')
# reset = Action(name="Reset LUT", action='do_reset_lut')
reset_button = Button(label="Reset LUT to data max/min")
record_zoom = Button(label="Record Zoom")
reset_zoom = Button(label="Reset Zoom")
from traitsui.key_bindings import KeyBinding, KeyBindings
key_bindings = KeyBindings(
KeyBinding( binding1 = 'Ctrl-A',
description = 'previous IC',
method_name = '_prev_button_fired' ),
KeyBinding( binding1 = 'Ctrl-Z',
description = 'next IC',
method_name = '_next_button_fired' )
)
ic_selected = Instance(Classification)
ic_list_editor = TableEditor(columns = [ ClassificationColumn( name='ic_number', label='IC#', editable=False ),
ClassificationColumn( name='class_name' ) ],
selected = 'ic_selected',
selection_mode = 'row',
rows = 5,
row_factory = Classification)
orientation_names = ['XY', 'XZ', 'YZ']
view_orientation = Enum(values='orientation_names')
view = View(
Group(
VSplit(
HSplit(
Item('lbox',
editor=MPLFigureEditor(),
show_label=False, width=settings['plot_width'], height=settings['plot_height']),
Item('class_list',
editor=ic_list_editor,
show_label=False, springy=False, resizable=False)
),
HSplit(
Item('plots',
editor=MPLFigureEditor(),
show_label=False, width=0.7, height=0.2),
VGroup(Item('ic_selected', label='Classification', style='custom'),
HGroup(Item('show_stats', label='Show statistics'),
Item('lut_min', label="LUT min/max"),
Item('lut_max', show_label=False)),
Item('ignore_blank_slices'),
HGroup(Item('reset_button', show_label=False)),
HGroup(Item('record_zoom', show_label=False),
Item('reset_zoom', show_label=False),
Item('ncols', editor=RangeEditor(Range(0, 100), mode='spinner'))),
HGroup(Item('view_orientation'))
)
)
),
label='Explorer'),
Group(Item('dirpath', label='ICA directory'),
Item('bgimage', label='Structural image'),
Item('tr'),
Item('threshold'),
Item('background_min'),
Item('background_max'),
label='Data'),
title = 'Melodic Results Viewer',
width = settings['width'],
height = settings['height'],
# x = 100, y = 100,
resizable = True,
key_bindings = key_bindings,
# buttons = [ load, save, save_as, quit ],
buttons = [ load, save, save_as ],
handler = MelodicWindowHandler()
)
def montageXZ(self, X, ncols=0):
x, y, z = shape(X)
mm = int(ceil(sqrt(y) * 1.5) - 1)
nn = y / mm + 1
if ncols == 0:
mm = int(ceil(sqrt(z) * 1.5) - 1)
else:
mm = ncols
nn = z / mm + 1
M = zeros((mm * x, nn * z))
......@@ -295,16 +421,18 @@ class MelodicWindow(HasTraits):
if image_id >= y:
break
sliceM, sliceN = j * x, k * z
# print j, k, sliceM, sliceN
M[sliceM:sliceM + x, sliceN:sliceN + z] = X[:, image_id, :]
image_id += 1
return M
def montageYZ(self, X, colormap=cm.gist_gray):
def montageYZ(self, X, ncols=0):
x, y, z = shape(X)
mm = int(ceil(sqrt(x) * 1.5) - 1)
nn = x / mm + 1
if ncols == 0:
mm = int(ceil(sqrt(y) * 1.5) - 1)
else:
mm = ncols
nn = y / mm + 1
M = zeros((mm * y, nn * z))
......@@ -314,18 +442,18 @@ class MelodicWindow(HasTraits):
if image_id >= x:
break
sliceM, sliceN = j * y, k * z
# print j, k, sliceM, sliceN
M[sliceM:sliceM + y, sliceN:sliceN + z] = X[image_id, :, :]
image_id += 1
return M
def montageXY(self, X, colormap=cm.gist_gray):
def montageXY(self, X, ncols=0):
x, y, z = shape(X)
mm = int(ceil(sqrt(z) * 1.5)-1)
if ncols == 0:
mm = int(ceil(sqrt(x) * 1.5) - 1)
else:
mm = ncols
nn = z / mm + 1
# print m, n, count
# print mm, nn
M = zeros((mm * x, nn * y))
image_id = 0
......@@ -334,16 +462,16 @@ class MelodicWindow(HasTraits):
if image_id >= z:
break
sliceM, sliceN = j * x, k * y
# print j, k, sliceM, sliceN
M[sliceM:sliceM + x, sliceN:sliceN + y] = X[:, :, image_id]
image_id += 1
return M
def __init__(self, path="", bgpath="", ic=1, *args, **kwargs):
# print "MelodicWindow.__init__()"
print "MelodicWindow.__init__()"
super(MelodicWindow, self).__init__(*args, **kwargs)
# print "MelodicWindow.__init__() called super"
self.montage = self.montageXY
try:
self.image_axes = self.lbox.add_subplot(1,1,1)
......@@ -396,6 +524,10 @@ class MelodicWindow(HasTraits):
@lru_cache(maxsize=10)
def get_bgdata(self, path):
return nb.load(path).get_data()
def nonzero_slice_list(self, X):
(nx, ny, nz) = X.shape
return [n for n in range(0, nz) if np.max(X[:,:,n]) > 0]
@on_trait_change('bgimage')
def bgimage_changed(self, obj, name, oldpath, path):
......@@ -480,11 +612,18 @@ class MelodicWindow(HasTraits):
def display(self):
# print "MelodicWindow.display()"
d=self.mel.get_stat_data()
b=self.bg_data
zi = rot90(self.montage(d))
bg = rot90(self.montage(b))
if self.ignore_blank_slices:
slices = self.nonzero_slice_list(b)
zi = rot90(self.montage(d[:,:,slices],ncols=self.ncols))
bg = rot90(self.montage(b[:,:,slices],ncols=self.ncols))
else:
zi = rot90(self.montage(d,ncols=self.ncols))
bg = rot90(self.montage(b,ncols=self.ncols))
self.image_axes.clear()
self.image_axes.axis('off')
......@@ -492,7 +631,8 @@ class MelodicWindow(HasTraits):
vmin=self.background_min,
vmax=self.background_max)
bgim.set_interpolation("nearest")
if self.show_stats:
if self.show_stats and not (self.ic_selected == None):
cm1 = cm.hot
cm1.set_under(alpha=0)
im1 = self.image_axes.imshow(zi, cmap=cm1, vmin=self.lut_min, vmax=self.lut_max)
......@@ -502,12 +642,18 @@ class MelodicWindow(HasTraits):
im2 = self.image_axes.imshow(zi, cmap=cm2, vmax=-self.lut_min, vmin=-self.lut_max)
im2.set_interpolation("nearest")
if not hasattr(self, "colorbar"):
divider = make_axes_locatable(self.image_axes)
cax = divider.append_axes("right", size="2.5%", pad=0.05)
self.colorbar = self.image_axes.get_figure().colorbar(im1, cax=cax)
self.colorbar.set_clim(self.lut_min, self.lut_max)
self.colorbar.update_normal(d)
if not self.ic_selected == None:
if not hasattr(self, "colorbar"):
divider = make_axes_locatable(self.image_axes)
cax = divider.append_axes("right", size="2.5%", pad=0.05)
self.colorbar = self.image_axes.get_figure().colorbar(im1, cax=cax)
self.colorbar.set_clim(self.lut_min, self.lut_max)
self.colorbar.update_normal(d)
if self.ic_selected.class_name in 'Signal':
self.image_axes.set_title('Signal!', bbox={'color':'green', 'pad':2, 'width':400})
else:
self.image_axes.set_title(self.ic_selected.class_name)
y = self.mel.get_mix_data()
n = y.shape[0]
......@@ -525,6 +671,10 @@ class MelodicWindow(HasTraits):
if self.initialised:
self.lbox.canvas.draw()
@on_trait_change('ncols')
def ncolsChange(self, ncols):
self.display()
@on_trait_change('view_orientation')
def orientationChange(self, orient):
self.montage=getattr(self, 'montage{0}'.format(orient))
......@@ -545,11 +695,22 @@ class MelodicWindow(HasTraits):
# print "Ding!"
pass
@on_trait_change('show_stats')
def showStatsChanged(self):
# print "Ding!"
@on_trait_change('show_stats,ignore_blank_slices')
def optionsChanged(self):
self.image_axes.clear()
self.display()
# @on_trait_change('ignore_blank_slices')
# def ignoreBlanksChanged(self):
# # print "Ding!"
# self.image_axes.clear()
# self.display()
#
# @on_trait_change('show_stats')
# def showStatsChanged(self):
# # print "Ding!"
# self.image_axes.clear()
# self.display()
@on_trait_change('ic_list_editor')
def selectedElementChanged(self):
......@@ -601,16 +762,23 @@ class MelodicWindow(HasTraits):
def file_load(self):
print "MelodicWindow.file_load()"
path = os.path.join(self.filedir, self.filename)
# print "Loading file {0}".format(self.filename)
print "Loading file {0}".format(path)
try:
import re
f = open(path, 'r')
cl = []
# print len(cl)
lines = f.readlines()
# print lines[0]
self.dirpath=lines[0].strip()
icadirpath=lines[0].strip()
if re.match('^/', icadirpath):
self.dirpath = icadirpath
else:
self.dirpath = os.path.join(self.filedir, icadirpath)
for ic in arange(0, self.nics):
cl.append( Classification(ic_number=ic+1, class_name='Unknown', filter=False) )
......@@ -624,7 +792,6 @@ class MelodicWindow(HasTraits):
last_line=lines[-1]
if doFix:
import re
if re.match('\[[0-9, ]*\]\n$', last_line):
for ic in eval(last_line):
......@@ -667,112 +834,9 @@ class MelodicWindow(HasTraits):
print e
error(None, 'Unable to open file for writing\n{0}'.format(path), title='File I/O error')
nics = Int(1)
ic = Int(0)
tr = Float(2.0)
lut_min = Float(0)
lut_max = Float(10)
background_min = Float(0)
background_max = Float(10)
needs_saving = Bool(False)
show_stats = Bool(True)
filedir = Str
filename = Str
file_wildcard = Str("Text file (*.txt)|*.txt|Data file (*.dat)|*.dat|All files|*")
# quit = Action(name="Quit", action='do_quit')
load = Action(name = "Load classifications...", action = "do_load")
save = Action(name = "Save classifications", action = "do_save",
enabled_when='needs_saving and filename is not ""')
save_as = Action(name = "Save as...", action = "do_save_as", enabled_when='needs_saving')
# reset = Action(name="Reset LUT", action='do_reset_lut')
reset_button = Button(label="Reset LUT to data max/min")
record_zoom = Button(label="Record Zoom")
reset_zoom = Button(label="Reset Zoom")
from traitsui.key_bindings import KeyBinding, KeyBindings
key_bindings = KeyBindings(
KeyBinding( binding1 = 'Ctrl-A',
description = 'previous IC',
method_name = '_prev_button_fired' ),
KeyBinding( binding1 = 'Ctrl-Z',
description = 'next IC',
method_name = '_next_button_fired' )
)
ic_selected = Instance(Classification)
ic_list_editor = TableEditor(columns = [ ObjectColumn( name='ic_number', label='IC#', editable=False ),
#CheckboxColumn( name='filter', cell_color = 'yellow' ),
ObjectColumn( name='class_name' ) ],
selected = 'ic_selected',
selection_mode = 'row',
rows = 5,
row_factory = Classification)
orientation_names = ['XY', 'XZ', 'YZ']
view_orientation = Enum(values='orientation_names')
view = View(Group(VSplit(HSplit(
Item('lbox',
editor=MPLFigureEditor(),
# editor=CustomEditor(make_plot),
show_label=False, width=0.8),
Item('class_list',
editor=ic_list_editor,
show_label=False, springy=True, resizable=False,
height=0.8)
),
HSplit(
Item('plots',
editor=MPLFigureEditor(),
# editor=CustomEditor(make_plot),
show_label=False),
VGroup(Item('ic_selected', label='Classification', style='custom'),
HGroup(Item('show_stats', label='Show statistics'),
Item('lut_min', label="LUT min/max"),
Item('lut_max', show_label=False)),
HGroup(Item('reset_button', show_label=False)),
HGroup(Item('record_zoom', show_label=False),
Item('reset_zoom', show_label=False)),
HGroup(Item('view_orientation'))
)
)
),
label='Explorer'),
Group(Item('dirpath', label='ICA directory'),
Item('bgimage', label='Structural image'),
Item('tr'),
Item('threshold'),
Item('background_min'),
Item('background_max'),
label='Data'),
title = 'Melodic Results Viewer',
width = 1000,
height = 600,
x = 100, y = 100,
resizable = True,
key_bindings = key_bindings,
# buttons = [ load, save, save_as, quit ],
buttons = [ load, save, save_as ],
handler = MelodicWindowHandler()
)
def main():
# print "__main__"
from configobj import ConfigObj
import validate
cfg = """
path = string(default='')
bgpath = string(default='')
......@@ -780,8 +844,8 @@ def main():
# print "__main__ reading config"
config = ConfigObj('melview.ini', configspec=cfg.split("\n"))
validator = validate.Validator()
config.validate(validator, copy=True)
# print "__main__ read config"
m = MelodicWindow(path=config['path'], bgpath=config['bgpath'])
......@@ -795,5 +859,13 @@ def main():
except:
error(None, 'Error while writing config file to working directory.', title='Config error')
settings['ncols'] = m.ncols
try:
settings.write()
except:
error(None, 'Error while writing settings file to home directory.', title='Settings error')
if __name__ == "__main__":
main()
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