示例#1
0
    def __copyOverlay(self):
        """Creates a copy of the currently selected overlay, and inserts it
        into the :class:`.OverlayList`.
        """

        import wx

        overlay = self.__displayCtx.getSelectedOverlay()

        if overlay is None:
            return

        # TODO support for other overlay types
        if type(overlay) != fslimage.Image:
            raise RuntimeError('Currently, only {} instances can be '
                               'copied'.format(fslimage.Image.__name__))

        display = self.__displayCtx.getDisplay(overlay)

        # We ask the user questions three:
        #  - Copy data, or create an empty (a.k.a. mask) image?
        #  - Copy display settings?
        #  - For 4D, copy 4D, or just the current 3D volume?
        #
        # Here we build a list of
        # questions and initial states.
        options = []
        states = []

        createMaskSetting = 'fsleyes.actions.copyoverlay.createMask'
        copyDisplaySetting = 'fsleyes.actions.copyoverlay.copyDisplay'
        copy4DSetting = 'fsleyes.actions.copyoverlay.copy4D'

        createMask = fslsettings.read(createMaskSetting, False)
        copyDisplay = fslsettings.read(copyDisplaySetting, False)
        copy4D = fslsettings.read(copy4DSetting, False)
        is4D = len(overlay.shape) > 3 and overlay.shape[3] > 1

        options.append(strings.messages['actions.copyoverlay.createMask'])
        states.append(createMask)

        options.append(strings.messages['actions.copyoverlay.copyDisplay'])
        states.append(copyDisplay)

        if is4D:
            options.append(strings.messages['actions.copyoverlay.copy4D'])
            states.append(copy4D)

        # Ask the user what they want to do
        dlg = fsldlg.CheckBoxMessageDialog(self.__frame,
                                           title=strings.actions[self],
                                           message='Copy {}'.format(
                                               display.name),
                                           cbMessages=options,
                                           cbStates=states,
                                           yesText='OK',
                                           cancelText='Cancel',
                                           focus='yes')

        if dlg.ShowModal() != wx.ID_YES:
            return

        createMask = dlg.CheckBoxState(0)
        copyDisplay = dlg.CheckBoxState(1)
        if is4D:
            copy4D = dlg.CheckBoxState(2)

        fslsettings.write(createMaskSetting, createMask)
        fslsettings.write(copyDisplaySetting, copyDisplay)
        if is4D:
            fslsettings.write(copy4DSetting, copy4D)

        copyImage(self.__overlayList,
                  self.__displayCtx,
                  overlay,
                  createMask=createMask,
                  copy4D=copy4D,
                  copyDisplay=copyDisplay)
示例#2
0
 def readDisplayNames(mapType):
     if mapType == 'cmap': key = 'fsleyes.colourmaps'
     elif mapType == 'lut': key = 'fsleyes.luts'
     return fslsettings.read(key, OrderedDict())
