Exemplo n.º 1
0
class EmulatorPreferencesPane(PreferencesPane):
    """ The preferences pane for the Framework application.
    """

    #### 'PreferencesPane' interface ##########################################

    # The factory to use for creating the preferences model object.
    model_factory = EmulatorPreferences

    category = Str('Editors')

    #### 'FrameworkPreferencesPane' interface ################################

    # Note the quirk in the RangeEditor: specifying a custom editor is
    # supposed to take the defaults from the item name specified, but I
    # can't get it to work with only the "mode" parameter.  I have to specify
    # all the other params, and the low/high values have to be attributes
    # in EmulatorPreferences, not the values in the trait itself.  See
    # traitsui/editors/range_editor.py
    view = View(
        VGroup(HGroup(Item('map_width', editor=RangeEditor(mode="spinner", is_float=False, low_name='map_width_low', high_name='map_width_high')),
                      Label('Default Character Map Width (in bytes)'),
                      show_labels = False),
               HGroup(Item('bitmap_width', editor=RangeEditor(mode="spinner", is_float=False, low_name='bitmap_width_low', high_name='bitmap_width_high')),
                      Label('Default Bitmap Width (in bytes)'),
                      show_labels = False),
               HGroup(Item('hex_grid_width', editor=RangeEditor(mode="spinner", is_float=False, low_name='hex_grid_width_low', high_name='hex_grid_width_high')),
                      Label('Default Hex Grid Width (in bytes)'),
                      show_labels = False),
               HGroup(Item('text_font'),
                      Label('Hex Display Font'),
                      show_labels = False),
               HGroup(Item('header_font'),
                      Label('Column Header Font'),
                      show_labels = False),
               HGroup(Item('int_display_format'),
                      Label('Number Display Format'),
                      show_labels=False),
               HGroup(Item('hex_display_format'),
                      Label('Hex Display Format'),
                      show_labels=False),
               HGroup(Item('hex_grid_lower_case'),
                      Label('Use Lower Case for Hex Digits'),
                      show_labels = False),
               HGroup(Item('assembly_lower_case'),
                      Label('Use Lower Case for Assembler Mnemonics'),
                      show_labels = False),
               HGroup(Item('highlight_background_color', editor=ColorEditor(), style='custom'),
                      Label('Highlight Color'),
                      show_labels = False),
               label='Hex Editor'),
        resizable=True)
