Search in sources :

Example 26 with ImageData

use of qupath.lib.images.ImageData in project qupath by qupath.

the class ProjectBrowser method getPopup.

ContextMenu getPopup() {
    Action actionOpenImage = new Action("Open image", e -> qupath.openImageEntry(getSelectedEntry()));
    Action actionRemoveImage = new Action("Remove image(s)", e -> {
        Collection<ImageRow> imageRows = getSelectedImageRowsRecursive();
        Collection<ProjectImageEntry<BufferedImage>> entries = ProjectTreeRow.getEntries(imageRows);
        if (entries.isEmpty())
            return;
        // Don't allow us to remove any entries that are currently open (in any viewer)
        for (var viewer : qupath.getViewers()) {
            var imageData = viewer.getImageData();
            var entry = imageData == null ? null : getProject().getEntry(imageData);
            if (entry != null && entries.contains(entry)) {
                Dialogs.showErrorMessage("Remove project entries", "Please close all images you want to remove!");
                return;
            }
        }
        if (entries.size() == 1) {
            if (!Dialogs.showConfirmDialog("Remove project entry", "Remove " + entries.iterator().next().getImageName() + " from project?"))
                return;
        } else if (!Dialogs.showYesNoDialog("Remove project entries", String.format("Remove %d entries?", entries.size())))
            return;
        var result = Dialogs.showYesNoCancelDialog("Remove project entries", "Delete all associated data?");
        if (result == DialogButton.CANCEL)
            return;
        project.removeAllImages(entries, result == DialogButton.YES);
        refreshTree(null);
        syncProject(project);
        if (tree != null) {
            boolean isExpanded = tree.getRoot() != null && tree.getRoot().isExpanded();
            tree.setRoot(model.getRoot());
            tree.getRoot().setExpanded(isExpanded);
        }
    });
    Action actionDuplicateImages = new Action("Duplicate image(s)", e -> {
        Collection<ImageRow> imageRows = getSelectedImageRowsRecursive();
        if (imageRows.isEmpty()) {
            logger.debug("Nothing to duplicate - no entries selected");
            return;
        }
        boolean singleImage = false;
        String name = "";
        String title = "Duplicate images";
        String namePrompt = "Append to image name";
        String nameHelp = "Specify text to append to the image name to distinguish duplicated images";
        if (imageRows.size() == 1) {
            title = "Duplicate image";
            namePrompt = "Duplicate image name";
            nameHelp = "Specify name for the duplicated image";
            singleImage = true;
            name = imageRows.iterator().next().getDisplayableString();
            name = GeneralTools.generateDistinctName(name, project.getImageList().stream().map(p -> p.getImageName()).collect(Collectors.toSet()));
        }
        var params = new ParameterList().addStringParameter("name", namePrompt, name, nameHelp).addBooleanParameter("copyData", "Also duplicate data files", true, "Duplicate any associated data files along with the image");
        if (!Dialogs.showParameterDialog(title, params))
            return;
        boolean copyData = params.getBooleanParameterValue("copyData");
        name = params.getStringParameterValue("name");
        // Ensure we have a single space and then the text to append, with extra whitespace removed
        if (!singleImage && !name.isBlank())
            name = " " + name.strip();
        for (var imageRow : imageRows) {
            try {
                var newEntry = project.addDuplicate(ProjectTreeRow.getEntry(imageRow), copyData);
                if (newEntry != null && !name.isBlank()) {
                    if (singleImage)
                        newEntry.setImageName(name);
                    else
                        newEntry.setImageName(newEntry.getImageName() + name);
                }
            } catch (Exception ex) {
                Dialogs.showErrorNotification("Duplicating image", "Error duplicating " + ProjectTreeRow.getEntry(imageRow).getImageName());
                logger.error(ex.getLocalizedMessage(), ex);
            }
        }
        try {
            project.syncChanges();
        } catch (Exception ex) {
            logger.error("Error synchronizing project changes: " + ex.getLocalizedMessage(), ex);
        }
        refreshProject();
        if (imageRows.size() == 1)
            logger.debug("Duplicated 1 image entry");
        else
            logger.debug("Duplicated {} image entries");
    });
    Action actionSetImageName = new Action("Rename image", e -> {
        TreeItem<ProjectTreeRow> path = tree.getSelectionModel().getSelectedItem();
        if (path == null)
            return;
        if (path.getValue().getType() == ProjectTreeRow.Type.IMAGE) {
            if (setProjectEntryImageName(ProjectTreeRow.getEntry(path.getValue())) && project != null)
                syncProject(project);
        }
    });
    // Add a metadata value
    Action actionAddMetadataValue = new Action("Add metadata", e -> {
        Project<BufferedImage> project = getProject();
        Collection<ImageRow> imageRows = getSelectedImageRowsRecursive();
        if (project != null && !imageRows.isEmpty()) {
            TextField tfMetadataKey = new TextField();
            var suggestions = project.getImageList().stream().map(p -> p.getMetadataKeys()).flatMap(Collection::stream).distinct().sorted().collect(Collectors.toList());
            TextFields.bindAutoCompletion(tfMetadataKey, suggestions);
            TextField tfMetadataValue = new TextField();
            Label labKey = new Label("New key");
            Label labValue = new Label("New value");
            labKey.setLabelFor(tfMetadataKey);
            labValue.setLabelFor(tfMetadataValue);
            tfMetadataKey.setTooltip(new Tooltip("Enter the name for the metadata entry"));
            tfMetadataValue.setTooltip(new Tooltip("Enter the value for the metadata entry"));
            ProjectImageEntry<BufferedImage> entry = imageRows.size() == 1 ? ProjectTreeRow.getEntry(imageRows.iterator().next()) : null;
            int nMetadataValues = entry == null ? 0 : entry.getMetadataKeys().size();
            GridPane pane = new GridPane();
            pane.setVgap(5);
            pane.setHgap(5);
            pane.add(labKey, 0, 0);
            pane.add(tfMetadataKey, 1, 0);
            pane.add(labValue, 0, 1);
            pane.add(tfMetadataValue, 1, 1);
            String name = imageRows.size() + " images";
            if (entry != null) {
                name = entry.getImageName();
                if (nMetadataValues > 0) {
                    Label labelCurrent = new Label("Current metadata");
                    TextArea textAreaCurrent = new TextArea();
                    textAreaCurrent.setEditable(false);
                    String keyString = entry.getMetadataSummaryString();
                    if (keyString.isEmpty())
                        textAreaCurrent.setText("No metadata entries yet");
                    else
                        textAreaCurrent.setText(keyString);
                    textAreaCurrent.setPrefRowCount(3);
                    labelCurrent.setLabelFor(textAreaCurrent);
                    pane.add(labelCurrent, 0, 2);
                    pane.add(textAreaCurrent, 1, 2);
                }
            }
            Dialog<ButtonType> dialog = new Dialog<>();
            dialog.setTitle("Metadata");
            dialog.getDialogPane().getButtonTypes().setAll(ButtonType.OK, ButtonType.CANCEL);
            dialog.getDialogPane().setHeaderText("Set metadata for " + name);
            dialog.getDialogPane().setContent(pane);
            Optional<ButtonType> result = dialog.showAndWait();
            if (result.isPresent() && result.get() == ButtonType.OK) {
                String key = tfMetadataKey.getText().trim();
                String value = tfMetadataValue.getText();
                if (key.isEmpty()) {
                    logger.warn("Attempted to set metadata value for {}, but key was empty!", name);
                } else {
                    // Set metadata for all entries
                    for (var temp : imageRows) ProjectTreeRow.getEntry(temp).putMetadataValue(key, value);
                    syncProject(project);
                    tree.refresh();
                }
            }
        } else {
            Dialogs.showErrorMessage("Edit image description", "No entry is selected!");
        }
    });
    // Edit the description for the image
    Action actionEditDescription = new Action("Edit description", e -> {
        Project<?> project = getProject();
        ProjectImageEntry<?> entry = getSelectedEntry();
        if (project != null && entry != null) {
            if (showDescriptionEditor(entry)) {
                descriptionText.set(entry.getDescription());
                syncProject(project);
            }
        } else {
            Dialogs.showErrorMessage("Edit image description", "No entry is selected!");
        }
    });
    // Mask the name of the images and shuffle the entry
    Action actionMaskImageNames = ActionTools.createSelectableAction(PathPrefs.maskImageNamesProperty(), "Mask image names");
    // Refresh thumbnail according to current display settings
    Action actionRefreshThumbnail = new Action("Refresh thumbnail", e -> {
        TreeItem<ProjectTreeRow> path = tree.getSelectionModel().getSelectedItem();
        if (path == null)
            return;
        if (path.getValue().getType() == ProjectTreeRow.Type.IMAGE) {
            ProjectImageEntry<BufferedImage> entry = ProjectTreeRow.getEntry(path.getValue());
            if (!isCurrentImage(entry)) {
                logger.warn("Cannot refresh entry for image that is not open!");
                return;
            }
            BufferedImage imgThumbnail = qupath.getViewer().getRGBThumbnail();
            imgThumbnail = resizeForThumbnail(imgThumbnail);
            try {
                entry.setThumbnail(imgThumbnail);
            } catch (IOException e1) {
                logger.error("Error writing thumbnail", e1);
            }
            tree.refresh();
        }
    });
    // Open the project directory using Explorer/Finder etc.
    Action actionOpenProjectDirectory = createBrowsePathAction("Project...", () -> getProjectPath());
    Action actionOpenProjectEntryDirectory = createBrowsePathAction("Project entry...", () -> getProjectEntryPath());
    Action actionOpenImageServerDirectory = createBrowsePathAction("Image server...", () -> getImageServerPath());
    Menu menuSort = new Menu("Sort by...");
    ContextMenu menu = new ContextMenu();
    var hasProjectBinding = qupath.projectProperty().isNotNull();
    var menuOpenDirectories = MenuTools.createMenu("Open directory...", actionOpenProjectDirectory, actionOpenProjectEntryDirectory, actionOpenImageServerDirectory);
    menuOpenDirectories.visibleProperty().bind(hasProjectBinding);
    // MenuItem miOpenProjectDirectory = ActionUtils.createMenuItem(actionOpenProjectDirectory);
    MenuItem miOpenImage = ActionUtils.createMenuItem(actionOpenImage);
    MenuItem miRemoveImage = ActionUtils.createMenuItem(actionRemoveImage);
    MenuItem miDuplicateImage = ActionUtils.createMenuItem(actionDuplicateImages);
    MenuItem miSetImageName = ActionUtils.createMenuItem(actionSetImageName);
    MenuItem miRefreshThumbnail = ActionUtils.createMenuItem(actionRefreshThumbnail);
    MenuItem miEditDescription = ActionUtils.createMenuItem(actionEditDescription);
    MenuItem miAddMetadata = ActionUtils.createMenuItem(actionAddMetadataValue);
    MenuItem miMaskImages = ActionUtils.createCheckMenuItem(actionMaskImageNames);
    // Set visibility as menu being displayed
    menu.setOnShowing(e -> {
        TreeItem<ProjectTreeRow> selected = tree.getSelectionModel().getSelectedItem();
        ProjectImageEntry<BufferedImage> selectedEntry = selected == null ? null : ProjectTreeRow.getEntry(selected.getValue());
        var entries = getSelectedImageRowsRecursive();
        boolean isImageEntry = selectedEntry != null;
        // miOpenProjectDirectory.setVisible(project != null && project.getBaseDirectory().exists());
        miOpenImage.setVisible(isImageEntry);
        miDuplicateImage.setVisible(isImageEntry);
        miSetImageName.setVisible(isImageEntry);
        miAddMetadata.setVisible(!entries.isEmpty());
        miEditDescription.setVisible(isImageEntry);
        miRefreshThumbnail.setVisible(isImageEntry && isCurrentImage(selectedEntry));
        miRemoveImage.setVisible(selected != null && project != null && !project.getImageList().isEmpty());
        if (project == null) {
            menuSort.setVisible(false);
            return;
        }
        Map<String, MenuItem> newItems = new TreeMap<>();
        for (ProjectImageEntry<?> entry : project.getImageList()) {
            // Add all entry metadata keys
            for (String key : entry.getMetadataKeys()) {
                if (!newItems.containsKey(key))
                    newItems.put(key, ActionUtils.createMenuItem(createSortByKeyAction(key, key)));
            }
            // Add all additional keys
            for (String key : baseMetadataKeys) {
                if (!newItems.containsKey(key))
                    newItems.put(key, ActionUtils.createMenuItem(createSortByKeyAction(key, key)));
            }
        }
        menuSort.getItems().setAll(newItems.values());
        menuSort.getItems().add(0, ActionUtils.createMenuItem(createSortByKeyAction("None", null)));
        menuSort.getItems().add(1, new SeparatorMenuItem());
        menuSort.setVisible(true);
        if (menu.getItems().isEmpty())
            e.consume();
    });
    SeparatorMenuItem separator = new SeparatorMenuItem();
    separator.visibleProperty().bind(menuSort.visibleProperty());
    menu.getItems().addAll(miOpenImage, miRemoveImage, miDuplicateImage, new SeparatorMenuItem(), miSetImageName, miAddMetadata, miEditDescription, miMaskImages, miRefreshThumbnail, separator, menuSort);
    separator = new SeparatorMenuItem();
    separator.visibleProperty().bind(menuOpenDirectories.visibleProperty());
    if (Desktop.isDesktopSupported()) {
        menu.getItems().addAll(separator, menuOpenDirectories);
    }
    return menu;
}
Also used : Button(javafx.scene.control.Button) ImageServer(qupath.lib.images.servers.ImageServer) DoubleBinding(javafx.beans.binding.DoubleBinding) ActionUtils(org.controlsfx.control.action.ActionUtils) LoggerFactory(org.slf4j.LoggerFactory) RenderingHints(java.awt.RenderingHints) StackPane(javafx.scene.layout.StackPane) Side(javafx.geometry.Side) ParameterList(qupath.lib.plugins.parameters.ParameterList) MasterDetailPane(org.controlsfx.control.MasterDetailPane) ContextMenu(javafx.scene.control.ContextMenu) Map(java.util.Map) URI(java.net.URI) Path(java.nio.file.Path) QuPathGUI(qupath.lib.gui.QuPathGUI) Pane(javafx.scene.layout.Pane) TextField(javafx.scene.control.TextField) MenuItem(javafx.scene.control.MenuItem) BufferedImage(java.awt.image.BufferedImage) IconFactory(qupath.lib.gui.tools.IconFactory) Collection(java.util.Collection) Set(java.util.Set) Canvas(javafx.scene.canvas.Canvas) Collectors(java.util.stream.Collectors) Executors(java.util.concurrent.Executors) TreeView(javafx.scene.control.TreeView) Objects(java.util.Objects) Platform(javafx.application.Platform) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem) List(java.util.List) Project(qupath.lib.projects.Project) GuiTools(qupath.lib.gui.tools.GuiTools) Optional(java.util.Optional) ThreadTools(qupath.lib.common.ThreadTools) ObservableList(javafx.collections.ObservableList) BorderPane(javafx.scene.layout.BorderPane) StringProperty(javafx.beans.property.StringProperty) IntStream(java.util.stream.IntStream) TextArea(javafx.scene.control.TextArea) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) ButtonType(javafx.scene.control.ButtonType) TreeItem(javafx.scene.control.TreeItem) Action(org.controlsfx.control.action.Action) ImageRow(qupath.lib.gui.panes.ProjectTreeRow.ImageRow) FXCollections(javafx.collections.FXCollections) PathIcons(qupath.lib.gui.tools.IconFactory.PathIcons) Supplier(java.util.function.Supplier) Bindings(javafx.beans.binding.Bindings) HashSet(java.util.HashSet) Dialogs(qupath.lib.gui.dialogs.Dialogs) TextFields(org.controlsfx.control.textfield.TextFields) Insets(javafx.geometry.Insets) Graphics2D(java.awt.Graphics2D) ActionTools(qupath.lib.gui.ActionTools) Tooltip(javafx.scene.control.Tooltip) ExecutorService(java.util.concurrent.ExecutorService) GridPane(javafx.scene.layout.GridPane) ImageData(qupath.lib.images.ImageData) Desktop(java.awt.Desktop) KeyCode(javafx.scene.input.KeyCode) ObjectProperty(javafx.beans.property.ObjectProperty) Logger(org.slf4j.Logger) Dialog(javafx.scene.control.Dialog) Label(javafx.scene.control.Label) TitledPane(javafx.scene.control.TitledPane) ProjectImageEntry(qupath.lib.projects.ProjectImageEntry) GeneralTools(qupath.lib.common.GeneralTools) IOException(java.io.IOException) MetadataRow(qupath.lib.gui.panes.ProjectTreeRow.MetadataRow) ProjectCommands(qupath.lib.gui.commands.ProjectCommands) MenuTools(qupath.lib.gui.tools.MenuTools) Menu(javafx.scene.control.Menu) SelectionMode(javafx.scene.control.SelectionMode) TreeMap(java.util.TreeMap) Type(qupath.lib.gui.panes.ProjectTreeRow.Type) ImageView(javafx.scene.image.ImageView) SwingFXUtils(javafx.embed.swing.SwingFXUtils) TreeCell(javafx.scene.control.TreeCell) ObservableValue(javafx.beans.value.ObservableValue) ChangeListener(javafx.beans.value.ChangeListener) Collections(java.util.Collections) Image(javafx.scene.image.Image) PathPrefs(qupath.lib.gui.prefs.PathPrefs) ImageServerMetadata(qupath.lib.images.servers.ImageServerMetadata) DialogButton(qupath.lib.gui.dialogs.Dialogs.DialogButton) PaneTools(qupath.lib.gui.tools.PaneTools) Action(org.controlsfx.control.action.Action) ImageRow(qupath.lib.gui.panes.ProjectTreeRow.ImageRow) TextArea(javafx.scene.control.TextArea) Label(javafx.scene.control.Label) ContextMenu(javafx.scene.control.ContextMenu) BufferedImage(java.awt.image.BufferedImage) Dialog(javafx.scene.control.Dialog) TextField(javafx.scene.control.TextField) ProjectImageEntry(qupath.lib.projects.ProjectImageEntry) ContextMenu(javafx.scene.control.ContextMenu) Menu(javafx.scene.control.Menu) ButtonType(javafx.scene.control.ButtonType) GridPane(javafx.scene.layout.GridPane) Tooltip(javafx.scene.control.Tooltip) MenuItem(javafx.scene.control.MenuItem) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem) IOException(java.io.IOException) TreeMap(java.util.TreeMap) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem) IOException(java.io.IOException) ParameterList(qupath.lib.plugins.parameters.ParameterList)