示例#3
0
    def __onLoadButton(self, ev):
        """Called when the *Load labels* button is pushed.  Prompts the user
        to select a label file to load, then does the following:

        1. If the selected label file refers to the currently selected
           melodic_IC overlay, the labels are applied to the overlay.

        2. If the selected label file refers to a different melodic_IC
           overlay, the user is asked whether they want to load the
           different melodic_IC file (the default), or whether they
           want the labels applied to the existing overlay.

        3. If the selected label file does not refer to any overlay
           (it only contains the bad component list), the user is asked
           whether they want the labels applied to the current melodic_IC
           overlay.

        If the number of labels in the file is less than the number of
        melodic_IC components, the remaining components are labelled
        as unknown. If the number of labels in the file is greater than
        the number of melodic_IC components, an error is shown, and
        nothing is done.
        """

        # The aim of the code beneath the
        # applyLabels function is to load
        # a set of component labels, and
        # to figure out which overlay
        # they should be added to.

        # When it has done this, it calls
        # applyLabels, which applies the
        # loaded labels to the overlay.
        def applyLabels(labelFile, overlay, allLabels, newOverlay):
            # labelFile:  Path to the loaded label file
            # overlay:    Overlay to apply them to
            # allLabels:  Loaded labels (list of (component, [label]) tuples)
            # newOverlay: True if the selected overlay has changed, False
            #             otherwise

            lut       = self.__lut
            volLabels = self.overlayList.getData(overlay, 'VolumeLabels')

            ncomps  = volLabels.numComponents()
            nlabels = len(allLabels)

            # Error: number of labels in the
            # file is greater than the number
            # of components in the overlay.
            if ncomps < nlabels:
                msg   = strings.messages[self, 'wrongNComps'].format(
                    labelFile, overlay.dataSource)
                title = strings.titles[  self, 'loadError']
                wx.MessageBox(msg, title, wx.ICON_ERROR | wx.OK)
                return

            # Number of labels in the file is
            # less than number of components
            # in the overlay - we pad the
            # labels with 'Unknown'
            elif ncomps > nlabels:
                for i in range(nlabels, ncomps):
                    allLabels.append(['Unknown'])

            # Disable notification while applying
            # labels so the component/label grids
            # don't confuse themselves.
            with volLabels.skip(self.__componentGrid.name), \
                 volLabels.skip(self.__labelGrid    .name):

                volLabels.clear()

                for comp, lbls in enumerate(allLabels):
                    for lbl in lbls:
                        volLabels.addLabel(comp, lbl)

                # Make sure a colour in the melodic
                # lookup table exists for all labels
                for label in volLabels.getAllLabels():

                    label    = volLabels.getDisplayLabel(label)
                    lutLabel = lut.getByName(label)

                    if lutLabel is None:
                        log.debug('New melodic classification '
                                  'label: {}'.format(label))
                        lut.new(label, colour=fslcm.randomBrightColour())

            # New overlay was loaded
            if newOverlay:

                # Make sure the new image is selected.
                with props.skip(self.displayCtx,
                                'selectedOverlay',
                                self.name):
                    self.displayCtx.selectOverlay(overlay)

                self.__componentGrid.setOverlay(overlay)
                self.__labelGrid    .setOverlay(overlay)

            # Labels were applied to
            # already selected overlay.
            else:
                self.__componentGrid.refreshTags()
                self.__labelGrid    .refreshTags()

        # If the current overlay is a compatible
        # Image, the open file dialog starting
        # point will be its directory.
        overlay          = self.displayCtx.getSelectedOverlay()
        selectedIsCompat = isinstance(overlay, fslimage.Image)

        if selectedIsCompat and overlay.dataSource is not None:
            loadDir = op.dirname(overlay.dataSource)

        # Otherwise it will be the most
        # recent overlay load directory.
        else:
            loadDir = fslsettings.read('loadSaveOverlayDir', os.getcwd())

        # Ask the user to select a label file
        dlg = wx.FileDialog(
            self,
            message=strings.titles[self, 'loadDialog'],
            defaultDir=loadDir,
            style=wx.FD_OPEN)

        # User cancelled the dialog
        if dlg.ShowModal() != wx.ID_OK:
            return

        # Load the specified label file
        filename = dlg.GetPath()
        emsg     = strings.messages[self, 'loadError'].format(filename)
        etitle   = strings.titles[  self, 'loadError']
        try:
            with status.reportIfError(msg=emsg, title=etitle):
                melDir, allLabels = fixlabels.loadLabelFile(filename)
        except:
            return

        # Ok we've got the labels, now
        # we need to figure out which
        # overlay to add them to.

        # If the label file does not refer
        # to a Melodic directory, and the
        # current overlay is a compatible
        # image, apply the labels to the
        # image.
        if selectedIsCompat and (melDir is None):
            applyLabels(filename, overlay, allLabels, False)
            return

        # If the label file refers to a
        # Melodic directory, and the
        # current overlay is a compatible
        # image.
        if selectedIsCompat and (melDir is not None):

            if isinstance(overlay, fslmelimage.MelodicImage):
                overlayDir = overlay.getMelodicDir()
            elif overlay.dataSource is not None:
                overlayDir = op.dirname(overlay.dataSource)
            else:
                overlayDir = 'none'

            # And both the current overlay and
            # the label file refer to the same
            # directory, then we apply the
            # labels to the curent overlay.
            if op.abspath(melDir) == op.abspath(overlayDir):

                applyLabels(filename, overlay, allLabels, False)
                return

            # Otherwise, if the overlay and the
            # label file refer to different
            # directories...

            # Ask the user whether they want to load
            # the image specified in the label file,
            # or apply the labels to the currently
            # selected image.
            dlg = wx.MessageDialog(
                self,
                strings.messages[self, 'diffMelDir'].format(
                    melDir, overlayDir),
                style=wx.ICON_QUESTION | wx.YES_NO | wx.CANCEL)
            dlg.SetYesNoLabels(
                strings.messages[self, 'diffMelDir.labels'],
                strings.messages[self, 'diffMelDir.overlay'])

            response = dlg.ShowModal()

            # User cancelled the dialog
            if response == wx.ID_CANCEL:
                return

            # User chose to load the melodic
            # image specified in the label
            # file. We'll carry on with this
            # processing below.
            elif response == wx.ID_YES:
                pass

            # Apply the labels to the current
            # overlay, even though they are
            # from different analyses.
            else:
                applyLabels(filename, overlay, allLabels, False)
                return

        # If we've reached this far, we are
        # going to attempt to identify the
        # image associated with the label
        # file, load that image, and then
        # apply the labels.

        # The label file does not
        # specify a melodic directory
        if melDir is None:

            msg   = strings.messages[self, 'noMelDir'].format(filename)
            title = strings.titles[  self, 'loadError']
            wx.MessageBox(msg, title, wx.ICON_ERROR | wx.OK)
            return

        # Try loading the melodic_IC image
        # specified in the label file.
        try:
            overlay = fslmelimage.MelodicImage(melDir)

            log.debug('Adding {} to overlay list'.format(overlay))

            with props.skip(self.overlayList, 'overlays',        self.name),\
                 props.skip(self.displayCtx,  'selectedOverlay', self.name):

                self.overlayList.append(overlay)

            if self.displayCtx.autoDisplay:
                autodisplay.autoDisplay(overlay,
                                        self.overlayList,
                                        self.displayCtx)

            fslsettings.write('loadSaveOverlayDir', op.abspath(melDir))

        except Exception as e:

            e     = str(e)
            msg   = strings.messages[self, 'loadError'].format(filename, e)
            title = strings.titles[  self, 'loadError']
            log.debug('Error loading classification file '
                      '({}), ({})'.format(filename, e), exc_info=True)
            wx.MessageBox(msg, title, wx.ICON_ERROR | wx.OK)

        # Apply the loaded labels
        # to the loaded overlay.
        applyLabels(filename, overlay, allLabels, True)
