Skip to content

Commit

Permalink
Added more colorschemes and a new GUI to create more colorschemes.
Browse files Browse the repository at this point in the history
- Added support to colorschemes from OsiriX.
- Created a new GUI to create colorschemes
  - This GUI generates the event EVT_CLUT_NODE_CHANGED when the user
    changes the position and colours of the nodes.
  - This GUI has style setted to FRAME_FLOAT_ON_PARENT, to make this GUI
    stay always on top of invesalius GUI.
- Added pubsub event "Update window level text" to change WW&WL text

Added (but not integrated) the clut_imagedata widget

Remove unused imports from clut_imagedata

The ClutImagedata is changing the slice lookuptable

Showing the histogram at the clut widget

Using checkbox instead of radiobutton to color menu

Applying ww & wl changes to the color table generated by clut_imagedata

Window&level affects clut colors

Window&level affects clut colors

Removed non-color presets from color preset dir

Hidding clutimagedatadialog when user selects another color preset

Removed idiot print

Keeping nodes after closing clutimagedata

Not passing ww&wl to clutimagedata anymore

Generating EVT_CLUT_POINT_MOVE when the clutimagedata is shown

Since EVT_SHOW binded to a wx.Panel is not working with 2.8, I'm binding
the OnShow method to EVT_IDLE and unbinding after the first time this
event is generated.

Using keyboard left and right to change nodes

Enter give the user option to change node colour

(shift +) Tab selects the next (previous) node

Improvements on the drawing of clut_imagedata

ClutImagedataDialog is dealing with clut_imagedata events

Added properties in clut_imagedata to get the ww&wl

Added a pubsub method to update the ww&wl text

WW&WL interactor style affects clut_imagedata widget

Style: the position of window_level and window_width

Delete key deletes a node

Cleaning and closing clut_imagedata dialog when a project is closed

Refreshing clut_imagedata after user change node color

Clut_imagedata dialog setted style to STAY_ON_TOP

Change clut_imagedata dialog style from wx.STAY_ON_TOP to wx.FRAME_FLOAT_ON_PARENT

Cleaning code related to clut_imagedata dialog in viewer_slice

Cleaning code related to clut_imagedata dialog in viewer_slice

EVT_CLUT_POINT_MOVE renamed to EVT_CLUT_NODE_CHANGED

Generating event when user selects custom color scheme

Removed OnShow method from clut_imagedata

Remove useless prints

Reseting things when closing a project
  • Loading branch information
