class CameraImage(traits.HasTraits): #Traits view definitions: traits_view = traitsui.View(traitsui.Group( traitsui.HGroup(traitsui.Item('pixelsX', label="Pixels X"), traitsui.Item('pixelsY', label="Pixels Y"))), buttons=["OK", "Cancel"]) pixelsX = traits.CInt(768) pixelsY = traits.CInt(512) xs = traits.Array ys = traits.Array zs = traits.Array minZ = traits.Float maxZ = traits.Float scale = traits.Float(1.) offset = traits.Float(0.) ODCorrectionBool = traits.Bool( False, desc= "if true will correct the image to account for the maximum OD parameter" ) ODSaturationValue = traits.Float( 3.0, desc="the value of the saturated optical density") model_changed = traits.Event def __init__(self, *args, **kwargs): super(CameraImage, self).__init__(*args, **kwargs) def _xs_default(self): return scipy.linspace(0.0, self.pixelsX - 1, self.pixelsX) def _ys_default(self): return scipy.linspace(0.0, self.pixelsY - 1, self.pixelsY) def _zs_default(self): #return scipy.zeros((self.pixelsY, self.pixelsX)) return scipy.random.random_sample((self.pixelsY, self.pixelsX)) def _scale_changed(self): """update zs data when scale or offset changed """ logger.info("model scale changed") #self.getImageData(self.imageFile) self.zs = scipy.random.random_sample((self.pixelsY, self.pixelsX)) self.model_changed = True def _offset_changed(self): """update zs data when scale or offset changed """
class OdinEmbeddedMeta(Schema): """OdinEmbeddedMeta info that can be stored in a schema bundle.""" #: Subject code subject = traits.CBytes(desc='subject code', maxlen=16) #: Time of creation timestamp = traits.CFloat(desc='unix timestamp') #: Number of embedded channels num_channels = traits.CInt(desc='number of channels') #: Number of classifiers num_classifiers = traits.CInt(desc='number of classifiers')
class DataAxis(t.HasTraits): name = t.Str() units = t.Str() scale = t.Float() offset = t.Float() size = t.CInt() low_value = t.Float() high_value = t.Float() value = t.Range('low_value', 'high_value') low_index = t.Int(0) high_index = t.Int() slice = t.Instance(slice) navigate = t.Bool(t.Undefined) index = t.Range('low_index', 'high_index') axis = t.Array() continuous_value = t.Bool(False) def __init__(self, size, index_in_array=None, name=t.Undefined, scale=1., offset=0., units=t.Undefined, navigate=t.Undefined): super(DataAxis, self).__init__() self.name = name self.units = units self.scale = scale self.offset = offset self.size = size self.high_index = self.size - 1 self.low_index = 0 self.index = 0 self.update_axis() self.navigate = navigate self.axes_manager = None self.on_trait_change(self.update_axis, ['scale', 'offset', 'size']) self.on_trait_change(self.update_value, 'index') self.on_trait_change(self.set_index_from_value, 'value') self.on_trait_change(self._update_slice, 'navigate') self.on_trait_change(self.update_index_bounds, 'size') # The slice must be updated even if the default value did not # change to correctly set its value. self._update_slice(self.navigate) @property def index_in_array(self): if self.axes_manager is not None: return self.axes_manager._axes.index(self) else: raise AttributeError( "This DataAxis does not belong to an AxesManager" " and therefore its index_in_array attribute " " is not defined") @property def index_in_axes_manager(self): if self.axes_manager is not None: return self.axes_manager._get_axes_in_natural_order().\ index(self) else: raise AttributeError( "This DataAxis does not belong to an AxesManager" " and therefore its index_in_array attribute " " is not defined") def _get_positive_index(self, index): if index < 0: index = self.size + index if index < 0: raise IndexError("index out of bounds") return index def _get_index(self, value): if isfloat(value): return self.value2index(value) else: return value def _get_array_slices(self, slice_): """Returns a slice to slice the corresponding data axis without changing the offset and scale of the DataAxis. Parameters ---------- slice_ : {float, int, slice} Returns ------- my_slice : slice """ v2i = self.value2index if isinstance(slice_, slice): start = slice_.start stop = slice_.stop step = slice_.step else: if isfloat(slice_): start = v2i(slice_) else: start = self._get_positive_index(slice_) stop = start + 1 step = None if isfloat(step): step = int(round(step / self.scale)) if isfloat(start): try: start = v2i(start) except ValueError: # The value is below the axis limits # we slice from the start. start = None if isfloat(stop): try: stop = v2i(stop) except ValueError: # The value is above the axes limits # we slice up to the end. stop = None if step == 0: raise ValueError("slice step cannot be zero") return slice(start, stop, step) def _slice_me(self, slice_): """Returns a slice to slice the corresponding data axis and change the offset and scale of the DataAxis acordingly. Parameters ---------- slice_ : {float, int, slice} Returns ------- my_slice : slice """ i2v = self.index2value my_slice = self._get_array_slices(slice_) start, stop, step = my_slice.start, my_slice.stop, my_slice.step if start is None: if step > 0 or step is None: start = 0 else: start = self.size - 1 self.offset = i2v(start) if step is not None: self.scale *= step return my_slice def _get_name(self): if self.name is t.Undefined: if self.axes_manager is None: name = "Unnamed" else: name = "Unnamed " + ordinal(self.index_in_axes_manager) else: name = self.name return name def __repr__(self): text = '<%s axis, size: %i' % (self._get_name(), self.size,) if self.navigate is True: text += ", index: %i" % self.index text += ">" return text.encode('utf8') def __str__(self): return self._get_name() + " axis" def connect(self, f, trait='value'): self.on_trait_change(f, trait) def disconnect(self, f, trait='value'): self.on_trait_change(f, trait, remove=True) def update_index_bounds(self): self.high_index = self.size - 1 def update_axis(self): self.axis = generate_axis(self.offset, self.scale, self.size) if len(self.axis) != 0: self.low_value, self.high_value = ( self.axis.min(), self.axis.max()) def _update_slice(self, value): if value is False: self.slice = slice(None) else: self.slice = None def get_axis_dictionary(self): adict = { 'name': self.name, 'scale': self.scale, 'offset': self.offset, 'size': self.size, 'units': self.units, 'navigate': self.navigate } return adict def copy(self): return DataAxis(**self.get_axis_dictionary()) def __copy__(self): return self.copy() def __deepcopy__(self, memo): cp = self.copy() return cp def update_value(self): self.value = self.axis[self.index] def value2index(self, value, rounding=round): """Return the closest index to the given value if between the limit. Parameters ---------- value : number or numpy array Returns ------- index : integer or numpy array Raises ------ ValueError if any value is out of the axis limits. """ if value is None: return None if isinstance(value, np.ndarray): if rounding is round: rounding = np.round elif rounding is math.ceil: rounding = np.ceil elif rounding is math.floor: rounding = np.floor index = rounding((value - self.offset) / self.scale) if isinstance(value, np.ndarray): index = index.astype(int) if np.all(self.size > index) and np.all(index >= 0): return index else: raise ValueError("A value is out of the axis limits") else: index = int(index) if self.size > index >= 0: return index else: raise ValueError("The value is out of the axis limits") def index2value(self, index): if isinstance(index, np.ndarray): return self.axis[index.ravel()].reshape(index.shape) else: return self.axis[index] def set_index_from_value(self, value): self.index = self.value2index(value) # If the value is above the limits we must correct the value if self.continuous_value is False: self.value = self.index2value(self.index) def calibrate(self, value_tuple, index_tuple, modify_calibration=True): scale = (value_tuple[1] - value_tuple[0]) /\ (index_tuple[1] - index_tuple[0]) offset = value_tuple[0] - scale * index_tuple[0] if modify_calibration is True: self.offset = offset self.scale = scale else: return offset, scale def value_range_to_indices(self, v1, v2): """Convert the given range to index range. When an out of the axis limits, the endpoint is used instead. Parameters ---------- v1, v2 : float The end points of the interval in the axis units. v2 must be greater than v1. """ if v1 > v2: raise ValueError("v2 must be greater than v1.") if v1 is not None and v1 > self.low_value and v1 <= self.high_value: i1 = self.value2index(v1) else: i1 = 0 if v2 is not None and v2 < self.high_value and v2 >= self.low_value: i2 = self.value2index(v2) else: i2 = self.size - 1 return i1, i2
class DataAxis(t.HasTraits): name = t.Str() units = t.Str() scale = t.Float() offset = t.Float() size = t.CInt() low_value = t.Float() high_value = t.Float() value = t.Range('low_value', 'high_value') low_index = t.Int(0) high_index = t.Int() slice = t.Instance(slice) navigate = t.Bool(t.Undefined) index = t.Range('low_index', 'high_index') axis = t.Array() continuous_value = t.Bool(False) def __init__(self, size, index_in_array=None, name=t.Undefined, scale=1., offset=0., units=t.Undefined, navigate=t.Undefined): super(DataAxis, self).__init__() self.events = Events() self.events.index_changed = Event(""" Event that triggers when the index of the `DataAxis` changes Triggers after the internal state of the `DataAxis` has been updated. Arguments: --------- obj : The DataAxis that the event belongs to. index : The new index """, arguments=["obj", 'index']) self.events.value_changed = Event(""" Event that triggers when the value of the `DataAxis` changes Triggers after the internal state of the `DataAxis` has been updated. Arguments: --------- obj : The DataAxis that the event belongs to. value : The new value """, arguments=["obj", 'value']) self._suppress_value_changed_trigger = False self._suppress_update_value = False self.name = name self.units = units self.scale = scale self.offset = offset self.size = size self.high_index = self.size - 1 self.low_index = 0 self.index = 0 self.update_axis() self.navigate = navigate self.axes_manager = None self.on_trait_change(self.update_axis, ['scale', 'offset', 'size']) self.on_trait_change(self._update_slice, 'navigate') self.on_trait_change(self.update_index_bounds, 'size') # The slice must be updated even if the default value did not # change to correctly set its value. self._update_slice(self.navigate) def _index_changed(self, name, old, new): self.events.index_changed.trigger(obj=self, index=self.index) if not self._suppress_update_value: new_value = self.axis[self.index] if new_value != self.value: self.value = new_value def _value_changed(self, name, old, new): old_index = self.index new_index = self.value2index(new) if self.continuous_value is False: # Only values in the grid alowed if old_index != new_index: self.index = new_index if new == self.axis[self.index]: self.events.value_changed.trigger(obj=self, value=new) elif old_index == new_index: new_value = self.index2value(new_index) if new_value == old: self._suppress_value_changed_trigger = True try: self.value = new_value finally: self._suppress_value_changed_trigger = False elif new_value == new and not\ self._suppress_value_changed_trigger: self.events.value_changed.trigger(obj=self, value=new) else: # Intergrid values are alowed. This feature is deprecated self.events.value_changed.trigger(obj=self, value=new) if old_index != new_index: self._suppress_update_value = True self.index = new_index self._suppress_update_value = False @property def index_in_array(self): if self.axes_manager is not None: return self.axes_manager._axes.index(self) else: raise AttributeError( "This DataAxis does not belong to an AxesManager" " and therefore its index_in_array attribute " " is not defined") @property def index_in_axes_manager(self): if self.axes_manager is not None: return self.axes_manager._get_axes_in_natural_order().\ index(self) else: raise AttributeError( "This DataAxis does not belong to an AxesManager" " and therefore its index_in_array attribute " " is not defined") def _get_positive_index(self, index): if index < 0: index = self.size + index if index < 0: raise IndexError("index out of bounds") return index def _get_index(self, value): if isfloat(value): return self.value2index(value) else: return value def _get_array_slices(self, slice_): """Returns a slice to slice the corresponding data axis without changing the offset and scale of the DataAxis. Parameters ---------- slice_ : {float, int, slice} Returns ------- my_slice : slice """ v2i = self.value2index if isinstance(slice_, slice): start = slice_.start stop = slice_.stop step = slice_.step else: if isfloat(slice_): start = v2i(slice_) else: start = self._get_positive_index(slice_) stop = start + 1 step = None if isfloat(step): step = int(round(step / self.scale)) if isfloat(start): try: start = v2i(start) except ValueError: if start > self.high_value: # The start value is above the axis limit raise IndexError( "Start value above axis high bound for axis %s." "value: %f high_bound: %f" % (repr(self), start, self.high_value)) else: # The start value is below the axis limit, # we slice from the start. start = None if isfloat(stop): try: stop = v2i(stop) except ValueError: if stop < self.low_value: # The stop value is below the axis limits raise IndexError( "Stop value below axis low bound for axis %s." "value: %f low_bound: %f" % (repr(self), stop, self.low_value)) else: # The stop value is below the axis limit, # we slice until the end. stop = None if step == 0: raise ValueError("slice step cannot be zero") return slice(start, stop, step) def _slice_me(self, slice_): """Returns a slice to slice the corresponding data axis and change the offset and scale of the DataAxis acordingly. Parameters ---------- slice_ : {float, int, slice} Returns ------- my_slice : slice """ i2v = self.index2value my_slice = self._get_array_slices(slice_) start, stop, step = my_slice.start, my_slice.stop, my_slice.step if start is None: if step is None or step > 0: start = 0 else: start = self.size - 1 self.offset = i2v(start) if step is not None: self.scale *= step return my_slice def _get_name(self): if self.name is t.Undefined: if self.axes_manager is None: name = "Unnamed" else: name = "Unnamed " + ordinal(self.index_in_axes_manager) else: name = self.name return name def __repr__(self): text = '<%s axis, size: %i' % ( self._get_name(), self.size, ) if self.navigate is True: text += ", index: %i" % self.index text += ">" return text def __str__(self): return self._get_name() + " axis" def update_index_bounds(self): self.high_index = self.size - 1 def update_axis(self): self.axis = generate_axis(self.offset, self.scale, self.size) if len(self.axis) != 0: self.low_value, self.high_value = (self.axis.min(), self.axis.max()) def _update_slice(self, value): if value is False: self.slice = slice(None) else: self.slice = None def get_axis_dictionary(self): adict = { 'name': self.name, 'scale': self.scale, 'offset': self.offset, 'size': self.size, 'units': self.units, 'navigate': self.navigate } return adict def copy(self): return DataAxis(**self.get_axis_dictionary()) def __copy__(self): return self.copy() def __deepcopy__(self, memo): cp = self.copy() return cp def value2index(self, value, rounding=round): """Return the closest index to the given value if between the limit. Parameters ---------- value : number or numpy array Returns ------- index : integer or numpy array Raises ------ ValueError if any value is out of the axis limits. """ if value is None: return None if isinstance(value, np.ndarray): if rounding is round: rounding = np.round elif rounding is math.ceil: rounding = np.ceil elif rounding is math.floor: rounding = np.floor index = rounding((value - self.offset) / self.scale) if isinstance(value, np.ndarray): index = index.astype(int) if np.all(self.size > index) and np.all(index >= 0): return index else: raise ValueError("A value is out of the axis limits") else: index = int(index) if self.size > index >= 0: return index else: raise ValueError("The value is out of the axis limits") def index2value(self, index): if isinstance(index, np.ndarray): return self.axis[index.ravel()].reshape(index.shape) else: return self.axis[index] def calibrate(self, value_tuple, index_tuple, modify_calibration=True): scale = (value_tuple[1] - value_tuple[0]) /\ (index_tuple[1] - index_tuple[0]) offset = value_tuple[0] - scale * index_tuple[0] if modify_calibration is True: self.offset = offset self.scale = scale else: return offset, scale def value_range_to_indices(self, v1, v2): """Convert the given range to index range. When an out of the axis limits, the endpoint is used instead. Parameters ---------- v1, v2 : float The end points of the interval in the axis units. v2 must be greater than v1. """ if v1 is not None and v2 is not None and v1 > v2: raise ValueError("v2 must be greater than v1.") if v1 is not None and self.low_value < v1 <= self.high_value: i1 = self.value2index(v1) else: i1 = 0 if v2 is not None and self.high_value > v2 >= self.low_value: i2 = self.value2index(v2) else: i2 = self.size - 1 return i1, i2 def update_from(self, axis, attributes=["scale", "offset", "units"]): """Copy values of specified axes fields from the passed AxesManager. Parameters ---------- axis : DataAxis The DataAxis instance to use as a source for values. attributes : iterable container of strings. The name of the attribute to update. If the attribute does not exist in either of the AxesManagers, an AttributeError will be raised. Returns ------- A boolean indicating whether any changes were made. """ any_changes = False changed = {} for f in attributes: if getattr(self, f) != getattr(axis, f): changed[f] = getattr(axis, f) if len(changed) > 0: self.trait_set(**changed) any_changes = True return any_changes
class ImagePlotInspector(traits.HasTraits): pixelsX = traits.CInt(768) pixelsY = traits.CInt(512) xs = traits.Array ys = traits.Array zs = traits.Array minZ = traits.Float maxZ = traits.Float drawContourBool = traits.Bool(False) test = traits.Button("test") contourXS = None contourYS = None contourZS = None model_changed = traits.Event def _xs_default(self): return scipy.linspace(0.0, self.pixelsX - 1, self.pixelsX) def _ys_default(self): return scipy.linspace(0.0, self.pixelsY - 1, self.pixelsY) def _zs_default(self): return scipy.zeros((self.pixelsY, self.pixelsX)) def _minZ_default(self): return 0.0 def _maxZ_default(self): return 1.0 def setData(self, zs): self.zs = zs self.minZ = zs.min() self.maxZ = zs.max() self.update() settingsGroup = traitsui.VGroup( traitsui.VGroup(traitsui.HGroup( traitsui.Item('pixelsX', label="Pixels X"), traitsui.Item('pixelsY', label="Pixels Y")), traitsui.HGroup( traitsui.Item('contourLevels', label="Contour Levels"), traitsui.Item('colormap', label="Colour Map"), "test"), traitsui.HGroup( traitsui.Item('fixAspectRatioBool', label="Fix Plot Aspect Ratio?")), label="settings", show_border=True), traitsui.VGroup(traitsui.HGroup('autoRangeColor', 'colorMapRangeLow', 'colorMapRangeHigh'), traitsui.HGroup('horizontalAutoRange', 'horizontalLowerLimit', 'horizontalUpperLimit'), traitsui.HGroup('verticalAutoRange', 'verticalLowerLimit', 'verticalUpperLimit'), label="axis limits", show_border=True)) plotGroup = traitsui.Group( traitsui.Item('container', editor=ComponentEditor(size=(800, 600)), show_label=False)) traits_view = traitsui.View(settingsGroup, plotGroup, buttons=traitsmenu.NoButtons, handler=ImagePlotInspectorHandler, title="ImagePlotInspectorHandler", resizable=True) contourLevels = traits.Int(15) colormap = traits.Enum(colormaps.color_map_name_dict.keys()) autoRangeColor = traits.Bool(True) colorMapRangeLow = traits.Float colorMapRangeHigh = traits.Float horizontalAutoRange = traits.Bool(True) horizontalLowerLimit = traits.Float horizontalUpperLimit = traits.Float verticalAutoRange = traits.Bool(True) verticalLowerLimit = traits.Float verticalUpperLimit = traits.Float fixAspectRatioBool = traits.Bool(False) #--------------------------------------------------------------------------- # Private Traits #--------------------------------------------------------------------------- _image_index = traits.Instance(chaco.GridDataSource) _image_value = traits.Instance(chaco.ImageData) _cmap = traits.Trait(colormaps.jet, traits.Callable) #--------------------------------------------------------------------------- # Public View interface #--------------------------------------------------------------------------- def __init__(self, *args, **kwargs): super(ImagePlotInspector, self).__init__(*args, **kwargs) self.create_plot() self.update() logger.info("initialisation of Image inspector plot") def create_plot(self): # Create the mapper, etc self._image_index = chaco.GridDataSource(scipy.array([]), scipy.array([]), sort_order=("ascending", "ascending")) image_index_range = chaco.DataRange2D(self._image_index) self._image_index.on_trait_change(self._metadata_changed, "metadata_changed") self._image_value = chaco.ImageData(data=scipy.array([]), value_depth=1) image_value_range = chaco.DataRange1D(self._image_value) self.polyplot = chaco.CMapImagePlot( index=self._image_index, value=self._image_value, index_mapper=chaco.GridMapper(range=image_index_range), color_mapper=self._cmap(image_value_range)) # Add a left axis to the plot left = chaco.PlotAxis(orientation='left', title="y", mapper=self.polyplot.index_mapper._ymapper, component=self.polyplot) self.polyplot.overlays.append(left) # Add a bottom axis to the plot bottom = chaco.PlotAxis(orientation='bottom', title="x", mapper=self.polyplot.index_mapper._xmapper, component=self.polyplot) self.polyplot.overlays.append(bottom) # Add some tools to the plot self.polyplot.tools.append( tools.PanTool(self.polyplot, constrain_key="shift", drag_button="middle")) self.polyplot.overlays.append( tools.ZoomTool(component=self.polyplot, tool_mode="box", always_on=False)) self.lineInspectorX = clickableLineInspector.ClickableLineInspector( component=self.polyplot, axis='index_x', inspect_mode="indexed", write_metadata=True, is_listener=False, color="white") self.lineInspectorY = clickableLineInspector.ClickableLineInspector( component=self.polyplot, axis='index_y', inspect_mode="indexed", write_metadata=True, color="white", is_listener=False) self.polyplot.overlays.append(self.lineInspectorX) self.polyplot.overlays.append(self.lineInspectorY) self.boxSelection2D = boxSelection2D.BoxSelection2D( component=self.polyplot) self.polyplot.overlays.append(self.boxSelection2D) # Add these two plots to one container self.centralContainer = chaco.OverlayPlotContainer(padding=0, use_backbuffer=True, unified_draw=True) self.centralContainer.add(self.polyplot) # Create a colorbar cbar_index_mapper = chaco.LinearMapper(range=image_value_range) self.colorbar = chaco.ColorBar( index_mapper=cbar_index_mapper, plot=self.polyplot, padding_top=self.polyplot.padding_top, padding_bottom=self.polyplot.padding_bottom, padding_right=40, resizable='v', width=30) self.plotData = chaco.ArrayPlotData( line_indexHorizontal=scipy.array([]), line_valueHorizontal=scipy.array([]), scatter_indexHorizontal=scipy.array([]), scatter_valueHorizontal=scipy.array([]), scatter_colorHorizontal=scipy.array([]), fitLine_indexHorizontal=scipy.array([]), fitLine_valueHorizontal=scipy.array([])) self.crossPlotHorizontal = chaco.Plot(self.plotData, resizable="h") self.crossPlotHorizontal.height = 100 self.crossPlotHorizontal.padding = 20 self.crossPlotHorizontal.plot( ("line_indexHorizontal", "line_valueHorizontal"), line_style="dot") self.crossPlotHorizontal.plot( ("scatter_indexHorizontal", "scatter_valueHorizontal", "scatter_colorHorizontal"), type="cmap_scatter", name="dot", color_mapper=self._cmap(image_value_range), marker="circle", marker_size=4) self.crossPlotHorizontal.index_range = self.polyplot.index_range.x_range self.plotData.set_data("line_indexVertical", scipy.array([])) self.plotData.set_data("line_valueVertical", scipy.array([])) self.plotData.set_data("scatter_indexVertical", scipy.array([])) self.plotData.set_data("scatter_valueVertical", scipy.array([])) self.plotData.set_data("scatter_colorVertical", scipy.array([])) self.plotData.set_data("fitLine_indexVertical", scipy.array([])) self.plotData.set_data("fitLine_valueVertical", scipy.array([])) self.crossPlotVertical = chaco.Plot(self.plotData, width=140, orientation="v", resizable="v", padding=20, padding_bottom=160) self.crossPlotVertical.plot( ("line_indexVertical", "line_valueVertical"), line_style="dot") self.crossPlotVertical.plot( ("scatter_indexVertical", "scatter_valueVertical", "scatter_colorVertical"), type="cmap_scatter", name="dot", color_mapper=self._cmap(image_value_range), marker="circle", marker_size=4) self.crossPlotVertical.index_range = self.polyplot.index_range.y_range # Create a container and add components self.container = chaco.HPlotContainer(padding=40, fill_padding=True, bgcolor="white", use_backbuffer=False) inner_cont = chaco.VPlotContainer(padding=40, use_backbuffer=True) inner_cont.add(self.crossPlotHorizontal) inner_cont.add(self.centralContainer) self.container.add(self.colorbar) self.container.add(inner_cont) self.container.add(self.crossPlotVertical) def initialiseContourPlot(self): """called if this is the first Fit Plot to be drawn """ xstep = 0.5 ystep = 0.5 self.contourXS = scipy.linspace(xstep / 2, self.pixelsX - xstep / 2, self.pixelsX - 1) self.contourYS = scipy.linspace(ystep / 2, self.pixelsY - ystep / 2, self.pixelsY - 1) self.countourZS = chaco.ImageData(data=scipy.array([]), value_depth=1) self.lineplot = chaco.ContourLinePlot( index=self._image_index, value=self.countourZS, index_mapper=chaco.GridMapper( range=self.polyplot.index_mapper.range), levels=self.contourLevels) self.centralContainer.add(self.lineplot) self.plotData.set_data("fitLine_indexHorizontal", self.xs) self.plotData.set_data("fitLine_indexVertical", self.ys) self.crossPlotVertical.plot( ("fitLine_indexVertical", "fitLine_valueVertical"), type="line", name="fitVertical") self.crossPlotHorizontal.plot( ("fitLine_indexHorizontal", "fitLine_valueHorizontal"), type="line", name="fitHorizontal") logger.debug("initialise fit plot %s " % self.crossPlotVertical.plots) def addContourPlot(self, contourZS): """add a contour plot on top using fitted data and add additional plots to sidebars (TODO) """ logger.debug("adding fit plot with fit") if not self.drawContourBool: logger.info("first fit plot so initialising contour plot") self.initialiseFitPlot() logger.info("attempting to set fit data") self.countourZS.data = contourZS self.container.invalidate_draw() self.container.request_redraw() self.drawContourBool = True def update(self): logger.info("updating plot") logger.info("zs = %s", self.zs) if self.autoRangeColor: self.colorbar.index_mapper.range.low = self.minZ self.colorbar.index_mapper.range.high = self.maxZ self._image_index.set_data(self.xs, self.ys) self._image_value.data = self.zs self.plotData.set_data("line_indexHorizontal", self.xs) self.plotData.set_data("line_indexVertical", self.ys) if self.drawContourBool and self.contourZS is not None: self.plotData.set_data("fitLine_indexHorizontal", self.contourXS) self.plotData.set_data("fitLine_indexVertical", self.contourYS) self.updatePlotLimits() #self._image_index.metadata_changed=True self.container.invalidate_draw() self.container.request_redraw() #--------------------------------------------------------------------------- # Event handlers #--------------------------------------------------------------------------- def _metadata_changed(self, old, new): """ This function takes out a cross section from the image data, based on the line inspector selections, and updates the line and scatter plots.""" if self.horizontalAutoRange: self.crossPlotHorizontal.value_range.low = self.minZ self.crossPlotHorizontal.value_range.high = self.maxZ if self.verticalAutoRange: self.crossPlotVertical.value_range.low = self.minZ self.crossPlotVertical.value_range.high = self.maxZ if self._image_index.metadata.has_key("selections"): x_ndx, y_ndx = self._image_index.metadata["selections"] if y_ndx and x_ndx: self.plotData.set_data("line_valueHorizontal", self._image_value.data[y_ndx, :]) self.plotData.set_data("line_valueVertical", self._image_value.data[:, x_ndx]) xdata, ydata = self._image_index.get_data() xdata, ydata = xdata.get_data(), ydata.get_data() self.plotData.set_data("scatter_indexHorizontal", scipy.array([xdata[x_ndx]])) self.plotData.set_data("scatter_indexVertical", scipy.array([ydata[y_ndx]])) self.plotData.set_data( "scatter_valueHorizontal", scipy.array([self._image_value.data[y_ndx, x_ndx]])) self.plotData.set_data( "scatter_valueVertical", scipy.array([self._image_value.data[y_ndx, x_ndx]])) self.plotData.set_data( "scatter_colorHorizontal", scipy.array([self._image_value.data[y_ndx, x_ndx]])) self.plotData.set_data( "scatter_colorVertical", scipy.array([self._image_value.data[y_ndx, x_ndx]])) if self.drawContourBool: self.plotData.set_data("fitLine_valueHorizontal", self.countourZS.data[y_ndx, :]) self.plotData.set_data("fitLine_valueVertical", self.countourZS.data[:, x_ndx]) else: self.plotData.set_data("scatter_valueHorizontal", scipy.array([])) self.plotData.set_data("scatter_valueVertical", scipy.array([])) self.plotData.set_data("line_valueHorizontal", scipy.array([])) self.plotData.set_data("line_valueVertical", scipy.array([])) self.plotData.set_data("fitLine_valueHorizontal", scipy.array([])) self.plotData.set_data("fitLine_valueVertical", scipy.array([])) def _colormap_changed(self): self._cmap = colormaps.color_map_name_dict[self.colormap] if hasattr(self, "polyplot"): value_range = self.polyplot.color_mapper.range self.polyplot.color_mapper = self._cmap(value_range) value_range = self.crossPlotHorizontal.color_mapper.range self.crossPlotHorizontal.color_mapper = self._cmap(value_range) # FIXME: change when we decide how best to update plots using # the shared colormap in plot object self.crossPlotHorizontal.plots["dot"][0].color_mapper = self._cmap( value_range) self.crossPlotVertical.plots["dot"][0].color_mapper = self._cmap( value_range) self.container.request_redraw() def _colorMapRangeLow_changed(self): self.colorbar.index_mapper.range.low = self.colorMapRangeLow def _colorMapRangeHigh_changed(self): self.colorbar.index_mapper.range.high = self.colorMapRangeHigh def _horizontalLowerLimit_changed(self): self.crossPlotHorizontal.value_range.low = self.horizontalLowerLimit def _horizontalUpperLimit_changed(self): self.crossPlotHorizontal.value_range.high = self.horizontalUpperLimit def _verticalLowerLimit_changed(self): self.crossPlotVertical.value_range.low = self.verticalLowerLimit def _verticalUpperLimit_changed(self): self.crossPlotVertical.value_range.high = self.verticalUpperLimit def _autoRange_changed(self): if self.autoRange: self.colorbar.index_mapper.range.low = self.minz self.colorbar.index_mapper.range.high = self.maxz def _num_levels_changed(self): if self.num_levels > 3: self.polyplot.levels = self.num_levels self.lineplot.levels = self.num_levels def _colorMapRangeLow_default(self): logger.debug("setting color map rangle low default") return self.minZ def _colorMapRangeHigh_default(self): return self.maxZ def _horizontalLowerLimit_default(self): return self.minZ def _horizontalUpperLimit_default(self): return self.maxZ def _verticalLowerLimit_default(self): return self.minZ def _verticalUpperLimit_default(self): return self.maxZ def _selectedFit_changed(self, selected): logger.debug("selected fit was changed") def _fixAspectRatioBool_changed(self): if self.fixAspectRatioBool: self.centralContainer.aspect_ratio = float(self.pixelsX) / float( self.pixelsY) else: self.centralContainer.aspect_ratio = None self.container.request_redraw() self.centralContainer.request_redraw() def updatePlotLimits(self): """just updates the values in the GUI """ if self.autoRangeColor: self.colorMapRangeLow = self.minZ self.colorMapRangeHigh = self.maxZ if self.horizontalAutoRange: self.horizontalLowerLimit = self.minZ self.horizontalUpperLimit = self.maxZ if self.verticalAutoRange: self.verticalLowerLimit = self.minZ self.verticalUpperLimit = self.maxZ def _zs_changed(self): logger.debug("zs changed") self.update() def _random_zs(self, low=0.0, high=1.0): """generates random zs. Useful for testing """ self.setData( scipy.random.uniform(low, high, (self.pixelsY, self.pixelsX))) def _test_fired(self): self._random_zs()
class CameraImage(traits.HasTraits): #Traits view definitions: traits_view = traitsui.View(traitsui.Group( traitsui.HGroup(traitsui.Item('pixelsX', label="Pixels X"), traitsui.Item('pixelsY', label="Pixels Y"))), buttons=["OK", "Cancel"]) pixelsX = traits.CInt(768) pixelsY = traits.CInt(512) xs = traits.Array ys = traits.Array zs = traits.Array minZ = traits.Float maxZ = traits.Float scale = traits.Float(10089.33) offset = traits.Float(5000.) ODCorrectionBool = traits.Bool( False, desc= "if true will correct the image to account for the maximum OD parameter" ) ODSaturationValue = traits.Float( 3.0, desc="the value of the saturated optical density") model_changed = traits.Event fitList = traits.List(fits.Fit) #list of possible fits def __init__(self, *args, **kwargs): super(CameraImage, self).__init__(*args, **kwargs) self.fitList = [ fits.GaussianFit(endX=self.pixelsX, endY=self.pixelsY), fits.RotatedGaussianFit(endX=self.pixelsX, endY=self.pixelsY), fits.ParabolaFit(endX=self.pixelsX, endY=self.pixelsY), fits.GaussianAndParabolaFit(endX=self.pixelsX, endY=self.pixelsY), fits.FermiGasFit(endX=self.pixelsX, endY=self.pixelsY), fits.ClippedExponentialIntegral(endX=self.pixelsX, endY=self.pixelsY) ] def _xs_default(self): return scipy.linspace(0.0, self.pixelsX - 1, self.pixelsX) def _ys_default(self): return scipy.linspace(0.0, self.pixelsY - 1, self.pixelsY) def _zs_default(self): return scipy.zeros((self.pixelsY, self.pixelsX)) def getImageData(self, imageFile): logger.debug("pulling image data") # The xs and ys used for the image plot range need to be the # edges of the cells. self.imageFile = imageFile self.xs = scipy.linspace(0.0, self.pixelsX - 1, self.pixelsX) self.ys = scipy.linspace(0.0, self.pixelsY - 1, self.pixelsY) if not os.path.exists(imageFile): #if no file is define the image is flat 0s of camera size logger.error("image file not found. filling with zeros") self.zs = scipy.zeros((self.pixelsX, self.pixelsY)) print self.zs self.minZ = 0.0 self.maxZ = 1.0 self.model_changed = True else: try: self.rawImage = scipy.misc.imread(imageFile) self.zs = (self.rawImage - self.offset) / self.scale if self.ODCorrectionBool: logger.info("Correcting for OD saturation") self.zs = scipy.log( (1.0 - scipy.exp(-self.ODSaturationValue)) / (scipy.exp(-self.zs) - scipy.exp(-self.ODSaturationValue))) #we should account for the fact if ODSaturation value is wrong or there is noise we can get complex numbers! self.zs[scipy.imag(self.zs) > 0] = scipy.nan self.zs = self.zs.astype(float) self.minZ = scipy.nanmin(self.zs) self.maxZ = scipy.nanmax(self.zs) self.model_changed = True for fit in self.fitList: fit.xs = self.xs fit.ys = self.ys fit.zs = self.zs except Exception as e: logger.error("error in setting data %s" % e.message) logger.debug( "Sometimes we get an error unsupported operand type(s) for -: 'instance' and 'float'. " ) logger.debug( "checking for what could cause this . Tell Tim if you see this error message!!!!" ) logger.debug("type(self.rawImage) -> %s" % type(self.rawImage)) logger.debug("type(self.offset) -> %s" % type(self.offset)) def _scale_changed(self): """update zs data when scale or offset changed """ logger.info("model scale changed") self.getImageData(self.imageFile) def _offset_changed(self): """update zs data when scale or offset changed """ self.getImageData(self.imageFile)
class DataAxis(t.HasTraits): name = t.Str() units = t.Str() scale = t.Float() offset = t.Float() size = t.CInt() low_value = t.Float() high_value = t.Float() value = t.Range('low_value', 'high_value') low_index = t.Int(0) high_index = t.Int() slice = t.Instance(slice) navigate = t.Bool(t.Undefined) index = t.Range('low_index', 'high_index') axis = t.Array() continuous_value = t.Bool(False) def __init__(self, size, index_in_array=None, name=t.Undefined, scale=1., offset=0., units=t.Undefined, navigate=t.Undefined): super(DataAxis, self).__init__() self.name = name self.units = units self.scale = scale self.offset = offset self.size = size self.high_index = self.size - 1 self.low_index = 0 self.index = 0 self.update_axis() self.navigate = navigate self.axes_manager = None self.on_trait_change(self.update_axis, ['scale', 'offset', 'size']) self.on_trait_change(self.update_value, 'index') self.on_trait_change(self.set_index_from_value, 'value') self.on_trait_change(self._update_slice, 'navigate') self.on_trait_change(self.update_index_bounds, 'size') # The slice must be updated even if the default value did not # change to correctly set its value. self._update_slice(self.navigate) @property def index_in_array(self): if self.axes_manager is not None: return self.axes_manager._axes.index(self) else: raise AttributeError( "This DataAxis does not belong to an AxesManager" " and therefore its index_in_array attribute " " is not defined") @property def index_in_axes_manager(self): if self.axes_manager is not None: return self.axes_manager._get_axes_in_natural_order().\ index(self) else: raise AttributeError( "This DataAxis does not belong to an AxesManager" " and therefore its index_in_array attribute " " is not defined") def _get_positive_index(self, index): if index < 0: index = self.size + index if index < 0: raise IndexError("index out of bounds") return index def _get_index(self, value): if isinstance(value, float): return self.value2index(value) else: return value def _slice_me(self, slice_): """Returns a slice to slice the corresponding data axis and change the offset and scale of the DataAxis acordingly. Parameters ---------- slice_ : {float, int, slice} Returns ------- my_slice : slice """ i2v = self.index2value v2i = self.value2index if isinstance(slice_, slice): start = slice_.start stop = slice_.stop step = slice_.step else: if isinstance(slice_, float): start = v2i(slice_) else: start = self._get_positive_index(slice_) stop = start + 1 step = None if isinstance(step, float): step = int(round(step / self.scale)) if isinstance(start, float): try: start = v2i(start) except ValueError: # The value is below the axis limits # we slice from the start. start = None if isinstance(stop, float): try: stop = v2i(stop) except ValueError: # The value is above the axes limits # we slice up to the end. stop = None if step == 0: raise ValueError("slice step cannot be zero") my_slice = slice(start, stop, step) if start is None: if step > 0 or step is None: start = 0 else: start = self.size - 1 self.offset = i2v(start) if step is not None: self.scale *= step return my_slice def _get_name(self): name = (self.name if self.name is not t.Undefined else ("Unnamed " + ordinal(self.index_in_axes_manager))) return name def __repr__(self): text = '<%s axis, size: %i' % ( self._get_name(), self.size, ) if self.navigate is True: text += ", index: %i" % self.index text += ">" return text def __str__(self): return self._get_name() + " axis" def connect(self, f, trait='value'): self.on_trait_change(f, trait) def disconnect(self, f, trait='value'): self.on_trait_change(f, trait, remove=True) def update_index_bounds(self): self.high_index = self.size - 1 def update_axis(self): self.axis = generate_axis(self.offset, self.scale, self.size) if len(self.axis) != 0: self.low_value, self.high_value = (self.axis.min(), self.axis.max()) def _update_slice(self, value): if value is False: self.slice = slice(None) else: self.slice = None def get_axis_dictionary(self): adict = { 'name': self.name, 'scale': self.scale, 'offset': self.offset, 'size': self.size, 'units': self.units, 'index_in_array': self.index_in_array, 'navigate': self.navigate } return adict def copy(self): return DataAxis(**self.get_axis_dictionary()) def update_value(self): self.value = self.axis[self.index] def value2index(self, value, rounding=round): """Return the closest index to the given value if between the limit. Parameters ---------- value : float Returns ------- int Raises ------ ValueError if value is out of the axis limits. """ if value is None: return None else: index = int(rounding((value - self.offset) / self.scale)) if self.size > index >= 0: return index else: raise ValueError("The value is out of the axis limits") def index2value(self, index): return self.axis[index] def set_index_from_value(self, value): self.index = self.value2index(value) # If the value is above the limits we must correct the value if self.continuous_value is False: self.value = self.index2value(self.index) def calibrate(self, value_tuple, index_tuple, modify_calibration=True): scale = (value_tuple[1] - value_tuple[0]) /\ (index_tuple[1] - index_tuple[0]) offset = value_tuple[0] - scale * index_tuple[0] if modify_calibration is True: self.offset = offset self.scale = scale else: return offset, scale traits_view = \ tui.View( tui.Group( tui.Group( tui.Item(name='name'), tui.Item(name='size', style='readonly'), tui.Item(name='index_in_array', style='readonly'), tui.Item(name='index'), tui.Item(name='value', style='readonly'), tui.Item(name='units'), tui.Item(name='navigate', label = 'navigate'), show_border = True,), tui.Group( tui.Item(name='scale'), tui.Item(name='offset'), label = 'Calibration', show_border = True,), label = "Data Axis properties", show_border = True,), title = 'Axis configuration', )