Example 27 with ImageData

use of qupath.lib.images.ImageData in project qupath by qupath.

the class DefaultScriptEditor method createInsertAction.

Action createInsertAction(final String name) {
    Action action = new Action(name, e -> {
        var control = getCurrentTextComponent();
        String join = "," + System.lineSeparator() + "  ";
        String listFormat = "[" + System.lineSeparator() + "  %s" + System.lineSeparator() + "]";
        if (name.toLowerCase().equals("pixel classifiers")) {
            try {
                String classifiers = qupath.getProject().getPixelClassifiers().getNames().stream().map(classifierName -> "\"" + classifierName + "\"").collect(Collectors.joining(join));
                String s = classifiers.isEmpty() ? "[]" : String.format(listFormat, classifiers);
                control.paste(s);
            } catch (IOException ex) {
                logger.error("Could not fetch classifiers", ex.getLocalizedMessage());
            }
        } else if (name.toLowerCase().equals("object classifiers")) {
            try {
                String classifiers = qupath.getProject().getObjectClassifiers().getNames().stream().map(classifierName -> "\"" + classifierName + "\"").collect(Collectors.joining(join));
                String s = classifiers.isEmpty() ? "[]" : String.format(listFormat, classifiers);
                control.paste(s);
            } catch (IOException ex) {
                logger.error("Could not fetch classifiers", ex.getLocalizedMessage());
            }
        } else if (name.toLowerCase().equals("detection")) {
            var imageData = qupath.getImageData();
            String measurements = "";
            if (imageData != null) {
                measurements = imageData.getHierarchy().getDetectionObjects().stream().flatMap(d -> d.getMeasurementList().getMeasurementNames().stream()).distinct().map(m -> "\"" + m + "\"").collect(Collectors.joining(join));
            }
            String s = measurements.isEmpty() ? "[]" : String.format(listFormat, measurements);
            control.paste(s);
        } else if (name.toLowerCase().equals(GeneralTools.SYMBOL_MU + ""))
            control.paste(GeneralTools.SYMBOL_MU + "");
        else {
        // TODO: fix
        // // Imports (end with a new line)
        // if (name.toLowerCase().equals("qpex"))
        // control.insertText(0, "import static qupath.lib.gui.scripting.QPEx.*");
        // else if (name.toLowerCase().equals("qp"))
        // control.insertText(0, "import static qupath.lib.gui.scripting.QP.*");
        // else if (name.toLowerCase().equals("all default"))
        // control.insertText(0, QPEx.getDefaultImports(false));
        // currentLanguage.get().getSyntax().handleNewLine(control, smartEditing.get());
        }
        e.consume();
    });
    if (name.equals(GeneralTools.SYMBOL_MU + ""))
        action.setAccelerator(new KeyCodeCombination(KeyCode.M, KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN));
    else if (name.toLowerCase().equals("pixel classifiers") || name.toLowerCase().equals("object classifiers"))
        action.disabledProperty().bind(qupath.projectProperty().isNull());
    else if (name.toLowerCase().equals("detection"))
        action.disabledProperty().bind(qupath.imageDataProperty().isNull());
    return action;
}
Also used : StringBinding(javafx.beans.binding.StringBinding) BooleanBinding(javafx.beans.binding.BooleanBinding) Date(java.util.Date) ListCell(javafx.scene.control.ListCell) LoggerFactory(org.slf4j.LoggerFactory) KeyCombination(javafx.scene.input.KeyCombination) Future(java.util.concurrent.Future) Task(javafx.concurrent.Task) QuPathGUI(qupath.lib.gui.QuPathGUI) ScriptException(javax.script.ScriptException) PrintWriter(java.io.PrintWriter) Orientation(javafx.geometry.Orientation) SplitPane(javafx.scene.control.SplitPane) BufferedImage(java.awt.image.BufferedImage) Collection(java.util.Collection) Font(javafx.scene.text.Font) KeyEvent(javafx.scene.input.KeyEvent) GroovyLanguage(qupath.lib.gui.scripting.languages.GroovyLanguage) Collectors(java.util.stream.Collectors) RunnableLanguage(qupath.lib.gui.scripting.languages.RunnableLanguage) Objects(java.util.Objects) Platform(javafx.application.Platform) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem) List(java.util.List) BooleanProperty(javafx.beans.property.BooleanProperty) Project(qupath.lib.projects.Project) Clipboard(javafx.scene.input.Clipboard) Writer(java.io.Writer) Optional(java.util.Optional) Toggle(javafx.scene.control.Toggle) Pattern(java.util.regex.Pattern) BorderPane(javafx.scene.layout.BorderPane) LogManager(qupath.lib.gui.logging.LogManager) Scene(javafx.scene.Scene) RadioMenuItem(javafx.scene.control.RadioMenuItem) ListView(javafx.scene.control.ListView) TextArea(javafx.scene.control.TextArea) ButtonType(javafx.scene.control.ButtonType) Action(org.controlsfx.control.action.Action) ProgressDialog(org.controlsfx.dialog.ProgressDialog) SimpleScriptContext(javax.script.SimpleScriptContext) Bindings(javafx.beans.binding.Bindings) ProjectDialogs(qupath.lib.gui.dialogs.ProjectDialogs) TransferMode(javafx.scene.input.TransferMode) Projects(qupath.lib.projects.Projects) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) Dialogs(qupath.lib.gui.dialogs.Dialogs) ScriptLanguage(qupath.lib.gui.scripting.languages.ScriptLanguage) ActionTools(qupath.lib.gui.ActionTools) Callback(javafx.util.Callback) Tooltip(javafx.scene.control.Tooltip) LinkedHashSet(java.util.LinkedHashSet) ImageData(qupath.lib.images.ImageData) KeyCode(javafx.scene.input.KeyCode) ObjectProperty(javafx.beans.property.ObjectProperty) Modality(javafx.stage.Modality) Logger(org.slf4j.Logger) Dialog(javafx.scene.control.Dialog) MenuBar(javafx.scene.control.MenuBar) TitledPane(javafx.scene.control.TitledPane) ProjectImageEntry(qupath.lib.projects.ProjectImageEntry) GeneralTools(qupath.lib.common.GeneralTools) Node(javafx.scene.Node) IOException(java.io.IOException) StringEscapeUtils(org.apache.commons.text.StringEscapeUtils) MenuTools(qupath.lib.gui.tools.MenuTools) File(java.io.File) ScriptContext(javax.script.ScriptContext) Menu(javafx.scene.control.Menu) KeyCodeCombination(javafx.scene.input.KeyCodeCombination) ScriptLanguageProvider(qupath.lib.gui.scripting.languages.ScriptLanguageProvider) ActionEvent(javafx.event.ActionEvent) ToggleGroup(javafx.scene.control.ToggleGroup) Stage(javafx.stage.Stage) SimpleObjectProperty(javafx.beans.property.SimpleObjectProperty) ObservableValue(javafx.beans.value.ObservableValue) PlainLanguage(qupath.lib.gui.scripting.languages.PlainLanguage) PathPrefs(qupath.lib.gui.prefs.PathPrefs) DialogButton(qupath.lib.gui.dialogs.Dialogs.DialogButton) Action(org.controlsfx.control.action.Action) KeyCodeCombination(javafx.scene.input.KeyCodeCombination) IOException(java.io.IOException)

