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]
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() } }
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]
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()