示例#4
0
    def __doScreenshot(self):
        """Capture a screenshot. Prompts the user to select a file to save
        the screenshot to, and then calls the :func:`screenshot` function.
        """

        lastDirSetting = 'fsleyes.actions.screenshot.lastDir'

        # Ask the user where they want
        # the screenshot to be saved
        fromDir = fslsettings.read(lastDirSetting, os.getcwd())

        # We can get a list of supported output
        # types via a matplotlib figure object
        fig = plt.figure()
        fmts = fig.canvas.get_supported_filetypes()

        # Default to png if
        # it is available
        if 'png' in fmts:
            fmts = [('png', fmts['png'])] + \
                   [(k, v) for k, v in fmts.items() if k is not 'png']
        else:
            fmts = list(fmts.items())

        wildcard = [
            '[*.{}] {}|*.{}'.format(fmt, desc, fmt) for fmt, desc in fmts
        ]
        wildcard = '|'.join(wildcard)
        filename = 'screenshot'

        dlg = wx.FileDialog(self.__panel,
                            message=strings.messages[self, 'screenshot'],
                            wildcard=wildcard,
                            defaultDir=fromDir,
                            defaultFile=filename,
                            style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)

        if dlg.ShowModal() != wx.ID_OK:
            return

        filename = dlg.GetPath()

        # Make the dialog go away before
        # the screenshot gets taken
        dlg.Close()
        dlg.Destroy()
        wx.GetApp().Yield()

        # Show an error if the screenshot
        # function raises an error
        doScreenshot = status.reportErrorDecorator(
            strings.titles[self, 'error'],
            strings.messages[self, 'error'])(screenshot)

        # We do the screenshot asynchronously,
        # to make sure it is performed on
        # the main thread, during idle time
        idle.idle(doScreenshot, self.__panel, filename)

        status.update(strings.messages[self, 'pleaseWait'].format(filename))

        fslsettings.write(lastDirSetting, op.dirname(filename))
