def test_picking_file_updates_default_image_directory_to_previously_used(
        image: Path, tmp_preferences):
    dialog = ImageFileDialog(None)
    dialog.selectFile(image.as_posix())

    QTimer.singleShot(SINGLE_SHOT_DELAY, dialog.accept)
    assert dialog.getSelectedPaths() == [image]

    with open(tmp_preferences, "rb") as f:
        assert pickle.load(f) == {
            dialog.preferences_group: {
                dialog.preferences_setting: image.as_posix()
            }
        }
    def _readDatasetInfo(self, infoGroup, projectFilePath, headless):
        # Unready datasets are represented with an empty group.
        if len(infoGroup) == 0:
            return None, False

        if "__class__" in infoGroup:
            info_class = self.InfoClassNames[infoGroup["__class__"][(
            )].decode("utf-8")]
        else:  # legacy support
            location = infoGroup["location"][()].decode("utf-8")
            if location == "FileSystem":  # legacy support: a lot of DatasetInfo types are saved as "FileSystem"
                filePath = infoGroup["filePath"][()].decode("utf-8")
                if isUrl(filePath):
                    info_class = UrlDatasetInfo
                elif isRelative(filePath):
                    info_class = RelativeFilesystemDatasetInfo
                else:
                    info_class = FilesystemDatasetInfo
            elif location == "ProjectInternal":
                info_class = ProjectInternalDatasetInfo
            else:
                info_class = PreloadedArrayDatasetInfo

        dirty = False
        try:
            datasetInfo = info_class.from_h5_group(infoGroup)
        except FileNotFoundError as e:
            if headless:
                return (DummyDatasetInfo.from_h5_group(infoGroup), True)

            from PyQt5.QtWidgets import QMessageBox
            from ilastik.widgets.ImageFileDialog import ImageFileDialog

            repaired_paths = []
            for missing_path in e.filename.split(os.path.pathsep):
                should_repair = QMessageBox.question(
                    None,
                    "Missing file",
                    (f"File {missing_path} could not be found "
                     "(maybe you moved either that file or the .ilp project file). "
                     "Would you like to look for it elsewhere?"),
                    QMessageBox.Yes | QMessageBox.No,
                )
                if should_repair == QMessageBox.No:
                    raise e
                paths = ImageFileDialog(None).getSelectedPaths()
                if not paths:
                    raise e
                dirty = True
                repaired_paths.extend([str(p) for p in paths])

            if "filePath" in infoGroup:
                del infoGroup["filePath"]
            infoGroup["filePath"] = os.path.pathsep.join(
                repaired_paths).encode("utf-8")
            datasetInfo = FilesystemDatasetInfo.from_h5_group(infoGroup)

        return datasetInfo, dirty
def test_picking_n5_json_file_returns_directory_path(tmp_n5_file: Path):
    dialog = ImageFileDialog(None)
    dialog.setDirectory(str(tmp_n5_file))
    dialog.selectFile("attributes.json")

    QTimer.singleShot(SINGLE_SHOT_DELAY, dialog.accept)
    assert dialog.getSelectedPaths() == [tmp_n5_file]
Exemple #4
0
 def addFiles(self, roleIndex, startingLaneNum=None):
     """
     The user clicked the "Add File" button.
     Ask him to choose a file (or several) and add them to both
       the GUI table and the top-level operator inputs.
     """
     # Launch the "Open File" dialog
     paths = ImageFileDialog(self).getSelectedPaths()
     self.addFileNames(paths, startingLaneNum, roleIndex)
 def select_files(self):
     preference_name = f"recent-dir-role-{self._role_name}"
     file_paths = ImageFileDialog(
         self,
         preferences_group="BatchProcessing",
         preferences_setting=preference_name).getSelectedPaths()
     if file_paths:
         self.clear()
         self.list_widget.addItems(map(str, file_paths))