Example 28 with ImageData

use of qupath.lib.images.ImageData in project qupath by qupath.

the class DensityMapDataOp method apply.

@Override
public Mat apply(ImageData<BufferedImage> imageData, RegionRequest request) throws IOException {
    ensureInitialized();
    logger.trace("Applying density map op for {}", request);
    // Calculate how much padding we need
    var padding = op.getPadding();
    if (!padding.isEmpty()) {
        // Add padding to the request
        double downsample = request.getDownsample();
        var padding2 = Padding.getPadding((int) Math.round(padding.getX1() * downsample), (int) Math.round(padding.getX2() * downsample), (int) Math.round(padding.getY1() * downsample), (int) Math.round(padding.getY2() * downsample));
        request = request.pad2D(padding2);
    }
    // Get all objects within the padded region
    var allPathObjects = imageData.getHierarchy().getObjectsForRegion(null, request, null).stream().filter(allObjects).collect(Collectors.toList());
    if (allPathObjects.size() == 1)
        logger.trace("Generating counts tile for 1 object");
    else
        logger.trace("Generating counts tile for {} objects", allPathObjects.size());
    // Create an output mat
    int nChannels = getChannelCount();
    int width = (int) Math.round(request.getWidth() / request.getDownsample());
    int height = (int) Math.round(request.getHeight() / request.getDownsample());
    var mat = new Mat(height, width, opencv_core.CV_64FC(nChannels), Scalar.ZERO);
    DoubleIndexer idx = mat.createIndexer();
    // Get points representing all the centroids of each subpopulation of object
    // Use these to increment pixel values in a counts image
    int c = 0;
    for (var entry : primaryObjects.entrySet()) {
        var predicate = entry.getValue();
        var primaryROIs = allPathObjects.stream().filter(predicate).map(p -> PathObjectTools.getROI(p, true)).collect(Collectors.toList());
        var points = objectsToPoints(primaryROIs);
        incrementCounts(idx, points, request, width, height, c);
        c++;
    }
    // Get points for all objects, if we need them
    if (c < getChannelCount()) {
        var allObjectROIs = allPathObjects.stream().map(p -> PathObjectTools.getROI(p, true)).collect(Collectors.toList());
        var points = objectsToPoints(allObjectROIs);
        incrementCounts(idx, points, request, width, height, c);
        c++;
    }
    idx.close();
    // Now apply the op
    var output = this.op.apply(mat);
    return output;
}
Also used : IntStream(java.util.stream.IntStream) Arrays(java.util.Arrays) org.bytedeco.opencv.global.opencv_core(org.bytedeco.opencv.global.opencv_core) LoggerFactory(org.slf4j.LoggerFactory) ImageChannel(qupath.lib.images.servers.ImageChannel) ImageDataOp(qupath.opencv.ops.ImageDataOp) ArrayList(java.util.ArrayList) LinkedHashMap(java.util.LinkedHashMap) ImageOps(qupath.opencv.ops.ImageOps) Point2(qupath.lib.geom.Point2) DensityMapType(qupath.lib.analysis.heatmaps.DensityMaps.DensityMapType) Map(java.util.Map) Mat(org.bytedeco.opencv.opencv_core.Mat) Scalar(org.bytedeco.opencv.opencv_core.Scalar) URI(java.net.URI) DoubleIndexer(org.bytedeco.javacpp.indexer.DoubleIndexer) ImageData(qupath.lib.images.ImageData) Logger(org.slf4j.Logger) BufferedImage(java.awt.image.BufferedImage) RegionRequest(qupath.lib.regions.RegionRequest) PixelType(qupath.lib.images.servers.PixelType) Collection(java.util.Collection) ImageOp(qupath.opencv.ops.ImageOp) IOException(java.io.IOException) Padding(qupath.lib.regions.Padding) Collectors(java.util.stream.Collectors) PathObjectTools(qupath.lib.objects.PathObjectTools) Objects(java.util.Objects) ROI(qupath.lib.roi.interfaces.ROI) List(java.util.List) PathObjectPredicate(qupath.lib.objects.PathObjectPredicates.PathObjectPredicate) Collections(java.util.Collections) Mat(org.bytedeco.opencv.opencv_core.Mat) DoubleIndexer(org.bytedeco.javacpp.indexer.DoubleIndexer)