tfmoraes committed May 23, 2013
1 parent 41d183f commit 3289c90
Show file tree
Hide file tree
Showing 12 changed files with 789 additions and 19,597 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ invesalius/gui/widgets/*.log
invesalius/gui/widgets/*.pyc
invesalius/reader/*.log
invesalius/reader/*.pyc

*.pyc
*.swp
*.so
tags
*.c
2 changes: 1 addition & 1 deletion invesalius/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@
REDUCE_IMAGEDATA_QUALITY = 0

ICON_DIR = os.path.abspath(os.path.join('..', 'icons'))
SAMPLE_DIR = os.path.abspath(os.path.join('..', 'samples'))
SAMPLE_DIR = "/usr/share/doc/invesalius-examples/examples/"
DOC_DIR = os.path.abspath(os.path.join('..', 'docs'))


Expand Down
171 changes: 145 additions & 26 deletions invesalius/data/slice_.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
from mask import Mask
from project import Project

OTHER=0
PLIST=1
WIDGET=2


class SliceBuffer(object):
"""
This class is used as buffer that mantains the vtkImageData and numpy array
Expand Down Expand Up @@ -66,9 +71,6 @@ def discard_buffer(self):
self.vtk_mask = None





class Slice(object):
__metaclass__= utils.Singleton
# Only one slice will be initialized per time (despite several viewers
Expand All @@ -79,7 +81,8 @@ def __init__(self):
self.imagedata = None
self.current_mask = None
self.blend_filter = None
self.matrix = None
self.histogram = None
self._matrix = None
self.spacing = (1.0, 1.0, 1.0)

self.number_of_colours = 256
Expand All @@ -94,8 +97,23 @@ def __init__(self):
self.num_gradient = 0
self.interaction_style = st.StyleStateManager()

self.values = None
self.nodes = None

self.from_ = OTHER
self.__bind_events()

@property
def matrix(self):
return self._matrix

@matrix.setter
def matrix(self, value):
self._matrix = value
i, e = value.min(), value.max()
r = e - i
self.histogram = numpy.histogram(self._matrix, r, (i, e))[0]

def __bind_events(self):
# General slice control
Publisher.subscribe(self.CreateSurfaceFromIndex,
Expand Down Expand Up @@ -125,6 +143,12 @@ def __bind_events(self):
Publisher.subscribe(self.UpdateColourTableBackground,\
'Change colour table from background image')

Publisher.subscribe(self.UpdateColourTableBackgroundPlist,\
'Change colour table from background image from plist')

Publisher.subscribe(self.UpdateColourTableBackgroundWidget,\
'Change colour table from background image from widget')

Publisher.subscribe(self.InputImageWidget, 'Input Image in the widget')

Publisher.subscribe(self.OnExportMask,'Export mask to file')
Expand Down Expand Up @@ -213,7 +237,18 @@ def OnCloseProject(self, pubsub_evt):
def CloseProject(self):
self.imagedata = None
self.current_mask = None

self.values = None
self.nodes = None
self.from_= OTHER

self.number_of_colours = 256
self.saturation_range = (0, 0)
self.hue_range = (0, 0)
self.value_range = (0, 1)

Publisher.sendMessage('Select first item from slice menu')

#self.blend_filter = None
#self.blend_filter = None
#self.num_gradient = 0
Expand Down Expand Up @@ -702,6 +737,7 @@ def UpdateWindowLevelBackground(self, pubsub_evt):

def UpdateColourTableBackground(self, pubsub_evt):
values = pubsub_evt.data
self.from_= OTHER
self.number_of_colours= values[0]
self.saturation_range = values[1]
self.hue_range = values[2]
Expand All @@ -710,6 +746,29 @@ def UpdateColourTableBackground(self, pubsub_evt):
buffer_.discard_vtk_image()
Publisher.sendMessage('Reload actual slice')

def UpdateColourTableBackgroundPlist(self, pubsub_evt):
self.values = pubsub_evt.data
self.from_= PLIST
for buffer_ in self.buffer_slices.values():
buffer_.discard_vtk_image()

Publisher.sendMessage('Reload actual slice')

def UpdateColourTableBackgroundWidget(self, pubsub_evt):
self.nodes = pubsub_evt.data
self.from_= WIDGET
for buffer_ in self.buffer_slices.values():
buffer_.discard_vtk_image()

knodes = sorted(self.nodes)
p0 = knodes[0].value
pn = knodes[-1].value

self.window_width = pn - p0
self.window_level = (pn + p0) / 2

Publisher.sendMessage('Reload actual slice')

def InputImageWidget(self, pubsub_evt):
widget, orientation = pubsub_evt.data

Expand Down Expand Up @@ -816,15 +875,72 @@ def __load_masks(self, imagedata, mask_dict):
Publisher.sendMessage('Update slice viewer')

def do_ww_wl(self, image):
colorer = vtk.vtkImageMapToWindowLevelColors()
colorer.SetInput(image)
colorer.SetWindow(self.window_width)
colorer.SetLevel(self.window_level)
colorer.SetOutputFormatToRGB()
colorer.Update()
if self.from_ == PLIST:
lut = vtk.vtkWindowLevelLookupTable()
lut.SetWindow(self.window_width)
lut.SetLevel(self.window_level)
lut.Build()

i = 0
for r, g, b in self.values:
lut.SetTableValue(i, r/255.0, g/255.0, b/255.0, 1.0)
i += 1

colorer = vtk.vtkImageMapToColors()
colorer.SetInput(image)
colorer.SetLookupTable(lut)
colorer.SetOutputFormatToRGB()
colorer.Update()
elif self.from_ == WIDGET:
lut = vtk.vtkColorTransferFunction()

for n in self.nodes:
r, g, b = n.colour
lut.AddRGBPoint(n.value, r/255.0, g/255.0, b/255.0)

lut.Build()

colorer = vtk.vtkImageMapToColors()
colorer.SetLookupTable(lut)
colorer.SetInput(image)
colorer.SetOutputFormatToRGB()
colorer.Update()
else:
colorer = vtk.vtkImageMapToWindowLevelColors()
colorer.SetInput(image)
colorer.SetWindow(self.window_width)
colorer.SetLevel(self.window_level)
colorer.SetOutputFormatToRGB()
colorer.Update()

return colorer.GetOutput()

def _update_wwwl_widget_nodes(self, ww, wl):
if self.from_ == WIDGET:
knodes = sorted(self.nodes)

p1 = knodes[0]
p2 = knodes[-1]
half = (p2.value - p1.value) / 2.0
middle = p1.value + half

shiftWL = wl - middle
shiftWW = p1.value + shiftWL - (wl - 0.5 * ww)

factor = 1.0

for n, node in enumerate(knodes):
factor = abs(node.value - middle) / half
if factor < 0:
factor = 0

node.value += shiftWL

if n < len(self.nodes) / 2.0:
node.value -= shiftWW * factor
else:
node.value += shiftWW * factor

def do_threshold_to_a_slice(self, slice_matrix, mask):
"""
Based on the current threshold bounds generates a threshold mask to
Expand All @@ -837,22 +953,25 @@ def do_threshold_to_a_slice(self, slice_matrix, mask):
return m.astype('uint8')

def do_colour_image(self, imagedata):
# map scalar values into colors
lut_bg = vtk.vtkLookupTable()
lut_bg.SetTableRange(imagedata.GetScalarRange())
lut_bg.SetSaturationRange(self.saturation_range)
lut_bg.SetHueRange(self.hue_range)
lut_bg.SetValueRange(self.value_range)
lut_bg.Build()

# map the input image through a lookup table
img_colours_bg = vtk.vtkImageMapToColors()
img_colours_bg.SetOutputFormatToRGB()
img_colours_bg.SetLookupTable(lut_bg)
img_colours_bg.SetInput(imagedata)
img_colours_bg.Update()

return img_colours_bg.GetOutput()
if self.from_ in (PLIST, WIDGET):
return imagedata
else:
# map scalar values into colors
lut_bg = vtk.vtkLookupTable()
lut_bg.SetTableRange(imagedata.GetScalarRange())
lut_bg.SetSaturationRange(self.saturation_range)
lut_bg.SetHueRange(self.hue_range)
lut_bg.SetValueRange(self.value_range)
lut_bg.Build()

# map the input image through a lookup table
img_colours_bg = vtk.vtkImageMapToColors()
img_colours_bg.SetOutputFormatToRGB()
img_colours_bg.SetLookupTable(lut_bg)
img_colours_bg.SetInput(imagedata)
img_colours_bg.Update()

return img_colours_bg.GetOutput()

def do_colour_mask(self, imagedata):
scalar_range = int(imagedata.GetScalarRange()[1])
Expand Down
26 changes: 24 additions & 2 deletions invesalius/data/viewer_slice.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def OnContextMenu(self, evt):
self.right_pressed = 0
if (self.last_position_mouse_move ==\
self.interactor.GetLastEventPosition()):
self.menu.caller = self
self.PopupMenu(self.menu)
evt.Skip()

Expand Down Expand Up @@ -196,7 +197,25 @@ def UpdateWindowLevelValue(self, pubsub_evt):
window, level = pubsub_evt.data
self.acum_achange_window, self.acum_achange_level = (window, level)
self.SetWLText(window, level)

slc = sl.Slice()
slc._update_wwwl_widget_nodes(window, level)

Publisher.sendMessage('Update all slice')
Publisher.sendMessage('Update clut imagedata widget')

def UpdateWindowLevelText(self, pubsub_evt):
window, level = pubsub_evt.data
self.acum_achange_window, self.acum_achange_level = (window, level)
self.SetWLText(window, level)
self.interactor.Render()

def OnClutChange(self, evt):
Publisher.sendMessage('Change colour table from background image from widget',
evt.GetNodes())
slc = sl.Slice()
Publisher.sendMessage('Update window level value',
(slc.window_width, slc.window_level))

def SetWLText(self, window_width, window_level):
value = STR_WL%(window_level, window_width)
Expand Down Expand Up @@ -594,8 +613,11 @@ def __bind_events(self):
Publisher.subscribe(self.ChangeBrushOperation,
'Set edition operation')

Publisher.subscribe(self.UpdateWindowLevelValue,\
'Update window level value')
Publisher.subscribe(self.UpdateWindowLevelValue,
'Update window level value')

Publisher.subscribe(self.UpdateWindowLevelText,
'Update window level text')

#Publisher.subscribe(self._set_cross_visibility,\
# 'Set cross visibility')
Expand Down
43 changes: 42 additions & 1 deletion invesalius/gui/dialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
import session as ses
import utils

from gui.widgets.clut_imagedata import CLUTImageDataWidget, EVT_CLUT_NODE_CHANGED

import numpy as np

class MaskEvent(wx.PyCommandEvent):
def __init__(self , evtType, id, mask_index):
Expand Down Expand Up @@ -1274,9 +1277,47 @@ def ReloadMethodsOptions(self):
self.method_sizer.Layout()


class ClutImagedataDialog(wx.Dialog):
def __init__(self, histogram, init, end, nodes=None):
pre = wx.PreDialog()
pre.Create(wx.GetApp().GetTopWindow(), -1, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT)
self.PostCreate(pre)

self.histogram = histogram
self.init = init
self.end = end
self.nodes = nodes

self._init_gui()
self.bind_events()
self.bind_events_wx()


def _init_gui(self):
self.clut_widget = CLUTImageDataWidget(self, -1, self.histogram,
self.init, self.end, self.nodes)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.clut_widget, 1, wx.EXPAND)

self.SetSizer(sizer)
self.Fit()

def bind_events_wx(self):
self.clut_widget.Bind(EVT_CLUT_NODE_CHANGED, self.OnClutChange)

def bind_events(self):
Publisher.subscribe(self._refresh_widget, 'Update clut imagedata widget')

def OnClutChange(self, evt):
Publisher.sendMessage('Change colour table from background image from widget',
evt.GetNodes())
Publisher.sendMessage('Update window level text',
(self.clut_widget.window_width,
self.clut_widget.window_level))

def _refresh_widget(self, pubsub_evt):
self.clut_widget.Refresh()

def Show(self, gen_evt=True, show=True):
super(wx.Dialog, self).Show(show)
if gen_evt:
self.clut_widget._generate_event()
Loading

0 comments on commit 3289c90

Please sign in to comment.