def test_picking_file_updates_default_image_directory_to_previously_used(
        image: Path, tmp_preferences):
    dialog = ImageFileDialog(None)
    dialog.selectFile(image.as_posix())

    def handle_dialog():
        while not dialog.isVisible():
            QApplication.processEvents()

        dialog.accept()

    QTimer.singleShot(0, handle_dialog)
    assert dialog.getSelectedPaths() == [image]

    with open(tmp_preferences, "r") as f:
        assert json.load(f) == {
            "DataSelection": {
                "recent image": image.as_posix()
            }
        }
Exemple #7
0
        def handleImportLabelsAction():
            fileNames = ImageFileDialog(
                self,
                preferences_group="DataSelection",
                preferences_setting="recent image").getSelectedPaths()
            fileNames = list(map(str, fileNames))

            # For now, we require a single hdf5 file
            if len(fileNames) > 1:
                QMessageBox.critical(
                    self, "Too many files",
                    "Labels must be contained in a single hdf5 volume.")
                return
            if len(fileNames) == 0:
                # user cancelled
                return

            file_path = fileNames[0]
            internal_paths = DatasetInfo.getPossibleInternalPathsFor(file_path)
            if len(internal_paths) == 0:
                QMessageBox.critical(
                    self, "No volumes in file",
                    "Couldn't find a suitable dataset in your hdf5 file.")
                return
            if len(internal_paths) == 1:
                internal_path = internal_paths[0]
            else:
                dlg = SubvolumeSelectionDlg(internal_paths, self)
                if dlg.exec_() == QDialog.Rejected:
                    return
                selected_index = dlg.combo.currentIndex()
                internal_path = str(internal_paths[selected_index])

            path_components = PathComponents(file_path)
            path_components.internalPath = str(internal_path)

            try:
                top_op = self.topLevelOperatorView
                opReader = OpInputDataReader(parent=top_op.parent)
                opReader.FilePath.setValue(path_components.totalPath())

                # Reorder the axes
                op5 = OpReorderAxes(parent=top_op.parent)
                op5.AxisOrder.setValue(top_op.LabelInputs.meta.getAxisKeys())
                op5.Input.connect(opReader.Output)

                # Finally, import the labels
                top_op.importLabels(top_op.current_view_index(), op5.Output)

            finally:
                op5.cleanUp()
                opReader.cleanUp()
    def _readDatasetInfo(self, infoGroup, projectFilePath, headless):
        # Unready datasets are represented with an empty group.
        if len(infoGroup) == 0:
            return None, False

        if "__class__" in infoGroup:
            info_class = self.InfoClassNames[infoGroup["__class__"][(
            )].decode("utf-8")]
        else:  #legacy support
            location = infoGroup["location"][()].decode("utf-8")
            if location == "FileSystem" and isRelative(
                    infoGroup['filePath'][()].decode("utf-8")):
                info_class = RelativeFilesystemDatasetInfo
            else:
                info_class = self.LocationStrings[location]

        dirty = False
        try:
            datasetInfo = info_class.from_h5_group(infoGroup)
        except FileNotFoundError as e:
            if headless:
                shape = tuple(infoGroup["shape"])
                return PreloadedArrayDatasetInfo(preloaded_array=numpy.zeros(
                    shape, dtype=numpy.uint8)), True

            from PyQt5.QtWidgets import QMessageBox
            from ilastik.widgets.ImageFileDialog import ImageFileDialog

            repaired_paths = []
            for missing_path in e.filename.split(os.path.pathsep):
                should_repair = QMessageBox.question(
                    None,
                    "Missing file",
                    (f"File {missing_path} could not be found "
                     "(maybe you moved either that file or the .ilp project file). "
                     "Would you like to look for it elsewhere?"),
                    QMessageBox.Yes | QMessageBox.No,
                )
                if should_repair == QMessageBox.No:
                    raise e
                paths = ImageFileDialog(None).getSelectedPaths()
                if not paths:
                    raise e
                dirty = True
                repaired_paths.extend([str(p) for p in paths])
            infoGroup["filePath"] = os.path.pathsep.join(repaired_paths)
            datasetInfo = info_class.from_h5_group(infoGroup)

        return datasetInfo, dirty