示例#5
0
def loadDicom(dcmdir=None, parent=None, callback=None):
    """Does the following:

      1. Prompts the user to select a DICOM directory (unless
         ``dcmdir is not None``)

      2. Loads metadata about all of the data series in the
         DICOM directory

      3. Uses a :class:`.BrowseDicomDialog` to allow the user
         to choose which data series they wish to load

      4. Loads the selected series, and passes them to the
         ``callback`` function if it is provided.

    :arg dcmdir:   Directory to load DICOMs from. If not provided, the user is
                   prompted to select a directory.

    :arg parent:   ``wx`` parent object.

    :arg callback: Function which is passed the loaded DICOM series
                   (:class:`.Image` objects).
    """

    if parent is None:
        parent = wx.GetTopLevelWindows()[0]

    # 1. prompt user to select dicom directory
    if dcmdir is None:
        fromDir = fslsettings.read('loadSaveOverlayDir', os.getcwd())
        dlg = wx.DirDialog(parent,
                           message=strings.messages['loadDicom.selectDir'],
                           defaultPath=fromDir,
                           style=wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST)

        if dlg.ShowModal() != wx.ID_OK:
            return

        dcmdir = dlg.GetPath()

    # 2. load metadata about all data series in
    #    the DICOM directory. This is performed
    #    on a separate thread via the
    #    progress.runWithBounce function.
    series = []
    images = []

    def scan():
        try:
            series.extend(fsldcm.scanDir(dcmdir))
            if len(series) == 0:
                raise Exception('Could not find any DICOM '
                                'data series in {}'.format(dcmdir))

        except Exception as e:
            series.append(e)

    # 3. ask user which data series
    #    they want to load. This is called
    #    after the scan function has
    #    finished - see runWithBounce.
    def postScan(completed):

        # did the user cancel the progress dialog?
        if not completed:
            return

        # did an error occur in the scan step above?
        if isinstance(series[0], Exception):
            errTitle = strings.titles['loadDicom.scanError']
            errMsg = strings.messages['loadDicom.scanError']
            status.reportError(errTitle, errMsg, series[0])
            return

        dlg = BrowseDicomDialog(parent, series)
        dlg.CentreOnParent()

        if dlg.ShowModal() != wx.ID_OK:
            return

        # load the selected series - this is
        # done asynchronously via another
        # call to runWithBounce.
        for i in reversed(list(range(len(series)))):
            if not dlg.IsSelected(i):
                series.pop(i)

        title = strings.titles['loadDicom.loading']
        msg = strings.messages['loadDicom.loading']
        progress.runWithBounce(load, title, msg, callback=postLoad)

    # 4. Load the selected series. This is run
    #    on a separate thread via runWithBounce
    def load():
        try:
            for s in series:
                images.extend(fsldcm.loadSeries(s))

            if len(images) == 0:
                raise Exception('No images could be loaded '
                                'from {}'.format(dcmdir))

        except Exception as e:
            images.insert(0, e)

    # Pass the loaded images to the calback
    # function. This is called after the
    # load function has finished.
    def postLoad(completed):

        # Did the user cancel the progress dialog?
        if not completed:
            return

        # Did an error occur in the load step above?
        if isinstance(images[0], Exception):
            errTitle = strings.titles['loadDicom.loadError']
            errMsg = strings.messages['loadDicom.loadError']
            status.reportError(errTitle, errMsg, images[0])
            return

        fslsettings.write('loadSaveOverlayDir',
                          op.dirname(dcmdir.rstrip(op.sep)))

        if callback is not None:
            callback(images)

    # Kick off the process
    title = strings.titles['loadDicom.scanning']
    msg = strings.messages['loadDicom.scanning']
    progress.runWithBounce(scan, title, msg, callback=postScan)