Example 29 with ImageData

use of qupath.lib.images.ImageData in project qupath by qupath.

the class OMEPyramidWriterCommand method run.

@Override
public void run() {
    if (currentTask != null && !currentTask.isDone()) {
        if (!Dialogs.showConfirmDialog("OME Pyramid writer", "Do you want to stop the current export?"))
            // TODO: Delete exporting file?
            return;
        else {
            currentTask.cancel(true);
        }
    }
    QuPathViewer viewer = qupath.getViewer();
    int zPos = viewer.getZPosition();
    int tPos = viewer.getTPosition();
    ImageData<BufferedImage> imageData = viewer.getImageData();
    if (imageData == null) {
        Dialogs.showNoImageError("OME Pyramid writer");
        return;
    }
    ImageServer<BufferedImage> server = imageData.getServer();
    // Region
    PathObject selected = imageData.getHierarchy().getSelectionModel().getSelectedObject();
    ImageRegion region = null;
    int width, height;
    if (selected == null || !selected.hasROI() || !selected.getROI().isArea()) {
        width = server.getWidth();
        height = server.getHeight();
    } else {
        region = ImageRegion.createInstance(selected.getROI());
        width = region.getWidth();
        height = region.getHeight();
    }
    // Set compression - with a sanity check for validity, defaulting to another comparable method if necessary
    CompressionType compression = getDefaultPyramidCompression();
    List<String> compatibleCompression = Arrays.stream(CompressionType.values()).filter(c -> c.supportsImage(server)).map(c -> c.toFriendlyString()).collect(Collectors.toList());
    if (!compatibleCompression.contains(compression.toFriendlyString()))
        compression = CompressionType.DEFAULT;
    var params = new ParameterList().addChoiceParameter("compression", "Compression type", compression.toFriendlyString(), compatibleCompression).addIntParameter("scaledDownsample", "Pyramidal downsample", scaledDownsample.get(), "", 1, 8, "Amount to downsample each consecutive pyramidal level; use 1 to indicate the image should not be pyramidal").addIntParameter("tileSize", "Tile size", getDefaultTileSize(), "px", "Tile size for export (should be between 128 and 8192)").addBooleanParameter("parallelize", "Parallelize export", parallelizeTiling.get(), "Export image tiles in parallel - " + "this should be faster, best keep it on unless you encounter export problems").addBooleanParameter("allZ", "All z-slices", allZ.get(), "Include all z-slices in the stack").addBooleanParameter("allT", "All timepoints", allT.get(), "Include all timepoints in the time-series");
    boolean singleTile = server.getTileRequestManager().getTileRequests(RegionRequest.createInstance(server)).size() == 1;
    params.setHiddenParameters(server.nZSlices() == 1, "allZ");
    params.setHiddenParameters(server.nTimepoints() == 1, "allT");
    params.setHiddenParameters(singleTile, "tileSize", "parallelize");
    if (!Dialogs.showParameterDialog("Export OME-TIFF", params))
        return;
    compression = CompressionType.fromFriendlyString((String) params.getChoiceParameterValue("compression"));
    defaultPyramidCompression.set(compression);
    int downsampleScale = params.getIntParameterValue("scaledDownsample");
    scaledDownsample.set(downsampleScale);
    int tileSize = params.getIntParameterValue("tileSize");
    boolean parallelize = params.getBooleanParameterValue("parallelize");
    if (!singleTile) {
        tileSize = GeneralTools.clipValue(tileSize, 128, 8192);
        defaultTileSize.set(tileSize);
        parallelizeTiling.set(parallelize);
    }
    boolean doAllZ = false;
    boolean doAllT = false;
    if (server.nZSlices() > 1) {
        doAllZ = params.getBooleanParameterValue("allZ");
        allZ.set(doAllZ);
    }
    if (server.nTimepoints() > 1) {
        doAllT = params.getBooleanParameterValue("allT");
        allT.set(doAllT);
    }
    OMEPyramidWriter.Builder builder = new OMEPyramidWriter.Builder(server);
    if (region != null) {
        builder = builder.region(region);
    } else {
        if (server.nZSlices() > 1 && !doAllZ)
            builder.zSlice(zPos);
        if (server.nTimepoints() > 1 && !doAllT)
            builder.timePoint(tPos);
    }
    builder.compression(compression);
    if (downsampleScale <= 1 || Math.max(width, height) / server.getDownsampleForResolution(0) < minSizeForTiling.get())
        builder.downsamples(server.getDownsampleForResolution(0));
    else
        builder.scaledDownsampling(server.getDownsampleForResolution(0), downsampleScale);
    // Set tile size; if we just have one tile, use the image width & height
    if (singleTile)
        builder = builder.tileSize(width, height);
    else
        builder = builder.tileSize(tileSize).parallelize(parallelize);
    if (server.nZSlices() > 1 && doAllZ)
        builder.allZSlices();
    if (server.nTimepoints() > 1 && doAllT)
        builder.allTimePoints();
    // Prompt for file
    File fileOutput = Dialogs.promptToSaveFile("Write pyramid", null, null, "OME TIFF pyramid", ".ome.tif");
    if (fileOutput == null)
        return;
    String name = fileOutput.getName();
    // We can have trouble with only the '.tif' part of the name being included
    if (name.endsWith(".tif") && !name.endsWith(".ome.tif"))
        fileOutput = new File(fileOutput.getParentFile(), name.substring(0, name.length() - 4) + ".ome.tif");
    OMEPyramidSeries writer = builder.build();
    if (pool == null) {
        pool = Executors.newSingleThreadExecutor(ThreadTools.createThreadFactory("ome-pyramid-export", false));
    }
    currentTask = pool.submit(new WriterTask(OMEPyramidWriter.createWriter(writer), fileOutput.getAbsolutePath()));
}
Also used : Arrays(java.util.Arrays) ImageServer(qupath.lib.images.servers.ImageServer) LoggerFactory(org.slf4j.LoggerFactory) IntegerProperty(javafx.beans.property.IntegerProperty) Dialogs(qupath.lib.gui.dialogs.Dialogs) Future(java.util.concurrent.Future) ParameterList(qupath.lib.plugins.parameters.ParameterList) OMEPyramidSeries(qupath.lib.images.writers.ome.OMEPyramidWriter.OMEPyramidSeries) ImageRegion(qupath.lib.regions.ImageRegion) CompressionType(qupath.lib.images.writers.ome.OMEPyramidWriter.CompressionType) ExecutorService(java.util.concurrent.ExecutorService) QuPathGUI(qupath.lib.gui.QuPathGUI) ImageData(qupath.lib.images.ImageData) ObjectProperty(javafx.beans.property.ObjectProperty) Logger(org.slf4j.Logger) BufferedImage(java.awt.image.BufferedImage) GeneralTools(qupath.lib.common.GeneralTools) RegionRequest(qupath.lib.regions.RegionRequest) Collectors(java.util.stream.Collectors) File(java.io.File) Executors(java.util.concurrent.Executors) PathObject(qupath.lib.objects.PathObject) ClosedByInterruptException(java.nio.channels.ClosedByInterruptException) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer) List(java.util.List) BooleanProperty(javafx.beans.property.BooleanProperty) ThreadTools(qupath.lib.common.ThreadTools) PathPrefs(qupath.lib.gui.prefs.PathPrefs) ImageRegion(qupath.lib.regions.ImageRegion) BufferedImage(java.awt.image.BufferedImage) PathObject(qupath.lib.objects.PathObject) OMEPyramidSeries(qupath.lib.images.writers.ome.OMEPyramidWriter.OMEPyramidSeries) ParameterList(qupath.lib.plugins.parameters.ParameterList) CompressionType(qupath.lib.images.writers.ome.OMEPyramidWriter.CompressionType) File(java.io.File) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer)