def test_picking_n5_json_file_returns_directory_path(tmp_n5_file: Path):
    dialog = ImageFileDialog(None)
    dialog.setDirectory(str(tmp_n5_file))
    dialog.selectFile("attributes.json")

    def handle_dialog():
        while not dialog.isVisible():
            QApplication.processEvents()

        dialog.accept()

    QTimer.singleShot(0, handle_dialog)

    assert dialog.getSelectedPaths() == [tmp_n5_file]
Exemple #10
0
def import_labeling_layer(labelLayer, labelingSlots, parent_widget=None):
    """
    Prompt the user for layer import settings, and perform the layer import.
    :param labelLayer: The top label layer source
    :param labelingSlots: An instance of LabelingGui.LabelingSlots
    :param parent_widget: The Qt GUI parent object
    """
    writeSeeds = labelingSlots.labelInput
    assert isinstance(writeSeeds, lazyflow.graph.Slot), "slot is of type %r" % (type(writeSeeds))
    opLabels = writeSeeds.getRealOperator()
    assert isinstance(opLabels, lazyflow.graph.Operator), "slot's operator is of type %r" % (type(opLabels))

    fileNames = ImageFileDialog(
        parent_widget, preferences_group="labeling", preferences_setting="recently imported"
    ).getSelectedPaths()
    fileNames = list(map(str, fileNames))

    if not fileNames:
        return

    try:
        # Initialize operators
        opImport = OpInputDataReader(parent=opLabels.parent)
        opCache = OpBlockedArrayCache(parent=opLabels.parent)
        opMetadataInjector = OpMetadataInjector(parent=opLabels.parent)
        opReorderAxes = OpReorderAxes(parent=opLabels.parent)

        # Set up the pipeline as follows:
        #
        #   opImport --> (opCache) --> opMetadataInjector --------> opReorderAxes --(inject via setInSlot)--> labelInput
        #                             /                            /
        #     User-specified axisorder    labelInput.meta.axistags
        opImport.FilePath.setValue(fileNames[0] if len(fileNames) == 1 else os.path.pathsep.join(fileNames))
        assert opImport.Output.ready()

        maxLabels = len(labelingSlots.labelNames.value)

        # We don't bother with counting the label pixels
        # (and caching the data) if it's big (1 GB)
        if numpy.prod(opImport.Output.meta.shape) > 1e9:
            reading_slot = opImport.Output

            # For huge data, we don't go through and search for the pixel values,
            # because that takes an annoyingly long amount of time.
            # Instead, we make the reasonable assumption that the input labels are already 1,2,3..N
            # and we don't tell the user what the label pixel counts are.
            unique_read_labels = numpy.array(list(range(maxLabels + 1)))
            readLabelCounts = numpy.array([-1] * (maxLabels + 1))
            labelInfo = (maxLabels, (unique_read_labels, readLabelCounts))
        else:
            opCache.Input.connect(opImport.Output)
            opCache.CompressionEnabled.setValue(True)
            assert opCache.Output.ready()
            reading_slot = opCache.Output

            # We'll show a little window with a busy indicator while the data is loading
            busy_dlg = QProgressDialog(parent=parent_widget)
            busy_dlg.setLabelText("Scanning Label Data...")
            busy_dlg.setCancelButton(None)
            busy_dlg.setMinimum(100)
            busy_dlg.setMaximum(100)

            def close_busy_dlg(*args):
                QApplication.postEvent(busy_dlg, QCloseEvent())

            # Load the data from file into our cache
            # When it's done loading, close the progress dialog.
            req = reading_slot[:]
            req.notify_finished(close_busy_dlg)
            req.notify_failed(close_busy_dlg)
            req.submit()
            busy_dlg.exec_()

            readData = req.result

            # Can't use return_counts feature because that requires numpy >= 1.9
            # unique_read_labels, readLabelCounts = numpy.unique(readData, return_counts=True)

            # This does the same as the above, albeit slower, and probably with more ram.
            bincounts = chunked_bincount(readData)
            unique_read_labels = bincounts.nonzero()[0].astype(readData.dtype, copy=False)
            readLabelCounts = bincounts[unique_read_labels]

            labelInfo = (maxLabels, (unique_read_labels, readLabelCounts))
            del readData

        opMetadataInjector.Input.connect(reading_slot)
        metadata = reading_slot.meta.copy()
        opMetadataInjector.Metadata.setValue(metadata)
        opReorderAxes.Input.connect(opMetadataInjector.Output)

        # Transpose the axes for assignment to the labeling operator.
        opReorderAxes.AxisOrder.setValue(writeSeeds.meta.getAxisKeys())

        # Ask the user how to interpret the data.
        settingsDlg = LabelImportOptionsDlg(
            parent_widget, fileNames, opMetadataInjector.Output, labelingSlots.labelInput, labelInfo
        )

        def handle_updated_axes():
            # The user is specifying a new interpretation of the file's axes
            updated_axisorder = str(settingsDlg.axesEdit.text())
            metadata = opMetadataInjector.Metadata.value.copy()
            metadata.axistags = vigra.defaultAxistags(updated_axisorder)
            opMetadataInjector.Metadata.setValue(metadata)

            if opReorderAxes._invalid_axes:
                settingsDlg.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False)
                # Red background
                settingsDlg.axesEdit.setStyleSheet(
                    "QLineEdit { background: rgb(255, 128, 128);" "selection-background-color: rgb(128, 128, 255); }"
                )

        settingsDlg.axesEdit.editingFinished.connect(handle_updated_axes)

        # Initialize
        handle_updated_axes()

        dlg_result = settingsDlg.exec_()
        if dlg_result != LabelImportOptionsDlg.Accepted:
            return

        # Get user's chosen label mapping from dlg
        labelMapping = settingsDlg.labelMapping

        # Get user's chosen offsets, ordered by the 'write seeds' slot
        axes_5d = opReorderAxes.Output.meta.getAxisKeys()
        tagged_offsets = collections.OrderedDict(list(zip(axes_5d, [0] * len(axes_5d))))
        tagged_offsets.update(dict(list(zip(opReorderAxes.Output.meta.getAxisKeys(), settingsDlg.imageOffsets))))
        imageOffsets = list(tagged_offsets.values())

        # Optimization if mapping is identity
        if list(labelMapping.keys()) == list(labelMapping.values()):
            labelMapping = None

        # If the data was already cached, this will be fast.
        label_data = opReorderAxes.Output[:].wait()

        # Map input labels to output labels
        if labelMapping:
            # There are other ways to do a relabeling (e.g skimage.segmentation.relabel_sequential)
            # But this supports potentially huge values of unique_read_labels (in the billions),
            # without needing GB of RAM.
            mapping_indexes = numpy.searchsorted(unique_read_labels, label_data)
            new_labels = numpy.array([labelMapping[x] for x in unique_read_labels])
            label_data[:] = new_labels[mapping_indexes]

        label_roi = numpy.array(roiFromShape(opReorderAxes.Output.meta.shape))
        label_roi += imageOffsets
        label_slice = roiToSlice(*label_roi)
        writeSeeds[label_slice] = label_data

    finally:
        opReorderAxes.cleanUp()
        opMetadataInjector.cleanUp()
        opCache.cleanUp()
        opImport.cleanUp()
def test_default_image_directory_is_home_with_blank_preferences_file():
    dialog = ImageFileDialog(None)
    assert dialog.directory().absolutePath() == Path.home().as_posix()