示例#6
0
    def __doImport(self):

        import wx

        frame = wx.GetApp().GetTopWindow()

        # Ask the user where to get the data
        msg = strings.messages[self, 'selectFile']
        fromDir = fslsettings.read('loadSaveOverlayDir', os.getcwd())
        dlg = wx.FileDialog(frame,
                            message=msg,
                            defaultDir=fromDir,
                            style=wx.FD_OPEN)

        if dlg.ShowModal() != wx.ID_OK:
            return

        filePath = dlg.GetPath()
        fileName = op.basename(filePath)

        # Load the file, show an
        # error if it fails
        try:

            # Assuming that the data series
            # to plot are stored as columns
            data = np.loadtxt(filePath, dtype=np.float).T

            # Make sure the data is 2D, to
            # make code below easier and
            # happier.
            if len(data.shape) == 1:
                data = data.reshape((1, -1))

        except Exception as e:
            title = strings.titles[self, 'error']
            msg = strings.messages[self, 'error'].format(
                filePath, '{}: {}'.format(type(e).__name__, str(e)))

            wx.MessageBox(msg, title, wx.ICON_ERROR | wx.OK)
            return

        fslsettings.write('loadSaveOverlayDir', filePath)

        # Ask the user the x axis scaling factor.
        # If the currently selected overlay is
        # Nifti and 4D, default to its pixdim[3]
        overlay = self.displayCtx.getSelectedOverlay()

        if overlay is not None                 and \
           isinstance(overlay, fslimage.Nifti) and \
           len(overlay.shape) == 4             and \
           self.__plotPanel.usePixdim:
            xscale = overlay.pixdim[3]

        else:
            xscale = 1

        title = strings.titles[self, 'selectXScale']
        msg = strings.messages[self, 'selectXScale']

        # If the user pushes 'Ok', the entered value
        # is used as a fixed X axis interval. Otherwise,
        # it is assumed that the first column in the
        # file is the x axis data.
        dlg = numdlg.NumberDialog(frame,
                                  title=title,
                                  message=msg,
                                  initial=xscale,
                                  minValue=1e-5,
                                  cancelText=strings.labels[self,
                                                            'firstColumnIsX'])

        firstColumnIsX = dlg.ShowModal() != wx.ID_OK
        xscale = dlg.GetValue()

        # Add the data series
        series = []

        if firstColumnIsX:
            xdata = data[0, :]
            ydata = data[1:, :]
        else:
            xdata = np.arange(0,
                              data.shape[1] * xscale,
                              xscale,
                              dtype=np.float)
            ydata = data

        for i, ydata in enumerate(ydata):

            x = np.array(xdata)
            y = np.array(ydata)
            fin = np.isfinite(x) & np.isfinite(y)
            x = x[fin]
            y = y[fin]

            ds = plotting.DataSeries(None, self.overlayList, self.displayCtx,
                                     self.__plotPanel)
            ds.setData(x, y)

            # If we recognise the file name,
            # we can give it a useful label.
            label = strings.plotLabels.get('{}.{}'.format(fileName, i),
                                           '{} [{}]'.format(fileName, i))

            ds.label = label
            ds.lineWidth = 1
            ds.colour = fslcm.randomDarkColour()

            series.append(ds)

        self.__plotPanel.dataSeries.extend(series)