Example 30 with ImageData

use of qupath.lib.images.ImageData in project qupath by qupath.

the class PathClassPane method selectObjectsByClassification.

/**
 * Select objects by classification, logging the step (if performed) in the history workflow.
 * @param imageData the {@link ImageData} containing objects to be selected
 * @param pathClasses classifications that will result in an object being selected
 * @return true if a selection command was run, false otherwise (e.g. if no pathClasses were specified)
 */
public static boolean selectObjectsByClassification(ImageData<?> imageData, PathClass... pathClasses) {
    var hierarchy = imageData.getHierarchy();
    if (pathClasses.length == 0) {
        logger.warn("Cannot select objects by classification - no classifications selected!");
        return false;
    }
    QP.selectObjectsByPathClass(hierarchy, pathClasses);
    var s = Arrays.stream(pathClasses).map(p -> p == null || p == PathClassFactory.getPathClassUnclassified() ? "null" : "\"" + p.toString() + "\"").collect(Collectors.joining(", "));
    imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Select objects by classification", "selectObjectsByClassification(" + s + ");"));
    return true;
}
Also used : Button(javafx.scene.control.Button) Arrays(java.util.Arrays) ImageServer(qupath.lib.images.servers.ImageServer) ListCell(javafx.scene.control.ListCell) ActionUtils(org.controlsfx.control.action.ActionUtils) LoggerFactory(org.slf4j.LoggerFactory) Side(javafx.geometry.Side) ContextMenu(javafx.scene.control.ContextMenu) Map(java.util.Map) StandardPathClasses(qupath.lib.objects.classes.PathClassFactory.StandardPathClasses) QuPathGUI(qupath.lib.gui.QuPathGUI) Pane(javafx.scene.layout.Pane) TextField(javafx.scene.control.TextField) MenuItem(javafx.scene.control.MenuItem) BufferedImage(java.awt.image.BufferedImage) Predicate(java.util.function.Predicate) Set(java.util.Set) Rectangle(javafx.scene.shape.Rectangle) KeyEvent(javafx.scene.input.KeyEvent) Collectors(java.util.stream.Collectors) PathObject(qupath.lib.objects.PathObject) Platform(javafx.application.Platform) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer) Priority(javafx.scene.layout.Priority) List(java.util.List) BooleanProperty(javafx.beans.property.BooleanProperty) Project(qupath.lib.projects.Project) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) ProjectIO(qupath.lib.projects.ProjectIO) ToggleButton(javafx.scene.control.ToggleButton) Clipboard(javafx.scene.input.Clipboard) DataFormat(javafx.scene.input.DataFormat) GuiTools(qupath.lib.gui.tools.GuiTools) ColorToolsFX(qupath.lib.gui.tools.ColorToolsFX) ObservableList(javafx.collections.ObservableList) BorderPane(javafx.scene.layout.BorderPane) StringProperty(javafx.beans.property.StringProperty) ListView(javafx.scene.control.ListView) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) Action(org.controlsfx.control.action.Action) PathClassFactory(qupath.lib.objects.classes.PathClassFactory) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Bindings(javafx.beans.binding.Bindings) ArrayList(java.util.ArrayList) Dialogs(qupath.lib.gui.dialogs.Dialogs) Insets(javafx.geometry.Insets) Tooltip(javafx.scene.control.Tooltip) GridPane(javafx.scene.layout.GridPane) ColorPicker(javafx.scene.control.ColorPicker) ImageData(qupath.lib.images.ImageData) KeyCode(javafx.scene.input.KeyCode) Color(javafx.scene.paint.Color) Logger(org.slf4j.Logger) Label(javafx.scene.control.Label) PathClass(qupath.lib.objects.classes.PathClass) MenuTools(qupath.lib.gui.tools.MenuTools) File(java.io.File) OverlayOptions(qupath.lib.gui.viewer.OverlayOptions) Menu(javafx.scene.control.Menu) KeyCodeCombination(javafx.scene.input.KeyCodeCombination) SimpleBooleanProperty(javafx.beans.property.SimpleBooleanProperty) SelectionMode(javafx.scene.control.SelectionMode) QP(qupath.lib.scripting.QP) Collections(java.util.Collections) PathPrefs(qupath.lib.gui.prefs.PathPrefs) DialogButton(qupath.lib.gui.dialogs.Dialogs.DialogButton) PaneTools(qupath.lib.gui.tools.PaneTools) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep)

Aggregations

ImageData (qupath.lib.images.ImageData)32 BufferedImage (java.awt.image.BufferedImage)27 Collectors (java.util.stream.Collectors)26 Logger (org.slf4j.Logger)26 LoggerFactory (org.slf4j.LoggerFactory)26 List (java.util.List)25 ArrayList (java.util.ArrayList)23 IOException (java.io.IOException)21 PathObject (qupath.lib.objects.PathObject)21 File (java.io.File)19 Collection (java.util.Collection)19 Dialogs (qupath.lib.gui.dialogs.Dialogs)19 ImageServer (qupath.lib.images.servers.ImageServer)19 Collections (java.util.Collections)17 Map (java.util.Map)17 GeneralTools (qupath.lib.common.GeneralTools)17 QuPathGUI (qupath.lib.gui.QuPathGUI)17 Arrays (java.util.Arrays)16 PathPrefs (qupath.lib.gui.prefs.PathPrefs)15 Bindings (javafx.beans.binding.Bindings)14