Exemplo n.º 2
0
class Align(HasTraits):
    # The position of the view
    position = Array(shape=(3, ))

    brightness = Range(-2., 2., value=0.)
    contrast = Range(0., 3., value=1.)
    opacity = Range(0., 1., value=.1)
    colormap = Enum(*lut_manager.lut_mode_list())
    fliplut = Bool

    outlines_visible = Bool(default_value=True)
    outline_rep = Enum(outline_reps)
    outline_color = Color(
        default=options.config.get("mayavi_aligner", "outline_color"))
    line_width = Range(0.5,
                       10.,
                       value=float(
                           options.config.get("mayavi_aligner", "line_width")))
    point_size = Range(0.5,
                       10.,
                       value=float(
                           options.config.get("mayavi_aligner", "point_size")))

    epi_filter = Enum(None, "median", "gradient")
    filter_strength = Range(1, 20, value=3)

    scene_3d = Instance(MlabSceneModel, ())
    scene_x = Instance(MlabSceneModel, ())
    scene_y = Instance(MlabSceneModel, ())
    scene_z = Instance(MlabSceneModel, ())

    # The data source
    epi_src = Instance(Source)
    surf_src = Instance(Source)
    xfm = Instance(Filter)
    surf = Instance(Module)

    disable_render = Bool

    flip_fb = Bool
    flip_lr = Bool
    flip_ud = Bool

    save_callback = Instance(types.FunctionType)
    save_btn = Button(label="Save Transform")

    legend = Str(legend)

    #---------------------------------------------------------------------------
    # Object interface
    #---------------------------------------------------------------------------
    def __init__(self, pts, polys, epi, xfm=None, xfmtype='magnet', **traits):
        '''
        Parameters
        ----------
        xfm : array_like, optional
            The initial 4x4 rotation matrix into magnet space 
            (epi with slice affine)
        '''
        self.load_epi(epi, xfm, xfmtype)
        self.pts, self.polys = pts, polys
        self._undolist = []
        self._redo = None
        super(Align, self).__init__(**traits)

    def load_epi(self, epifilename, xfm=None, xfmtype="magnet"):
        """Loads the EPI image from the specified epifilename.
        """
        nii = nibabel.load(epifilename)
        self.epi_file = nii
        epi = nii.get_data().astype(float).squeeze()
        if epi.ndim > 3:
            epi = epi[:, :, :, 0]
        self.affine = nii.get_affine()
        base = nii.get_header().get_base_affine()
        self.base = base
        self.origin = base[:3, -1]
        self.spacing = np.diag(base)[:3]
        if xfm is None:
            self.startxfm = np.dot(base, np.linalg.inv(self.affine))
        elif xfmtype == "magnet":
            self.startxfm = np.dot(np.dot(base, np.linalg.inv(self.affine)),
                                   xfm)
        else:
            print("using xfmtype %s" % xfmtype)
            self.startxfm = xfm

        self.center = self.spacing * nii.get_shape()[:3] / 2 + self.origin

        self.padshape = 2**(np.ceil(np.log2(np.array(epi.shape))))

        epi = np.nan_to_num(epi)
        self.epi_orig = epi - epi.min()
        self.epi_orig /= self.epi_orig.max()
        self.epi_orig *= 2
        self.epi_orig -= 1
        self.epi = self.epi_orig.copy()

    #---------------------------------------------------------------------------
    # Default values
    #---------------------------------------------------------------------------
    def _position_default(self):
        return np.abs(self.origin) + (
            (np.array(self.epi.shape) + 1) % 2) * np.abs(self.spacing) / 2

    def _epi_src_default(self):
        sf = mlab.pipeline.scalar_field(self.epi,
                                        figure=self.scene_3d.mayavi_scene,
                                        name='EPI')
        sf.origin = self.origin
        sf.spacing = self.spacing
        return sf

    def _surf_src_default(self):
        return mlab.pipeline.triangular_mesh_source(
            self.pts[:, 0],
            self.pts[:, 1],
            self.pts[:, 2],
            self.polys,
            figure=self.scene_3d.mayavi_scene,
            name='Cortex')

    def _surf_default(self):
        smooth = mlab.pipeline.poly_data_normals(
            self.xfm, figure=self.scene_3d.mayavi_scene)
        smooth.filter.splitting = False
        surf = mlab.pipeline.surface(smooth, figure=self.scene_3d.mayavi_scene)
        surf.actor.mapper.scalar_visibility = 0
        return surf

    def _xfm_default(self):
        xfm = mlab.pipeline.transform_data(self.surf_src,
                                           figure=self.scene_3d.mayavi_scene)

        def savexfm(info, evt):
            self._undolist.append(xfm.transform.matrix.to_array())
            np.save("/tmp/last_xfm.npy", self.get_xfm())

        xfm.widget.add_observer("EndInteractionEvent", savexfm)
        xfm.widget.add_observer("EndInteractionEvent", self.update_slabs)
        xfm.transform.set_matrix(self.startxfm.ravel())
        xfm.widget.set_transform(xfm.transform)
        return xfm

    #---------------------------------------------------------------------------
    # Scene activation callbacks
    #---------------------------------------------------------------------------
    @on_trait_change('scene_3d.activated')
    def display_scene_3d(self):
        self.scene_3d.mlab.view(40, 50)
        self.scene_3d.scene.renderer.use_depth_peeling = True
        self.scene_3d.scene.background = (0, 0, 0)
        # Keep the view always pointing up
        self.scene_3d.scene.interactor.interactor_style = tvtk.InteractorStyleTerrain(
        )
        self.scene_3d.scene_editor.aligner = self

        self.opacity = float(options.config.get("mayavi_aligner", "opacity"))
        self.xfm.widget.enabled = False
        self.colormap = options.config.get("mayavi_aligner", "colormap")

        self.disable_render = True
        for ax in [self.x_axis, self.y_axis, self.z_axis]:
            ax.update_position()
            ax.reset_view()
        self.disable_render = False

    @on_trait_change('scene_x.activated')
    def display_scene_x(self):
        self.x_axis = XAxis(parent=self)

    @on_trait_change('scene_y.activated')
    def display_scene_y(self):
        self.y_axis = YAxis(parent=self)

    @on_trait_change('scene_z.activated')
    def display_scene_z(self):
        self.z_axis = ZAxis(parent=self)

    #---------------------------------------------------------------------------
    # Traits callback
    #---------------------------------------------------------------------------

    def _save_btn_changed(self):
        if self.save_callback is not None:
            self.save_callback(self)

    def _disable_render_changed(self):
        self.scene_3d.scene.disable_render = self.disable_render

    def _position_changed(self):
        self.disable_render = True
        for ax in [self.x_axis, self.y_axis, self.z_axis]:
            ax.update_position()
        self.disable_render = False

    def _outlines_visible_changed(self):
        self.disable_render = True
        for ax in [self.x_axis, self.y_axis, self.z_axis]:
            ax.toggle_outline()
        self.disable_render = False

    @on_trait_change("colormap, fliplut")
    def update_colormap(self):
        for ax in [self.x_axis, self.y_axis, self.z_axis]:
            if ax.ipw_3d and ax.ipw:
                ax.ipw_3d.parent.scalar_lut_manager.set(
                    lut_mode=self.colormap, reverse_lut=self.fliplut)
                ax.ipw.parent.scalar_lut_manager.set(lut_mode=self.colormap,
                                                     reverse_lut=self.fliplut)

    def _opacity_changed(self):
        self.surf.actor.property.opacity = self.opacity

    @on_trait_change("brightness,contrast")
    def update_brightness(self):
        self.epi_src.scalar_data = (self.epi * self.contrast) + self.brightness

    @on_trait_change("flip_ud")
    def update_flipud(self):
        #self.epi_src.scalar_data = self.epi_src.scalar_data[:,:,::-1]
        flip = np.eye(4)
        flip[2, 2] = -1
        mat = self.xfm.transform.matrix.to_array()
        self.set_xfm(np.dot(mat, flip), "base")

    @on_trait_change("flip_lr")
    def update_fliplr(self):
        #self.epi_src.scalar_data = self.epi_src.scalar_data[::-1]
        flip = np.eye(4)
        flip[0, 0] = -1
        mat = self.xfm.transform.matrix.to_array()
        self.set_xfm(np.dot(mat, flip), "base")

    @on_trait_change("flip_fb")
    def update_flipfb(self):
        #self.epi_src.scalar_data = self.epi_src.scalar_data[:,::-1]
        flip = np.eye(4)
        flip[1, 1] = -1
        mat = self.xfm.transform.matrix.to_array()
        self.set_xfm(np.dot(mat, flip), "base")

    @on_trait_change("epi_filter, filter_strength")
    def update_epifilter(self):
        if self.epi_filter is None:
            self.epi = self.epi_orig.copy()
        elif self.epi_filter == "median":
            fstr = np.floor(self.filter_strength / 2) * 2 + 1
            self.epi = volume.detrend_median(self.epi_orig.T, fstr).T
        elif self.epi_filter == "gradient":
            self.epi = volume.detrend_gradient(self.epi_orig.T,
                                               self.filter_strength).T

        self.update_brightness()

    def update_slabs(self, *args, **kwargs):
        self.disable_render = True
        for ax in [self.x_axis, self.y_axis, self.z_axis]:
            ax.update_slab()
        self.disable_render = False

    def get_xfm(self, xfmtype="magnet"):
        if xfmtype in ["anat->epicoord", "coord"]:
            ibase = np.linalg.inv(self.base)
            xfm = self.xfm.transform.matrix.to_array()
            return np.dot(ibase, xfm)
        elif xfmtype in ["anat->epibase", "base"]:
            return self.xfm.transform.matrix.to_array()
        elif xfmtype in ['anat->magnet', "magnet"]:
            ibase = np.linalg.inv(self.base)
            xfm = self.xfm.transform.matrix.to_array()
            return np.dot(self.affine, np.dot(ibase, xfm))

    def set_xfm(self, matrix, xfmtype='magnet'):
        assert xfmtype in "magnet coord base".split(), "Unknown transform type"
        if xfmtype == "coord":
            matrix = np.dot(self.base, matrix)
        elif xfmtype == "magnet":
            iaff = np.linalg.inv(self.affine)
            matrix = np.dot(self.base, np.dot(iaff, matrix))

        self.xfm.transform.set_matrix(matrix.ravel())
        self.xfm.widget.set_transform(self.xfm.transform)
        self.xfm.update_pipeline()
        self.update_slabs()

    def undo(self):
        if len(self._undolist) > 0:
            self.xfm.transform.set_matrix(self._undolist[-1].ravel())
            self.xfm.widget.set_transform(self.xfm.transform)
            self.xfm.update_pipeline()
            self.update_slabs()
            self._redo = self._undolist.pop()

    #---------------------------------------------------------------------------
    # The layout of the dialog created
    #---------------------------------------------------------------------------
    view = View(HGroup(
        Group(
            Item('scene_y', editor=SceneEditor(scene_class=FlatScene)),
            Item('scene_z', editor=SceneEditor(scene_class=FlatScene)),
            show_labels=False,
        ),
        Group(
            Item('scene_x', editor=SceneEditor(scene_class=FlatScene)),
            Item('scene_3d', editor=SceneEditor(scene_class=ThreeDScene)),
            show_labels=False,
        ),
        Group(Group(
            Item("save_btn",
                 show_label=False,
                 visible_when="save_callback is not None"),
            "brightness",
            "contrast",
            "epi_filter",
            Item('filter_strength', visible_when="epi_filter is not None"),
            "_",
            "opacity",
            "_",
            Item('colormap',
                 editor=ImageEnumEditor(values=lut_manager.lut_mode_list(),
                                        cols=6,
                                        path=lut_manager.lut_image_dir)),
            "fliplut",
            "_",
            "flip_ud",
            "flip_lr",
            "flip_fb",
            "_",
            Item('outline_color', editor=ColorEditor()),
            'outline_rep',
            'line_width',
            'point_size',
            '_',
        ),
              Group(
                  Item('legend',
                       editor=TextEditor(),
                       style='readonly',
                       show_label=False,
                       emphasized=True,
                       dock='vertical'),
                  show_labels=False,
              ),
              orientation='vertical'),
    ),
                resizable=True,
                title='Aligner')