示例#7
0
def saveOverlay(overlay, display=None):
    """Saves the currently selected overlay (only if it is a :class:`.Image`),
    by a call to :meth:`.Image.save`. If a ``display`` is provided, the
    :attr:`.Display.name` may be updated to match the new overlay file name.

    :arg overlay: The :class:`.Image` overlay to save
    :arg display: The :class:`.Display` instance associated with the overlay.
    """

    import wx

    # TODO support for other overlay types
    if not isinstance(overlay, fslimage.Image):
        raise RuntimeError('Non-volumetric types not supported yet')

    # If this image has been loaded from a file,
    # ask the user whether they want to overwrite
    # that file, or save the image to a new file.
    #
    if overlay.dataSource is not None:

        # If the data source is not nifti (e.g.
        # mgz), we are not going to overwrite it,
        # so we don't ask.
        if fslimage.looksLikeImage(overlay.dataSource):

            msg = strings.messages['SaveOverlayAction.overwrite'].format(
                overlay.dataSource)
            title = strings.titles['SaveOverlayAction.overwrite'].format(
                overlay.dataSource)

            dlg = wx.MessageDialog(wx.GetTopLevelWindows()[0],
                                   message=msg,
                                   caption=title,
                                   style=(wx.ICON_WARNING | wx.YES_NO
                                          | wx.CANCEL | wx.NO_DEFAULT))
            dlg.SetYesNoCancelLabels(
                strings.labels['SaveOverlayAction.overwrite'],
                strings.labels['SaveOverlayAction.saveNew'],
                strings.labels['SaveOverlayAction.cancel'])

            response = dlg.ShowModal()

            # Cancel == cancel the save
            # Yes    == overwrite the existing file
            # No     == save to a new file (prompt the user for the file name)
            if response == wx.ID_CANCEL:
                return

            if response == wx.ID_YES:
                doSave(overlay)
                return

        fromDir = op.dirname(overlay.dataSource)
        filename = fslimage.removeExt(op.basename(overlay.dataSource))
        filename = '{}_copy'.format(filename)
    else:
        fromDir = fslsettings.read('loadSaveOverlayDir', os.getcwd())

        if display is not None: filename = display.name
        else: filename = overlay.name

    filename = filename.replace('/', '_')
    filename = filename.replace('\\', '_')

    # Ask the user where they
    # want to save the image
    msg = strings.titles['SaveOverlayAction.saveFile']
    dlg = wx.FileDialog(wx.GetApp().GetTopWindow(),
                        message=msg,
                        defaultDir=fromDir,
                        defaultFile=filename,
                        style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)

    if dlg.ShowModal() != wx.ID_OK:
        return

    # Make sure that the user chose a supported
    # extension. If not, use the default extension.
    savePath = dlg.GetPath()
    prefix, suffix = fslimage.splitExt(savePath)

    if suffix == '':
        savePath = '{}{}'.format(prefix, fslimage.defaultExt())

    oldPath = overlay.dataSource
    saveDir = op.dirname(savePath)

    if doSave(overlay, savePath):

        # Cache the save directory for next time.
        fslsettings.write('loadSaveOverlayDir', saveDir)

        # If image was in memory, or its old
        # name equalled the old datasource
        # base name, update its name.
        if oldPath is None or \
           fslimage.removeExt(op.basename(oldPath)) == overlay.name:

            overlay.name = fslimage.removeExt(op.basename(savePath))

            if display is not None:
                display.name = overlay.name
