Exemple #1
0
def genComponentIndexList(comps, ncomps):
    """Turns the given sequence of integers and file paths into a list
    of 0-based component indices.

    :arg comps:  Sequence containing 1-based component indices, and/or paths
                 to FIX/AROMA label text files.

    :arg ncomps: Number of components in the input data - indices larger than
                 this will be ignored.

    :returns:    List of 0-based component indices.
    """

    allcomps = []

    for c in comps:
        if isinstance(c, int):
            ccomps = [c]
        else:
            ccomps = fixlabels.loadLabelFile(c, returnIndices=True)[2]

        allcomps.extend([c - 1 for c in ccomps])

    if any([c < 0 or c >= ncomps for c in allcomps]):
        raise ValueError('Invalid component indices: {}'.format(allcomps))

    return list(sorted(set(allcomps)))
Exemple #2
0
    def load(self, filename):
        """Loads component labels from the specified file. See the
        :func:`.fixlabels.loadLabelFile` function.

        .. note:: This method adds to, but does not replace, any existing
                  component classifications stored by this
                  ``VolumeLabels`` instance. Call the :meth:`clear` method,
                  before calling ``load``, if you want to discard any existing
                  classifications.
        """

        # Read the labels in
        _, allLabels = fixlabels.loadLabelFile(filename)

        # More labels in the file than there are in
        # this labels instance - that doesn't make
        # any sense.
        if len(allLabels) > self.__ncomps:
            raise fixlabels.InvalidLabelFileError(
                'Wrong number of components in {}!'.format(filename))

        # Less labels in the file than there are in
        # this labels instance - this is ok, as the
        # file may have only contained a list of
        # noisy components. We'll label the remaining
        # components as 'Unknown'.
        elif len(allLabels) < self.__ncomps:
            for i in range(len(allLabels), self.__ncomps):
                allLabels.append(['Unknown'])

        # Add the labels to this object
        with self.skipAll(topic='added'):
            for i, labels in enumerate(allLabels):
                for label in labels:
                    self.addLabel(i, label)
Exemple #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.__overlay

        if overlay is not None 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 Exception:
            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 overlay is not None 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 overlay is not None 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)