Exemplo n.º 3
0
class Polygon(HasTraits):
    line_color = Trait(Color((0, 0, 0)), editor=ColorEditor())
Exemplo n.º 4
0
class Polygon(HasTraits):
    line_color = Trait(wx.wxColour(0, 0, 0), editor=ColorEditor())
Exemplo n.º 5
0
class multicolorfits_viewer(HasTraits):
    """The main window. Has instructions for creating and destroying the app.
    """

    panel1 = Instance(ControlPanel)
    panel2 = Instance(ControlPanel)
    panel3 = Instance(ControlPanel)
    panel4 = Instance(ControlPanel)

    figure_combined = Instance(Figure, ())
    image = Array()
    image_axes = Instance(Axes)
    image_axesimage = Instance(AxesImage)
    image_xsize = Int(256)
    image_ysize = Int(256)

    gamma = Float(2.2)

    tickcolor = Str(
        '0.9'
    )  #,auto_set=False,enter_set=True) #Apparently need to set to TextEditor explicitly below...
    tickcolor_picker = ColorTrait((230, 230, 230))
    sexdec = Enum('Sexagesimal', 'Decimal')

    plotbutton_combined = Button(u"Plot Combined")
    plotbutton_inverted_combined = Button(u"Plot Inverted Combined")
    clearbutton_combined = Button(u"Clear Combined")
    save_the_image = Button(u"Save Image")
    save_the_fits = Button(u"Save RGB Fits")
    print_params = Button(u"Print Params")

    status_string_left = Str('')
    status_string_right = Str('')

    def _panel1_default(self):
        return ControlPanel()  #figure=self.figure)

    def _panel2_default(self):
        return ControlPanel()  #figure=self.figure)

    def _panel3_default(self):
        return ControlPanel()  #figure=self.figure)

    def _panel4_default(self):
        return ControlPanel()  #figure=self.figure)

    def __init__(self):
        super(multicolorfits_viewer, self).__init__()

        self._init_params(
        )  #Set placeholder things like the WCS, tick color, map units...
        self.image = self._fresh_image()  #Sets a blank image
        self.image_axes = self.figure_combined.add_subplot(111, aspect=1)
        self.image_axesimage = self.image_axes.imshow(self.image,
                                                      cmap='gist_gray',
                                                      origin='lower',
                                                      interpolation='nearest')
        self.image_axes.set_xlabel(self.xlabel)
        self.image_axes.set_ylabel(self.ylabel)
        self.image_axes.tick_params(
            axis='both',
            color=self.tickcolor)  #colors=... also sets label color
        try:
            self.image_axes.coords.frame.set_color(
                self.tickcolor
            )  #Updates the frame color.  .coords won't exist until WCS set
        except:
            [
                self.image_axes.spines[s].set_color(self.tickcolor)
                for s in ['top', 'bottom', 'left', 'right']
            ]

    view = View(Item("gamma",label=u"Gamma",show_label=True),
                Item('_'),

                HSplit(
                  Group(
                    Group(Item('panel1', style="custom",show_label=False),label='Image 1'),
                    Group(Item('panel2', style="custom",show_label=False),label='Image 2'),
                    Group(Item('panel3', style="custom",show_label=False),label='Image 3'),
                    Group(Item('panel4', style="custom",show_label=False),label='Image 4'),
                  orientation='horizontal',layout='tabbed',springy=True),

                VGroup(
                    HGroup(
                      Item('tickcolor',label='Tick Color',show_label=True, \
                          tooltip='Color of ticks: standard name float[0..1], or #hex', \
                          editor=TextEditor(auto_set=False, enter_set=True,)),
                      Item('tickcolor_picker',label='Pick',show_label=True,editor=ColorEditor()),
                      Item('sexdec',label='Coordinate Style',tooltip=u'Display coordinates in sexagesimal or decimal', \
                           show_label=True),
                      ),
                    Item('figure_combined', editor=MPLFigureEditor(),show_label=False, width=900, height=800,resizable=True),
                  HGroup(

                    Item('plotbutton_combined', tooltip=u"Plot the image",show_label=False),
                    Item('plotbutton_inverted_combined', tooltip=u"Plot the inverted image",show_label=False),
                    Item('clearbutton_combined',tooltip=u'Clear the combined figure',show_label=False),
                    Item("save_the_image", tooltip=u"Save current image. Mileage may vary...",show_label=False),
                    Item("save_the_fits", tooltip=u"Save RGB frames as single fits file with header.",show_label=False),
                    Item("print_params", tooltip=u"Print out current settings for use in manual image scripting.",show_label=False),
                  ), #HGroup
                ), #VGroup
                show_labels=False,),
           resizable=True,
           height=0.75, width=0.75,
           statusbar = [StatusItem(name = 'status_string_left', width = 0.5),
                        StatusItem(name = 'status_string_right', width = 0.5)],
           title=u"Fits Multi-Color Combiner",handler=MPLInitHandler ) #View

    def _init_params(self):
        plt.rcParams.update({'font.family': 'serif','xtick.major.size':6,'ytick.major.size':6, \
                             'xtick.major.width':1.,'ytick.major.width':1., \
                             'xtick.direction':'in','ytick.direction':'in'})
        try:
            plt.rcParams.update({
                'xtick.top': True,
                'ytick.right': True
            })  #apparently not in mpl v<2.0...
        except:
            pass  #Make a workaround for mpl<2.0 later...
        self.datamin_initial = 0.
        self.datamax_initial = 1.
        self.datamin = 0.
        self.datamax = 1.  #This will be the displayed value of the scaling min/max
        #self.mapunits='Pixel Value'
        #self.tickcolor='0.5'#'white', 'black', '0.5'
        self.wcs = WCS()
        self.xlabel = 'x'
        self.ylabel = 'y'

    def _fresh_image(self):
        #self.norm=ImageNormalize(self.image,stretch=scaling_fns['linear']() )
        blankdata = np.zeros([100, 100])
        blankdata[-1, -1] = 1
        return blankdata

    def update_radecpars(self):
        self.rapars = self.image_axes.coords[0]
        self.decpars = self.image_axes.coords[1]
        self.rapars.set_ticks(color=self.tickcolor)
        self.decpars.set_ticks(color=self.tickcolor)
        self.rapars.set_ticks(number=6)
        self.decpars.set_ticks(number=6)
        #self.rapars.set_ticklabel(size=8); self.decpars.set_ticklabel(size=8); #size here means the tick length
        ##self.rapars.set_ticks(spacing=10*u.arcmin, color='white', exclude_overlapping=True)
        ##self.decpars.set_ticks(spacing=5*u.arcmin, color='white', exclude_overlapping=True)
        self.rapars.display_minor_ticks(True)
        #self.rapars.set_minor_frequency(10)
        self.decpars.display_minor_ticks(True)
        if self.sexdec == 'Sexagesimal':
            self.rapars.set_major_formatter('hh:mm:ss.ss')
            self.decpars.set_major_formatter('dd:mm:ss.ss')
            #self.rapars.set_separator(('$^\mathrm{H}$', "'", '"'))
            self.rapars.set_separator(('H ', "' ", '" '))
            #format_xcoord=lambda x,y: '{}i$^\mathrm{H}${}{}{}'.format(x[0],x[1],"'",x[2],'"')
            #self.image_axes.format_coord=format_xcoord
        else:
            self.rapars.set_major_formatter('d.dddddd')
            self.decpars.set_major_formatter('d.dddddd')
        ##self.decpars.ticklabels.set_rotation(45) #Rotate ticklabels
        ##self.decpars.ticklabels.set_color(xkcdrust) #Ticklabel Color

    @on_trait_change('tickcolor')
    def update_tickcolor(self):
        try:
            #Catch case when you've predefined a color variable in hex string format, e.g., mynewred='#C11B17'
            #--> Need to do this first, otherwise traits throws a fit up the stack even despite the try/except check
            globals()[
                self.tickcolor]  #This check should catch undefined inputs
            self.image_axes.tick_params(axis='both',
                                        color=globals()[self.tickcolor])
            self.image_axes.coords.frame.set_color(self.tickcolor)
            self.tickcolor_picker = hex_to_rgb(globals()[self.tickcolor])
            self.status_string_right = 'Tick color changed to ' + self.tickcolor
        except:
            try:
                self.tickcolor = to_hex(self.tickcolor)
                try:
                    self.update_radecpars()
                except:
                    self.image_axes.tick_params(axis='both',
                                                color=to_hex(self.tickcolor))
                    self.image_axes.coords.frame.set_color(
                        to_hex(self.tickcolor))
                self.status_string_right = 'Tick color changed to ' + self.tickcolor
            except:
                self.status_string_right = "Color name %s not recognized.  Must be standard mpl.colors string, float[0..1] or #hex string" % (
                    self.tickcolor)
        try:
            self.tickcolor_picker = hex_to_rgb(to_hex(
                self.tickcolor))  #update the picker color...
        except:
            pass
        self.figure_combined.canvas.draw()

    @on_trait_change('tickcolor_picker')
    def update_tickcolorpicker(self):
        #print self.tickcolor_picker.name()
        self.tickcolor = self.tickcolor_picker.name()

    @on_trait_change('sexdec')
    def update_sexdec(self):
        self.update_radecpars()
        self.figure_combined.canvas.draw()
        self.status_string_right = 'Coordinate style changed to ' + self.sexdec

    def _plotbutton_combined_fired(self):
        try:
            self.panel1.data
        except:
            self.status_string_right = "No fits file loaded yet!"
            return
        #self.image=self.panel1.data
        self.wcs = WCS(self.panel1.hdr)
        self.hdr = self.panel1.hdr

        self.combined_RGB = combine_multicolor([
            pan.image_colorRGB
            for pan in [self.panel1, self.panel2, self.panel3, self.panel4]
            if pan.in_use == True
        ],
                                               gamma=self.gamma)

        ###Using this command is preferable, as long as the projection doesn't need to be updated...
        #  The home zoom button will work, but no WCS labels because projection wasn't set during init.
        #self.image_axesimage.set_data(self.data)
        ###Using this set instead properly updates the axes labels to WCS, but the home zoom button won't work
        self.figure_combined.clf()
        self.image_axes = self.figure_combined.add_subplot(111,
                                                           aspect=1,
                                                           projection=self.wcs)
        self.image_axesimage = self.image_axes.imshow(self.combined_RGB,
                                                      origin='lower',
                                                      interpolation='nearest')

        self.update_radecpars()
        self.figure_combined.canvas.draw()
        self.status_string_right = "Plot updated"

    def _plotbutton_inverted_combined_fired(self):
        try:
            self.panel1.data
        except:
            self.status_string_right = "No fits file loaded yet!"
            return
        self.wcs = WCS(self.panel1.hdr)
        self.hdr = self.panel1.hdr
        self.combined_RGB = combine_multicolor(
            [
                pan.image_colorRGB for pan in
                [self.panel1, self.panel2, self.panel3, self.panel4]
                if pan.in_use == True
            ],
            inverse=True,
            gamma=self.gamma,
        )
        self.figure_combined.clf()
        self.image_axes = self.figure_combined.add_subplot(111,
                                                           aspect=1,
                                                           projection=self.wcs)
        self.image_axesimage = self.image_axes.imshow(self.combined_RGB,
                                                      origin='lower',
                                                      interpolation='nearest')
        self.update_radecpars()
        self.figure_combined.canvas.draw()
        self.status_string_right = "Plot updated"

    def _clearbutton_combined_fired(self):
        try:
            del self.combined_RGB  #If clear already pressed once, data will already have been deleted...
        except:
            pass
        self.in_use = False
        self.figure_combined.clf()
        self.image = self._fresh_image()
        self.image_axes = self.figure_combined.add_subplot(111, aspect=1)
        self.image_axesimage = self.image_axes.imshow(self.image,
                                                      cmap='gist_gray',
                                                      origin='lower',
                                                      interpolation='nearest')
        self.xlabel = 'x'
        self.ylabel = 'y'
        self.image_axes.set_xlabel(self.xlabel)
        self.image_axes.set_ylabel(self.ylabel)
        self.image_axes.tick_params(axis='both', color=self.tickcolor)
        try:
            self.image_axes.coords.frame.set_color(self.tickcolor)
        except:
            self.tickcolor_picker = hex_to_rgb(to_hex(self.tickcolor))
        self.figure_combined.canvas.draw()
        self.status_string_right = "Plot cleared"

    def setup_mpl_events(self):
        self.image_axeswidget = AxesWidget(self.image_axes)
        self.image_axeswidget.connect_event('motion_notify_event',
                                            self.image_on_motion)
        self.image_axeswidget.connect_event('figure_leave_event',
                                            self.on_cursor_leave)
        self.image_axeswidget.connect_event('figure_enter_event',
                                            self.on_cursor_enter)
        self.image_axeswidget.connect_event('button_press_event',
                                            self.image_on_click)

    def image_on_motion(self, event):
        if event.xdata is None or event.ydata is None: return
        x = int(np.round(event.xdata))
        y = int(np.round(event.ydata))
        if ((x >= 0) and (x < self.image.shape[1]) and (y >= 0)
                and (y < self.image.shape[0])):
            imval = self.image[y, x]
            self.status_string_left = "x,y={},{}  {:.5g}".format(x, y, imval)
        else:
            self.status_string_left = ""

    def image_on_click(self, event):
        if event.xdata is None or event.ydata is None or event.button is not 1:
            return  #Covers when click outside of main plot
        #print event
        x = int(
            np.round(event.xdata)
        )  #xdata is the actual pixel position.  xy is in 'display space', i.e. pixels in the canvas
        y = int(np.round(event.ydata))
        xwcs, ywcs = self.wcs.wcs_pix2world([[x, y]], 0)[0]
        #print xwcs,ywcs
        if ((x >= 0) and (x < self.image.shape[1]) and (y >= 0)
                and (y < self.image.shape[0])):
            imval = self.image[y, x]
            self.status_string_right = "x,y=[{},{}], RA,DEC=[{}, {}], value = {:.5g}".format(
                x, y, xwcs, ywcs, imval)
            #self.status_string_right = "x,y[{},{}] = {:.3f},{:.3f}  {:.5g}".format(x, y,event.xdata,event.ydata, imval)
        else:
            self.status_string_right = ""
        ## left-click: event.button = 1, middle-click: event.button=2, right-click: event.button=3.
        ## For double-click, event.dblclick = False for first click, True on second
        #print event.button, event.dblclick

    def on_cursor_leave(self, event):
        QApplication.restoreOverrideCursor()
        self.status_string_left = ''

    def on_cursor_enter(self, event):
        QApplication.setOverrideCursor(Qt.CrossCursor)

    def _save_the_image_fired(self):
        dlg = FileDialog(action='save as')
        if dlg.open() == OK: plt.savefig(dlg.path, size=(800, 800), dpi=300)

    def _save_the_fits_fired(self):
        #Generate a generic header with correct WCS and comments about the colors that made it
        #... come back and finish this later...
        dlg = FileDialog(action='save as')
        if dlg.open() == OK:
            pyfits.writeto(
                dlg.path,
                np.swapaxes(np.swapaxes(self.combined_RGB, 0, 2), 2, 1),
                self.hdr)

    def _print_params_fired(self):
        print('\n\nRGB Image plot params:')
        pan_i = 0
        for pan in [self.panel1, self.panel2, self.panel3, self.panel4]:
            pan_i += 1
            if pan.in_use == True:
                print('image%i: ' % (pan_i))
                print('    vmin = %.3e , vmax = %.3e, scale = %s' %
                      (pan.datamin, pan.datamax, pan.image_scale))
                print("    image color = '%s'" % (pan.imagecolor))
        print("gamma = %.1f , tick color = '%s'\n" %
              (self.gamma, self.tickcolor))