示例#8
0
    def __copyOverlay(self):
        """Creates a copy of the currently selected overlay, and inserts it
        into the :class:`.OverlayList`.
        """

        import wx

        overlay = self.displayCtx.getSelectedOverlay()

        if overlay is None:
            return

        # TODO support for other overlay types
        if type(overlay) != fslimage.Image:
            raise RuntimeError('Currently, only {} instances can be '
                               'copied'.format(fslimage.Image.__name__))

        display = self.displayCtx.getDisplay(overlay)

        # We ask the user questions four:
        #  - Copy data, or create an empty (a.k.a. mask) image?
        #  - Copy display settings?
        #  - For 4D, copy 4D, or just the current 3D volume?
        #  - For complex/RGB(A), copy as single channel, or multi-channel?
        #
        # Here we build a list of
        # questions and initial states.
        options = []
        states = []

        createMaskSetting = 'fsleyes.actions.copyoverlay.createMask'
        copyDisplaySetting = 'fsleyes.actions.copyoverlay.copyDisplay'
        copy4DSetting = 'fsleyes.actions.copyoverlay.copy4D'
        copyMultiSetting = 'fsleyes.actions.copyoverlay.copyMulti'

        createMask = fslsettings.read(createMaskSetting, False)
        copyDisplay = fslsettings.read(copyDisplaySetting, False)
        copy4D = fslsettings.read(copy4DSetting, False)
        copyMulti = fslsettings.read(copy4DSetting, True)
        is4D = len(overlay.shape) > 3 and overlay.shape[3] > 1
        isMulti = overlay.iscomplex or overlay.nvals > 1

        options.append(strings.messages['actions.copyoverlay.createMask'])
        states.append(createMask)

        options.append(strings.messages['actions.copyoverlay.copyDisplay'])
        states.append(copyDisplay)

        if is4D:
            options.append(strings.messages['actions.copyoverlay.copy4D'])
            states.append(copy4D)

        if isMulti:
            options.append(strings.messages['actions.copyoverlay.copyMulti'])
            states.append(copyMulti)

        # Ask the user what they want to do
        dlg = fsldlg.CheckBoxMessageDialog(self.__frame,
                                           title=strings.actions[self],
                                           message='Copy {}'.format(
                                               display.name),
                                           cbMessages=options,
                                           cbStates=states,
                                           yesText='OK',
                                           cancelText='Cancel',
                                           focus='yes')

        if dlg.ShowModal() != wx.ID_YES:
            return

        createMask = dlg.CheckBoxState(0)
        copyDisplay = dlg.CheckBoxState(1)
        if is4D: copy4D = dlg.CheckBoxState(2)
        if isMulti: copyMulti = dlg.CheckBoxState(3 if is4D else 2)

        fslsettings.write(createMaskSetting, createMask)
        fslsettings.write(copyDisplaySetting, copyDisplay)
        if is4D: fslsettings.write(copy4DSetting, copy4D)
        if isMulti: fslsettings.write(copyMultiSetting, copyMulti)

        # If the user de-selected copy all channels,
        # ask them which channel they want to copy
        channel = None
        if isMulti and (not copyMulti):
            if overlay.iscomplex:
                choices = ['real', 'imag']
            else:
                choices = ['R', 'G', 'B', 'A'][:overlay.nvals]

            labels = [strings.choices[self, 'component'][c] for c in choices]
            title = strings.titles['actions.copyoverlay.component']
            msg = strings.messages['actions.copyoverlay.component']
            dlg = wx.SingleChoiceDialog(self.__frame,
                                        msg,
                                        title,
                                        choices=labels)

            if dlg.ShowModal() != wx.ID_OK:
                return

            channel = choices[dlg.GetSelection()]

        copyImage(self.overlayList,
                  self.displayCtx,
                  overlay,
                  createMask=createMask,
                  copy4D=copy4D,
                  channel=channel,
                  copyDisplay=copyDisplay)
示例#9
0
def loadImage(dtype, path, inmem=False):
    """Called by the :func:`loadOverlays` function. Loads an overlay which
    is represented by an ``Image`` instance, or a sub-class of ``Image``.
    Depending upon the image size, the data may be loaded into memory or
    kept on disk, and the initial image data range may be calculated
    from the whole image, or from a sample.

    :arg dtype: Overlay type (``Image``, or a sub-class of ``Image``).
    :arg path:  Path to the overlay file.
    :arg inmem: If ``True``, ``Image`` overlays are loaded into memory.
    """

    rangethres = fslsettings.read('fsleyes.overlay.rangethres', 419430400)
    idxthres   = fslsettings.read('fsleyes.overlay.idxthres',   1073741824)

    # We're going to load the file
    # twice - first to get its
    # dimensions, and then for real.
    #
    # TODO It is annoying that you have to create a 'dtype'
    #      instance twice, as e.g. the MelodicImage does a
    #      bunch of extra stuff (e.g. loading component
    #      time courses) that don't need to be done. Maybe
    #      the path passed to this function could be resolved
    #      (e.g. ./filtered_func.ica/ turned into
    #      ./filtered_func.ica/melodic_IC) so that you can
    #      just create a fsl.data.Image, or a nib.Nifti1Image.
    image = dtype(path,
                  loadData=False,
                  calcRange=False,
                  indexed=False,
                  threaded=False)
    nbytes = np.prod(image.shape) * image.dtype.itemsize
    image  = None

    # If the file is compressed (gzipped),
    # index the file if its compressed size
    # is greater than the index threshold.
    indexed = nbytes > idxthres
    image   = dtype(path,
                    loadData=inmem,
                    calcRange=False,
                    indexed=indexed,
                    threaded=indexed)

    # If the image is bigger than the
    # index threshold, keep it on disk.
    if inmem or (not indexed):
        log.debug('Loading {} into memory'.format(path))
        image.loadData()
    else:
        log.debug('Keeping {} on disk'.format(path))

    # If the image size is less than the range
    # threshold, calculate the full data range
    # now. Otherwise calculate the data range
    # from a sample. This is handled by the
    # Image.calcRange method.
    image.calcRange(rangethres)

    return image