Exemplo n.º 6
0
class ControlPanel(HasTraits):
    """This is the control panel where the various parameters for the images are specified
    """

    gamma = 2.2

    fitsfile = File(filter=[u"*.fits"])
    image_figure = Instance(Figure, ())
    image = Array()
    image_axes = Instance(Axes)
    image_axesimage = Instance(AxesImage)
    image_xsize = Int(256)
    image_ysize = Int(256)

    datamin = Float(0.0, auto_set=False, enter_set=True)  #Say, in mJy
    datamax = Float(
        1.0, auto_set=False, enter_set=True
    )  #auto_set=input set on each keystroke, enter_set=set after Enter
    percent_min = Range(value=0.0, low=0.0, high=100.)
    percent_max = Range(value=100.0, low=0.0,
                        high=100.)  #Percentile of data values for rescaling
    minmaxbutton = Button('Min/Max')
    zscalebutton = Button('Zscale')

    image_scale = Str('linear')
    scale_dropdown = Enum(
        ['linear', 'sqrt', 'squared', 'log', 'power', 'sinh', 'asinh'])

    imagecolor = Str('#FFFFFF')
    imagecolor_picker = ColorTrait((255, 255, 255))

    #plotbeam_button=Button('Add Beam (FWHM)')

    plotbutton_individual = Button(u"Plot Single")
    plotbutton_inverted_individual = Button(u"Plot Inverted Single")
    clearbutton_individual = Button(u"Clear Single")

    status_string_left = Str('')
    status_string_right = Str('')

    def __init__(self):
        self._init_params(
        )  #Set placeholder things like the WCS, tick color, map units...
        self.image = self._fresh_image()  #Sets a blank image
        self.image_axes = self.image_figure.add_subplot(111, aspect=1)
        self.image_axesimage = self.image_axes.imshow(self.image,
                                                      cmap='gist_gray',
                                                      origin='lower',
                                                      interpolation='nearest')
        self.image_axes.axis('off')

    view = View(
      HSplit(
        VGroup(
            Item("fitsfile", label=u"Select 2D FITS file", show_label=True), #,height=100),

            HGroup(
              VGroup(
                     Item('plotbutton_individual', tooltip=u"Plot the single image",show_label=False),
                     Item('plotbutton_inverted_individual', tooltip=u"Plot the single inverted image",show_label=False),
                     Item('clearbutton_individual', tooltip=u"Clear the single image",show_label=False),
                     Item('_'),

                     Item('imagecolor',label='Image Color',show_label=True, \
                      tooltip='Color of ticks: standard name float[0..1], or #hex', \
                      editor=TextEditor(auto_set=False, enter_set=True,)),
                     Item('imagecolor_picker',label='Pick',show_label=True,editor=ColorEditor()),

                     ),
              Item('image_figure', editor=MPLFigureEditor(), show_label=False, width=300, height=300,resizable=True),
            ),
            HGroup(Item('datamin', tooltip=u"Minimum data val for scaling", show_label=True),
                   Item('datamax', tooltip=u"Maximum data val for scaling", show_label=True)),
            Item('percent_min', tooltip=u"Min. percentile for scaling", show_label=True),
            Item('percent_max', tooltip=u"Max. percentile for scaling", show_label=True),
            HGroup(Item('minmaxbutton', tooltip=u"Reset to data min/max", show_label=False),
                   Item('zscalebutton', tooltip=u"Compute scale min/max from zscale algorithm", show_label=False)),
            Item('scale_dropdown',label='Scale',show_label=True),

        ), #End of Left column

      ), #End of HSplit
      resizable=True, #height=0.75, width=0.75, #title=u"Multi-Color Image Combiner", 
      handler=MPLInitHandler,
      statusbar = [StatusItem(name = 'status_string_left', width = 0.5), StatusItem(name = 'status_string_right', width = 0.5)]
    ) #End of View

    def _init_params(self):
        self.in_use = False
        plt.rcParams.update({'font.family': 'serif','xtick.major.size':6,'ytick.major.size':6, \
                             'xtick.major.width':1.,'ytick.major.width':1., \
                             'xtick.direction':'in','ytick.direction':'in'})
        try:
            plt.rcParams.update({
                'xtick.top': True,
                'ytick.right': True
            })  #apparently not in mpl v<2.0...
        except:
            pass  #Make a workaround for mpl<2.0 later...
        self.datamin_initial = 0.
        self.datamax_initial = 1.
        self.datamin = 0.
        self.datamax = 1.  #This will be the displayed value of the scaling min/max

    def _fresh_image(self):
        blankdata = np.zeros([100, 100])
        blankdata[-1, -1] = 1
        return blankdata

    def _fitsfile_changed(self):
        self.data, self.hdr = pyfits.getdata(self.fitsfile, header=True)
        force_hdr_floats(
            self.hdr
        )  #Ensure that WCS cards such as CDELT are floats instead of strings

        naxis = int(self.hdr['NAXIS'])
        if naxis > 2:
            #print('Dropping Extra axes')
            self.hdr = force_hdr_to_2D(self.hdr)
            try:
                self.data = self.data[0, 0, :, :]
            except:
                self.data = self.data[0, :, :]
            self.status_string_right = 'Dropped extra axes'

        self.datamax_initial = np.asscalar(np.nanmax(self.data))
        self.datamin_initial = np.asscalar(np.nanmin(self.data))
        self.datamax = np.asscalar(np.nanmax(self.data))
        self.datamin = np.asscalar(np.nanmin(self.data))

        self.in_use = True

    #@on_trait_change('imagecolor')
    #def update_imagecolor(self):
    def _imagecolor_changed(self):
        try:
            #Catch case when you've predefined a color variable in hex string format, e.g., mynewred='#C11B17'
            #--> Need to do this first, otherwise traits throws a fit up the stack even despite the try/except check
            globals()[
                self.imagecolor]  #This check should catch undefined inputs
            self.imagecolor_picker = hex_to_rgb(globals()[self.imagecolor])
            self.status_string_right = 'Image color changed to ' + self.imagecolor
        except:
            try:
                self.imagecolor = to_hex(self.imagecolor)
                self.status_string_right = 'Image color changed to ' + self.imagecolor
            except:
                self.status_string_right = "Color name %s not recognized.  Must be standard mpl.colors string, float[0..1] or #hex string" % (
                    self.imagecolor)
        try:
            self.imagecolor_picker = hex_to_rgb(to_hex(
                self.imagecolor))  #update the picker color...
        except:
            pass
        ### self.image_greyRGB and self.image_colorRGB may not yet be instantiated if the color is changed before clicking 'plot'
        try:
            self.image_colorRGB = colorize_image(self.image_greyRGB,
                                                 self.imagecolor,
                                                 colorintype='hex',
                                                 gammacorr_color=self.gamma)
        except:
            pass
        try:
            self.image_axesimage.set_data(self.image_colorRGB**(1. /
                                                                self.gamma))
        except:
            pass
        self.in_use = True
        self.image_figure.canvas.draw()

    #@on_trait_change('imagecolor_picker')
    #def update_imagecolorpicker(self):
    def _imagecolor_picker_changed(self):
        #print self.tickcolor_picker.name()
        self.imagecolor = self.imagecolor_picker.name()

    #@on_trait_change('percent_min')
    #def update_scalepercmin(self):
    def _percent_min_changed(self):
        self.datamin = np.nanpercentile(self.data, self.percent_min)
        self.data_scaled = (
            scaling_fns[self.image_scale]() +
            ManualInterval(vmin=self.datamin, vmax=self.datamax))(self.data)
        self.image_greyRGB = ski_color.gray2rgb(
            adjust_gamma(self.data_scaled, self.gamma))
        self.image_colorRGB = colorize_image(self.image_greyRGB,
                                             self.imagecolor,
                                             colorintype='hex',
                                             gammacorr_color=self.gamma)
        self.image_axesimage.set_data(self.image_colorRGB**(1. / self.gamma))
        self.image_figure.canvas.draw()
        self.status_string_right = "Updated scale using percentiles"

    #@on_trait_change('percent_max')
    #def update_scalepercmax(self):
    def _percent_max_changed(self):
        self.datamax = np.nanpercentile(self.data, self.percent_max)
        self.data_scaled = (
            scaling_fns[self.image_scale]() +
            ManualInterval(vmin=self.datamin, vmax=self.datamax))(self.data)
        self.image_greyRGB = ski_color.gray2rgb(
            adjust_gamma(self.data_scaled, self.gamma))
        self.image_colorRGB = colorize_image(self.image_greyRGB,
                                             self.imagecolor,
                                             colorintype='hex',
                                             gammacorr_color=self.gamma)
        self.image_axesimage.set_data(self.image_colorRGB**(1. / self.gamma))
        self.image_figure.canvas.draw()
        self.status_string_right = "Updated scale using percentiles"

    ### Very slow to update datamin and datamax as well as percs... Can comment these if desired and just hit plot after datamin

    #@on_trait_change('datamin')
    #def update_datamin(self): self.percent_min=np.round(percentileofscore(self.data.ravel(),self.datamin,kind='strict'),2)
    def _datamin_changed(self):
        self.percent_min = np.round(
            percentileofscore(self.data.ravel(), self.datamin, kind='strict'),
            2)

    #@on_trait_change('datamax')
    #def update_datamax(self): self.percent_max=np.round(percentileofscore(self.data.ravel(),self.datamax,kind='strict'),2)
    def _datamax_changed(self):
        self.percent_max = np.round(
            percentileofscore(self.data.ravel(), self.datamax, kind='strict'),
            2)

    #@on_trait_change('scale_dropdown')
    #def update_image_scale(self):
    def _scale_dropdown_changed(self):
        self.image_scale = self.scale_dropdown
        #self.norm=ImageNormalize(self.sregion,stretch=scaling_fns[self.image_scale]() )
        self.data_scaled = (
            scaling_fns[self.image_scale]() +
            ManualInterval(vmin=self.datamin, vmax=self.datamax))(self.data)
        #*** Instead, should I just integrate my imscale class here instead of astropy? ...
        self.image_greyRGB = ski_color.gray2rgb(
            adjust_gamma(self.data_scaled, self.gamma))
        self.image_colorRGB = colorize_image(self.image_greyRGB,
                                             self.imagecolor,
                                             colorintype='hex',
                                             gammacorr_color=self.gamma)

        self.image_axesimage.set_data(self.image_colorRGB**(1. / self.gamma))

        self.in_use = True

        self.image_figure.canvas.draw()
        self.status_string_right = 'Image scale function changed to ' + self.image_scale

    def _minmaxbutton_fired(self):
        self.datamin = self.datamin_initial
        self.datamax = self.datamax_initial
        #self.image_axesimage.norm.vmin=self.datamin
        #self.image_axesimage.norm.vmax=self.datamax
        self.percent_min = np.round(
            percentileofscore(self.data.ravel(), self.datamin, kind='strict'),
            2)
        self.percent_max = np.round(
            percentileofscore(self.data.ravel(), self.datamax, kind='strict'),
            2)
        #self.image_figure.canvas.draw()
        self.status_string_right = "Scale reset to min/max"

    def _zscalebutton_fired(self):
        tmpZscale = ZScaleInterval().get_limits(self.data)
        self.datamin = float(tmpZscale[0])
        self.datamax = float(tmpZscale[1])
        self.percent_min = np.round(
            percentileofscore(self.data.ravel(), self.datamin, kind='strict'),
            2)
        self.percent_max = np.round(
            percentileofscore(self.data.ravel(), self.datamax, kind='strict'),
            2)
        #self.image_figure.canvas.draw()
        self.status_string_right = "Min/max determined by zscale"

    def _plotbutton_individual_fired(self):
        try:
            self.data
        except:
            self.status_string_right = "No fits file loaded yet!"
            return
        #self.image=self.data
        ###Using this command is preferable, as long as the projection doesn't need to be updated...
        #  The home zoom button will work, but no WCS labels because projection wasn't set during init.
        #Scale the data to [0,1] range
        self.data_scaled = (
            scaling_fns[self.image_scale]() +
            ManualInterval(vmin=self.datamin, vmax=self.datamax))(self.data)
        #Convert scale[0,1] image to greyscale RGB image
        self.image_greyRGB = ski_color.gray2rgb(
            adjust_gamma(self.data_scaled, self.gamma))
        self.image_colorRGB = colorize_image(self.image_greyRGB,
                                             self.imagecolor,
                                             colorintype='hex',
                                             gammacorr_color=self.gamma)
        self.image_axesimage.set_data(self.image_colorRGB**(1. / self.gamma))
        ###Using this set instead properly updates the axes labels to WCS, but the home zoom button won't work
        #self.image_figure.clf()
        #self.image_axes = self.image_figure.add_subplot(111,aspect=1)#,projection=self.wcs)
        #self.image_axesimage = self.image_axes.imshow(self.image, cmap=self.image_cmap,origin='lower',interpolation='nearest', norm=self.norm)

        self.percent_min = np.round(
            percentileofscore(self.data.ravel(), self.datamin, kind='strict'),
            2)
        self.percent_max = np.round(
            percentileofscore(self.data.ravel(), self.datamax, kind='strict'),
            2)

        self.in_use = True

        #self.update_radecpars()
        self.image_figure.canvas.draw()
        self.status_string_right = "Plot updated"

    def _plotbutton_inverted_individual_fired(self):
        try:
            self.data
        except:
            self.status_string_right = "No fits file loaded yet!"
            return
        self.data_scaled = (
            scaling_fns[self.image_scale]() +
            ManualInterval(vmin=self.datamin, vmax=self.datamax))(self.data)
        self.image_greyRGB = ski_color.gray2rgb(
            adjust_gamma(self.data_scaled, self.gamma))
        self.image_colorRGB = colorize_image(self.image_greyRGB,
                                             hexinv(self.imagecolor),
                                             colorintype='hex',
                                             gammacorr_color=self.gamma)
        #self.image_axesimage.set_data(1.-self.image_colorRGB**(1./self.gamma))
        self.image_axesimage.set_data(
            combine_multicolor([
                self.image_colorRGB,
            ],
                               gamma=self.gamma,
                               inverse=True))
        self.percent_min = np.round(
            percentileofscore(self.data.ravel(), self.datamin, kind='strict'),
            2)
        self.percent_max = np.round(
            percentileofscore(self.data.ravel(), self.datamax, kind='strict'),
            2)
        self.in_use = True
        self.image_figure.canvas.draw()
        self.status_string_right = "Plot updated"

    def _clearbutton_individual_fired(self):
        try:
            del self.data, self.data_scaled, self.image_greyRGB
            self.image_colorRGB  #In case clear already pressed once
        except:
            pass
        self.in_use = False
        self.image_figure.clf()
        self.image = self._fresh_image()
        self.image_axes = self.image_figure.add_subplot(111, aspect=1)
        self.image_axesimage = self.image_axes.imshow(self.image,
                                                      cmap='gist_gray',
                                                      origin='lower',
                                                      interpolation='nearest')
        self.image_axes.axis('off')
        self.image_figure.canvas.draw()
        self.status_string_right = "Plot cleared"

    def setup_mpl_events(self):
        self.image_axeswidget = AxesWidget(self.image_axes)
        self.image_axeswidget.connect_event('motion_notify_event',
                                            self.image_on_motion)
        self.image_axeswidget.connect_event('figure_leave_event',
                                            self.on_cursor_leave)
        self.image_axeswidget.connect_event('figure_enter_event',
                                            self.on_cursor_enter)
        self.image_axeswidget.connect_event('button_press_event',
                                            self.image_on_click)

    def image_on_motion(self, event):
        if event.xdata is None or event.ydata is None: return
        x = int(np.round(event.xdata))
        y = int(np.round(event.ydata))
        if ((x >= 0) and (x < self.image.shape[1]) and (y >= 0)
                and (y < self.image.shape[0])):
            imval = self.image[y, x]
            self.status_string_left = "x,y={},{}  {:.5g}".format(x, y, imval)
        else:
            self.status_string_left = ""

    def image_on_click(self, event):
        if event.xdata is None or event.ydata is None or event.button is not 1:
            return  #Covers when click outside of main plot
        #print event
        x = int(
            np.round(event.xdata)
        )  #xdata is the actual pixel position.  xy is in 'display space', i.e. pixels in the canvas
        y = int(np.round(event.ydata))
        #xwcs,ywcs=self.wcs.wcs_pix2world([[x,y]],0)[0]; #print xwcs,ywcs
        if ((x >= 0) and (x < self.image.shape[1]) and (y >= 0)
                and (y < self.image.shape[0])):
            imval = self.image[y, x]
            #self.status_string_right = "x,y=[{},{}], RA,DEC=[{}, {}], value = {:.5g}".format(x, y,xwcs,ywcs, imval)
            self.status_string_right = "x,y[{},{}] = {:.3f},{:.3f}  {:.5g}".format(
                x, y, event.xdata, event.ydata, imval)
        else:
            self.status_string_right = ""
        ## left-click: event.button = 1, middle-click: event.button=2, right-click: event.button=3.
        ## For double-click, event.dblclick = False for first click, True on second
        #print event.button, event.dblclick

    def on_cursor_leave(self, event):
        QApplication.restoreOverrideCursor()
        self.status_string_left = ''

    def on_cursor_enter(self, event):
        QApplication.setOverrideCursor(Qt.CrossCursor)