Search in sources :

Example 1 with QuPathGUI

use of qupath.lib.gui.QuPathGUI in project qupath by qupath.

the class ProjectImportImagesCommand method promptToImportImages.

/**
 * Prompt to import images to the current project.
 *
 * @param qupath QuPath instance, used to access the current project and stage
 * @param builder if not null, this will be used to create the servers. If null, a combobox will be shown to choose an installed builder.
 * @param defaultPaths URIs to use to prepopulate the list
 * @return
 */
static List<ProjectImageEntry<BufferedImage>> promptToImportImages(QuPathGUI qupath, ImageServerBuilder<BufferedImage> builder, String... defaultPaths) {
    var project = qupath.getProject();
    if (project == null) {
        Dialogs.showNoProjectError(commandName);
        return Collections.emptyList();
    }
    ListView<String> listView = new ListView<>();
    listView.setPrefWidth(480);
    listView.setMinHeight(100);
    listView.getItems().addAll(defaultPaths);
    listView.setPlaceholder(new Label("Drag & drop image or project files for import, \nor choose from the options below"));
    Button btnFile = new Button("Choose files");
    btnFile.setOnAction(e -> loadFromFileChooser(listView.getItems()));
    Button btnURL = new Button("Input URL");
    btnURL.setOnAction(e -> loadFromSingleURL(listView.getItems()));
    Button btnClipboard = new Button("From clipboard");
    btnClipboard.setOnAction(e -> loadFromClipboard(listView.getItems()));
    Button btnFileList = new Button("From path list");
    btnFileList.setOnAction(e -> loadFromTextFile(listView.getItems()));
    TitledPane paneList = new TitledPane("Image paths", listView);
    paneList.setCollapsible(false);
    BorderPane paneImages = new BorderPane();
    class BuilderListCell extends ListCell<ImageServerBuilder<BufferedImage>> {

        @Override
        protected void updateItem(ImageServerBuilder<BufferedImage> item, boolean empty) {
            super.updateItem(item, empty);
            if (empty) {
                setText(null);
            } else {
                if (item == null)
                    setText("Default (let QuPath decide)");
                else
                    setText(item.getName());
            }
        }
    }
    boolean requestBuilder = builder == null;
    ComboBox<ImageServerBuilder<BufferedImage>> comboBuilder = new ComboBox<>();
    Label labelBuilder = new Label("Image provider");
    if (requestBuilder) {
        comboBuilder.setCellFactory(p -> new BuilderListCell());
        comboBuilder.setButtonCell(new BuilderListCell());
        List<ImageServerBuilder<BufferedImage>> availableBuilders = new ArrayList<>(ImageServerProvider.getInstalledImageServerBuilders(BufferedImage.class));
        if (!availableBuilders.contains(null))
            availableBuilders.add(0, null);
        comboBuilder.getItems().setAll(availableBuilders);
        comboBuilder.getSelectionModel().selectFirst();
        labelBuilder.setLabelFor(comboBuilder);
        labelBuilder.setMinWidth(Label.USE_PREF_SIZE);
    }
    ComboBox<ImageType> comboType = new ComboBox<>();
    comboType.getItems().setAll(ImageType.values());
    Label labelType = new Label("Set image type");
    labelType.setLabelFor(comboType);
    labelType.setMinWidth(Label.USE_PREF_SIZE);
    ComboBox<Rotation> comboRotate = new ComboBox<>();
    comboRotate.getItems().setAll(Rotation.values());
    Label labelRotate = new Label("Rotate image");
    labelRotate.setLabelFor(comboRotate);
    labelRotate.setMinWidth(Label.USE_PREF_SIZE);
    TextField tfArgs = new TextField();
    Label labelArgs = new Label("Optional args");
    labelArgs.setLabelFor(tfArgs);
    labelArgs.setMinWidth(Label.USE_PREF_SIZE);
    CheckBox cbPyramidalize = new CheckBox("Auto-generate pyramids");
    cbPyramidalize.setSelected(pyramidalizeProperty.get());
    CheckBox cbImportObjects = new CheckBox("Import objects");
    cbImportObjects.setSelected(importObjectsProperty.get());
    PaneTools.setMaxWidth(Double.MAX_VALUE, comboBuilder, comboType, comboRotate, cbPyramidalize, cbImportObjects, tfArgs);
    PaneTools.setFillWidth(Boolean.TRUE, comboBuilder, comboType, comboRotate, cbPyramidalize, cbImportObjects, tfArgs);
    PaneTools.setHGrowPriority(Priority.ALWAYS, comboBuilder, comboType, comboRotate, cbPyramidalize, cbImportObjects, tfArgs);
    GridPane paneType = new GridPane();
    paneType.setPadding(new Insets(5));
    paneType.setHgap(5);
    paneType.setVgap(5);
    int row = 0;
    if (requestBuilder)
        PaneTools.addGridRow(paneType, row++, 0, "Specify the library used to open images", labelBuilder, comboBuilder);
    PaneTools.addGridRow(paneType, row++, 0, "Specify the default image type for all images being imported (required for analysis, can be changed later under the 'Image' tab)", labelType, comboType);
    PaneTools.addGridRow(paneType, row++, 0, "Optionally rotate images on import", labelRotate, comboRotate);
    PaneTools.addGridRow(paneType, row++, 0, "Optionally pass reader-specific arguments to the image provider.\nUsually this should just be left empty.", labelArgs, tfArgs);
    PaneTools.addGridRow(paneType, row++, 0, "Dynamically create image pyramids for large, single-resolution images", cbPyramidalize, cbPyramidalize);
    PaneTools.addGridRow(paneType, row++, 0, "Read and import objects (e.g. annotations) from the image file, if possible", cbImportObjects, cbImportObjects);
    paneImages.setCenter(paneList);
    paneImages.setBottom(paneType);
    // TilePane paneButtons = new TilePane();
    // paneButtons.getChildren().addAll(btnFile, btnURL, btnClipboard, btnFileList);
    GridPane paneButtons = PaneTools.createColumnGridControls(btnFile, btnURL, btnClipboard, btnFileList);
    paneButtons.setHgap(5);
    paneButtons.setPadding(new Insets(5));
    BorderPane pane = new BorderPane();
    pane.setCenter(paneImages);
    pane.setBottom(paneButtons);
    // Support drag & drop for files
    pane.setOnDragOver(e -> {
        e.acceptTransferModes(TransferMode.COPY);
        e.consume();
    });
    pane.setOnDragDropped(e -> {
        Dragboard dragboard = e.getDragboard();
        if (dragboard.hasFiles()) {
            logger.trace("Files dragged onto project import dialog");
            try {
                var paths = dragboard.getFiles().stream().filter(f -> f.isFile() && !f.isHidden()).map(f -> f.getAbsolutePath()).collect(Collectors.toList());
                paths.removeAll(listView.getItems());
                if (!paths.isEmpty())
                    listView.getItems().addAll(paths);
            } catch (Exception ex) {
                Dialogs.showErrorMessage("Drag & Drop", ex);
            }
        }
        e.setDropCompleted(true);
        e.consume();
    });
    Dialog<ButtonType> dialog = new Dialog<>();
    dialog.setResizable(true);
    dialog.initOwner(qupath.getStage());
    dialog.setTitle("Import images to project");
    ButtonType typeImport = new ButtonType("Import", ButtonData.OK_DONE);
    dialog.getDialogPane().getButtonTypes().addAll(typeImport, ButtonType.CANCEL);
    ScrollPane scroll = new ScrollPane(pane);
    scroll.setFitToHeight(true);
    scroll.setFitToWidth(true);
    dialog.getDialogPane().setContent(scroll);
    Optional<ButtonType> result = dialog.showAndWait();
    if (!result.isPresent() || result.get() != typeImport)
        return Collections.emptyList();
    // // Do the actual import
    // List<String> pathSucceeded = new ArrayList<>();
    // List<String> pathFailed = new ArrayList<>();
    // for (String path : listView.getItems()) {
    // if (qupath.getProject().addImage(path.trim()))
    // pathSucceeded.add(path);
    // else
    // pathFailed.add(path);
    // }
    ImageType type = comboType.getValue();
    Rotation rotation = comboRotate.getValue();
    boolean pyramidalize = cbPyramidalize.isSelected();
    boolean importObjects = cbImportObjects.isSelected();
    pyramidalizeProperty.set(pyramidalize);
    importObjectsProperty.set(importObjects);
    ImageServerBuilder<BufferedImage> requestedBuilder = requestBuilder ? comboBuilder.getSelectionModel().getSelectedItem() : builder;
    List<String> argsList = new ArrayList<>();
    String argsString = tfArgs.getText();
    // TODO: Use a smarter approach to splitting! Currently we support so few arguments that splitting on spaces should be ok... for now.
    String[] argsSplit = argsString == null || argsString.isBlank() ? new String[0] : argsString.split(" ");
    for (var a : argsSplit) {
        argsList.add(a);
    }
    if (rotation != null && rotation != Rotation.ROTATE_NONE) {
        argsList.add("--rotate");
        argsList.add(rotation.toString());
    }
    if (!argsList.isEmpty())
        logger.debug("Args: [{}]", argsList.stream().collect(Collectors.joining(", ")));
    String[] args = argsList.toArray(String[]::new);
    List<String> pathSucceeded = new ArrayList<>();
    List<String> pathFailed = new ArrayList<>();
    List<ProjectImageEntry<BufferedImage>> entries = new ArrayList<>();
    Task<Collection<ProjectImageEntry<BufferedImage>>> worker = new Task<>() {

        @Override
        protected Collection<ProjectImageEntry<BufferedImage>> call() throws Exception {
            AtomicLong counter = new AtomicLong(0L);
            List<String> items = new ArrayList<>(listView.getItems());
            updateMessage("Checking for compatible image readers...");
            // Limit the size of the thread pool
            // The previous use of a cached thread pool caused trouble when importing may large, non-pyramidal images
            var pool = Executors.newFixedThreadPool(ThreadTools.getParallelism(), ThreadTools.createThreadFactory("project-import", true));
            // var pool = Executors.newCachedThreadPool(ThreadTools.createThreadFactory("project-import", true));
            List<Future<List<ServerBuilder<BufferedImage>>>> results = new ArrayList<>();
            List<ProjectImageEntry<BufferedImage>> projectImages = new ArrayList<>();
            for (var item : items) {
                // Try to load items from a project if possible
                if (item.toLowerCase().endsWith(ProjectIO.DEFAULT_PROJECT_EXTENSION)) {
                    try {
                        var tempProject = ProjectIO.loadProject(GeneralTools.toURI(item), BufferedImage.class);
                        projectImages.addAll(tempProject.getImageList());
                    } catch (Exception e) {
                        logger.error("Unable to add images from {} ({})", item, e.getLocalizedMessage());
                    }
                    continue;
                }
                results.add(pool.submit(() -> {
                    try {
                        var uri = GeneralTools.toURI(item);
                        UriImageSupport<BufferedImage> support;
                        if (requestedBuilder == null)
                            support = ImageServers.getImageSupport(uri, args);
                        else
                            support = ImageServers.getImageSupport(requestedBuilder, uri, args);
                        if (support != null)
                            return support.getBuilders();
                    } catch (Exception e) {
                        logger.error("Unable to add {}");
                        logger.error(e.getLocalizedMessage(), e);
                    }
                    return new ArrayList<ServerBuilder<BufferedImage>>();
                }));
            }
            List<ProjectImageEntry<BufferedImage>> failures = Collections.synchronizedList(new ArrayList<>());
            // If we have projects, try adding images from these first
            if (!projectImages.isEmpty()) {
                if (projectImages.size() == 1)
                    updateMessage("Importing 1 image from existing projects");
                else
                    updateMessage("Importing " + projectImages.size() + " images from existing projects");
                for (var temp : projectImages) {
                    try {
                        project.addDuplicate(temp, true);
                    } catch (Exception e) {
                        failures.add(temp);
                    }
                }
            }
            // If we have 'standard' image paths, use these next
            List<ServerBuilder<BufferedImage>> builders = new ArrayList<>();
            for (var result : results) {
                try {
                    builders.addAll(result.get());
                } catch (ExecutionException e) {
                    logger.error("Execution exception importing image", e);
                }
            }
            long max = builders.size();
            if (!builders.isEmpty()) {
                if (max == 1)
                    updateMessage("Adding 1 image to project");
                else
                    updateMessage("Adding " + max + " images to project");
                // Add everything in order first
                List<ProjectImageEntry<BufferedImage>> entries = new ArrayList<>();
                for (var builder : builders) {
                    // if (rotation != null && rotation != Rotation.ROTATE_NONE)
                    // builder = RotatedImageServer.getRotatedBuilder(builder, rotation);
                    // if (swapRedBlue)
                    // builder = RearrangeRGBImageServer.getSwapRedBlueBuilder(builder);
                    entries.add(project.addImage(builder));
                }
                // Initialize (the slow bit)
                int n = builders.size();
                for (var entry : entries) {
                    pool.submit(() -> {
                        try {
                            initializeEntry(entry, type, pyramidalize, importObjects);
                        } catch (Exception e) {
                            failures.add(entry);
                            logger.warn("Exception adding " + entry, e);
                        } finally {
                            long i = counter.incrementAndGet();
                            updateProgress(i, max);
                            String name = entry.getImageName();
                            if (name != null) {
                                updateMessage("Added " + i + "/" + n + " - " + name);
                            }
                        }
                    });
                }
            }
            pool.shutdown();
            try {
                pool.awaitTermination(60, TimeUnit.MINUTES);
            } catch (Exception e) {
                logger.error("Exception waiting for project import to complete: " + e.getLocalizedMessage(), e);
            }
            if (!failures.isEmpty()) {
                String message;
                if (failures.size() == 1)
                    message = "Failed to load one image.";
                else
                    message = "Failed to load " + failures.size() + " images.";
                if (requestedBuilder != null)
                    message += "\nThe image type might not be supported by '" + requestedBuilder.getName() + "'";
                Dialogs.showErrorMessage("Import images", message);
                var toRemove = failures.stream().filter(p -> project.getImageList().contains(p)).collect(Collectors.toList());
                project.removeAllImages(toRemove, true);
            }
            // Now save changes
            project.syncChanges();
            // builders.parallelStream().forEach(builder -> {
            // //				builders.parallelStream().forEach(builder -> {
            // try (var server =  builder.build()) {
            // var entry = addSingleImageToProject(project, server);
            // updateMessage("Added " + entry.getImageName());
            // } catch (Exception e) {
            // logger.warn("Exception adding " + builder, e);
            // } finally {
            // updateProgress(counter.incrementAndGet(), max);
            // }
            // });
            updateProgress(max, max);
            return entries;
        }
    };
    ProgressDialog progress = new ProgressDialog(worker);
    progress.setTitle("Project import");
    qupath.submitShortTask(worker);
    progress.showAndWait();
    try {
        project.syncChanges();
    } catch (IOException e1) {
        Dialogs.showErrorMessage("Sync project", e1);
    }
    qupath.refreshProject();
    StringBuilder sb = new StringBuilder();
    if (!pathSucceeded.isEmpty()) {
        sb.append("Successfully imported " + pathSucceeded.size() + " paths:\n");
        for (String path : pathSucceeded) sb.append("\t" + path + "\n");
        sb.append("\n");
        qupath.refreshProject();
        ProjectBrowser.syncProject(qupath.getProject());
    }
    if (!pathFailed.isEmpty()) {
        sb.append("Unable to import " + pathFailed.size() + " paths:\n");
        for (String path : pathFailed) sb.append("\t" + path + "\n");
        sb.append("\n");
        TextArea textArea = new TextArea();
        textArea.setText(sb.toString());
        if (pathSucceeded.isEmpty())
            Dialogs.showErrorMessage(commandName, textArea);
        else
            Dialogs.showMessageDialog(commandName, textArea);
    }
    // TODO: Add failed and successful paths to pathFailed/pathSucceeded, so the line below prints something
    if (sb.length() > 0)
        logger.info(sb.toString());
    return entries;
}
Also used : Button(javafx.scene.control.Button) ImageServer(qupath.lib.images.servers.ImageServer) ServerTools(qupath.lib.images.servers.ServerTools) PathObjectReader(qupath.lib.objects.PathObjectReader) ListCell(javafx.scene.control.ListCell) LoggerFactory(org.slf4j.LoggerFactory) Scanner(java.util.Scanner) RenderingHints(java.awt.RenderingHints) ProjectBrowser(qupath.lib.gui.panes.ProjectBrowser) Future(java.util.concurrent.Future) Task(javafx.concurrent.Task) ScrollPane(javafx.scene.control.ScrollPane) ComboBox(javafx.scene.control.ComboBox) URI(java.net.URI) ImageServers(qupath.lib.images.servers.ImageServers) ImageType(qupath.lib.images.ImageData.ImageType) QuPathGUI(qupath.lib.gui.QuPathGUI) TextField(javafx.scene.control.TextField) BufferedImage(java.awt.image.BufferedImage) ImageServerProvider(qupath.lib.images.servers.ImageServerProvider) Collection(java.util.Collection) ChannelDisplayInfo(qupath.lib.display.ChannelDisplayInfo) ImageServerBuilder(qupath.lib.images.servers.ImageServerBuilder) Collectors(java.util.stream.Collectors) FileNotFoundException(java.io.FileNotFoundException) Executors(java.util.concurrent.Executors) PathObject(qupath.lib.objects.PathObject) Priority(javafx.scene.layout.Priority) List(java.util.List) BooleanProperty(javafx.beans.property.BooleanProperty) Project(qupath.lib.projects.Project) ProjectIO(qupath.lib.projects.ProjectIO) Clipboard(javafx.scene.input.Clipboard) Optional(java.util.Optional) ThreadTools(qupath.lib.common.ThreadTools) BorderPane(javafx.scene.layout.BorderPane) ButtonData(javafx.scene.control.ButtonBar.ButtonData) ListView(javafx.scene.control.ListView) TextArea(javafx.scene.control.TextArea) ButtonType(javafx.scene.control.ButtonType) ProgressDialog(org.controlsfx.dialog.ProgressDialog) ImageDisplay(qupath.lib.display.ImageDisplay) UriImageSupport(qupath.lib.images.servers.ImageServerBuilder.UriImageSupport) TransferMode(javafx.scene.input.TransferMode) ArrayList(java.util.ArrayList) Dialogs(qupath.lib.gui.dialogs.Dialogs) Dragboard(javafx.scene.input.Dragboard) Insets(javafx.geometry.Insets) Graphics2D(java.awt.Graphics2D) GridPane(javafx.scene.layout.GridPane) ImageData(qupath.lib.images.ImageData) 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) CheckBox(javafx.scene.control.CheckBox) IOException(java.io.IOException) ServerBuilder(qupath.lib.images.servers.ImageServerBuilder.ServerBuilder) File(java.io.File) ExecutionException(java.util.concurrent.ExecutionException) TimeUnit(java.util.concurrent.TimeUnit) AtomicLong(java.util.concurrent.atomic.AtomicLong) WrappedBufferedImageServer(qupath.lib.images.servers.WrappedBufferedImageServer) Collections(java.util.Collections) PathPrefs(qupath.lib.gui.prefs.PathPrefs) Rotation(qupath.lib.images.servers.RotatedImageServer.Rotation) PaneTools(qupath.lib.gui.tools.PaneTools) Task(javafx.concurrent.Task) TextArea(javafx.scene.control.TextArea) Label(javafx.scene.control.Label) ArrayList(java.util.ArrayList) ProgressDialog(org.controlsfx.dialog.ProgressDialog) ImageType(qupath.lib.images.ImageData.ImageType) ListView(javafx.scene.control.ListView) Button(javafx.scene.control.Button) ProgressDialog(org.controlsfx.dialog.ProgressDialog) Dialog(javafx.scene.control.Dialog) TextField(javafx.scene.control.TextField) ButtonType(javafx.scene.control.ButtonType) Dragboard(javafx.scene.input.Dragboard) ImageServerBuilder(qupath.lib.images.servers.ImageServerBuilder) UriImageSupport(qupath.lib.images.servers.ImageServerBuilder.UriImageSupport) GridPane(javafx.scene.layout.GridPane) ComboBox(javafx.scene.control.ComboBox) CheckBox(javafx.scene.control.CheckBox) ScrollPane(javafx.scene.control.ScrollPane) Collection(java.util.Collection) ImageServerBuilder(qupath.lib.images.servers.ImageServerBuilder) ServerBuilder(qupath.lib.images.servers.ImageServerBuilder.ServerBuilder) BorderPane(javafx.scene.layout.BorderPane) Insets(javafx.geometry.Insets) ListCell(javafx.scene.control.ListCell) BufferedImage(java.awt.image.BufferedImage) ProjectImageEntry(qupath.lib.projects.ProjectImageEntry) ExecutionException(java.util.concurrent.ExecutionException) TitledPane(javafx.scene.control.TitledPane) IOException(java.io.IOException) Rotation(qupath.lib.images.servers.RotatedImageServer.Rotation) FileNotFoundException(java.io.FileNotFoundException) IOException(java.io.IOException) ExecutionException(java.util.concurrent.ExecutionException) AtomicLong(java.util.concurrent.atomic.AtomicLong) Future(java.util.concurrent.Future)

Example 2 with QuPathGUI

use of qupath.lib.gui.QuPathGUI in project qupath by qupath.

the class ExportObjectsCommand method runGeoJsonExport.

/**
 * Run the path object GeoJSON export command.
 * @param qupath
 * @return success
 * @throws IOException
 */
public static boolean runGeoJsonExport(QuPathGUI qupath) throws IOException {
    // Get ImageData
    var imageData = qupath.getImageData();
    if (imageData == null)
        return false;
    // Get hierarchy
    PathObjectHierarchy hierarchy = imageData.getHierarchy();
    String allObjects = "All objects";
    String selectedObjects = "Selected objects";
    String defaultObjects = hierarchy.getSelectionModel().noSelection() ? allObjects : selectedObjects;
    // Params
    var parameterList = new ParameterList().addChoiceParameter("exportOptions", "Export ", defaultObjects, Arrays.asList(allObjects, selectedObjects), "Choose which objects to export - run a 'Select annotations/detections' command first if needed").addBooleanParameter("excludeMeasurements", "Exclude measurements", false, "Exclude object measurements during export - for large numbers of detections this can help reduce the file size").addBooleanParameter("doPretty", "Pretty JSON", false, "Pretty GeoJSON is more human-readable but results in larger file sizes").addBooleanParameter("doFeatureCollection", "Export as FeatureCollection", true, "Export as a 'FeatureCollection', which is a standard GeoJSON way to represent multiple objects; if not, a regular JSON object/array will be export").addBooleanParameter("doZip", "Compress data (zip)", false, "Compressed files take less memory");
    if (!Dialogs.showParameterDialog("Export objects", parameterList))
        return false;
    Collection<PathObject> toProcess;
    var comboChoice = parameterList.getChoiceParameterValue("exportOptions");
    if (comboChoice.equals("Selected objects")) {
        if (hierarchy.getSelectionModel().noSelection()) {
            Dialogs.showErrorMessage("No selection", "No selection detected!");
            return false;
        }
        toProcess = hierarchy.getSelectionModel().getSelectedObjects();
    } else
        toProcess = hierarchy.getObjects(null, null);
    // Remove PathRootObject
    toProcess = toProcess.stream().filter(e -> !e.isRootObject()).collect(Collectors.toList());
    // Check if includes ellipse(s), as they will need to be polygonized
    var nEllipses = toProcess.stream().filter(ann -> isEllipse(ann)).count();
    if (nEllipses > 0) {
        String message = nEllipses == 1 ? "1 ellipse will be polygonized, continue?" : String.format("%d ellipses will be polygonized, continue?", nEllipses);
        var response = Dialogs.showYesNoDialog("Ellipse polygonization", message);
        if (!response)
            return false;
    }
    File outFile;
    // Get default name & output directory
    var project = qupath.getProject();
    String defaultName = imageData.getServer().getMetadata().getName();
    if (project != null) {
        var entry = project.getEntry(imageData);
        if (entry != null)
            defaultName = entry.getImageName();
    }
    defaultName = GeneralTools.getNameWithoutExtension(defaultName);
    File defaultDirectory = project == null || project.getPath() == null ? null : project.getPath().toFile();
    while (defaultDirectory != null && !defaultDirectory.isDirectory()) defaultDirectory = defaultDirectory.getParentFile();
    if (parameterList.getBooleanParameterValue("doZip"))
        outFile = Dialogs.promptToSaveFile("Export to file", defaultDirectory, defaultName + ".zip", "ZIP archive", ".zip");
    else
        outFile = Dialogs.promptToSaveFile("Export to file", defaultDirectory, defaultName + ".geojson", "GeoJSON", ".geojson");
    // If user cancels
    if (outFile == null)
        return false;
    List<GeoJsonExportOptions> options = new ArrayList<>();
    if (parameterList.getBooleanParameterValue("excludeMeasurements"))
        options.add(GeoJsonExportOptions.EXCLUDE_MEASUREMENTS);
    if (parameterList.getBooleanParameterValue("doPretty"))
        options.add(GeoJsonExportOptions.PRETTY_JSON);
    if (parameterList.getBooleanParameterValue("doFeatureCollection"))
        options.add(GeoJsonExportOptions.FEATURE_COLLECTION);
    // Export
    QP.exportObjectsToGeoJson(toProcess, outFile.getAbsolutePath(), options.toArray(GeoJsonExportOptions[]::new));
    // Notify user of success
    int nObjects = toProcess.size();
    String message = nObjects == 1 ? "1 object was exported to " + outFile.getAbsolutePath() : String.format("%d objects were exported to %s", nObjects, outFile.getAbsolutePath());
    Dialogs.showInfoNotification("Succesful export", message);
    // Get history workflow
    var historyWorkflow = imageData.getHistoryWorkflow();
    // args for workflow step
    Map<String, String> map = new LinkedHashMap<>();
    map.put("path", outFile.getPath());
    String method = comboChoice.equals(allObjects) ? "exportAllObjectsToGeoJson" : "exportSelectedObjectsToGeoJson";
    String methodTitle = comboChoice.equals(allObjects) ? "Export all objects" : "Export selected objects";
    String optionsString = options.stream().map(o -> "\"" + o.name() + "\"").collect(Collectors.joining(", "));
    map.put("options", optionsString);
    if (!optionsString.isEmpty())
        optionsString = ", " + optionsString;
    String methodString = String.format("%s(%s%s)", method, "\"" + GeneralTools.escapeFilePath(outFile.getPath()) + "\"", optionsString);
    historyWorkflow.addStep(new DefaultScriptableWorkflowStep(methodTitle, map, methodString));
    return true;
}
Also used : Arrays(java.util.Arrays) GeneralTools(qupath.lib.common.GeneralTools) GeoJsonExportOptions(qupath.lib.io.PathIO.GeoJsonExportOptions) Collection(java.util.Collection) IOException(java.io.IOException) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Collectors(java.util.stream.Collectors) File(java.io.File) ArrayList(java.util.ArrayList) PathObject(qupath.lib.objects.PathObject) LinkedHashMap(java.util.LinkedHashMap) Dialogs(qupath.lib.gui.dialogs.Dialogs) List(java.util.List) ParameterList(qupath.lib.plugins.parameters.ParameterList) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) Map(java.util.Map) QP(qupath.lib.scripting.QP) QuPathGUI(qupath.lib.gui.QuPathGUI) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) ArrayList(java.util.ArrayList) LinkedHashMap(java.util.LinkedHashMap) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) PathObject(qupath.lib.objects.PathObject) ParameterList(qupath.lib.plugins.parameters.ParameterList) GeoJsonExportOptions(qupath.lib.io.PathIO.GeoJsonExportOptions) File(java.io.File)

Example 3 with QuPathGUI

use of qupath.lib.gui.QuPathGUI in project qupath by qupath.

the class TMACommands method promptToExportTMAData.

/**
 * Prompt to export summary TMA data for a specific image to a directory.
 * @param qupath
 * @param imageData
 */
public static void promptToExportTMAData(QuPathGUI qupath, ImageData<BufferedImage> imageData) {
    String title = "Export TMA data";
    if (imageData == null) {
        Dialogs.showNoImageError(title);
        return;
    }
    PathObjectHierarchy hierarchy = imageData == null ? null : imageData.getHierarchy();
    if (hierarchy == null || hierarchy.isEmpty() || hierarchy.getTMAGrid() == null || hierarchy.getTMAGrid().nCores() == 0) {
        Dialogs.showErrorMessage(title, "No TMA data available!");
        return;
    }
    var overlayOptions = qupath.getViewers().stream().filter(v -> v.getImageData() == imageData).map(v -> v.getOverlayOptions()).findFirst().orElse(qupath.getOverlayOptions());
    String defaultName = ServerTools.getDisplayableImageName(imageData.getServer());
    File file = Dialogs.promptToSaveFile(null, null, defaultName, "TMA data", ".qptma");
    if (file != null) {
        if (!file.getName().endsWith(".qptma"))
            file = new File(file.getParentFile(), file.getName() + ".qptma");
        double downsample = PathPrefs.tmaExportDownsampleProperty().get();
        TMADataIO.writeTMAData(file, imageData, overlayOptions, downsample);
        WorkflowStep step = new DefaultScriptableWorkflowStep("Export TMA data", "exportTMAData(\"" + GeneralTools.escapeFilePath(file.getParentFile().getAbsolutePath()) + "\", " + downsample + ")");
        imageData.getHistoryWorkflow().addStep(step);
    // PathAwtIO.writeTMAData(file, imageData, viewer.getOverlayOptions(), Double.NaN);
    }
}
Also used : Arrays(java.util.Arrays) ServerTools(qupath.lib.images.servers.ServerTools) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) ArrayList(java.util.ArrayList) Dialogs(qupath.lib.gui.dialogs.Dialogs) TMADataIO(qupath.lib.gui.tma.TMADataIO) ParameterList(qupath.lib.plugins.parameters.ParameterList) Map(java.util.Map) WeakHashMap(java.util.WeakHashMap) RunningStatistics(qupath.lib.analysis.stats.RunningStatistics) QuPathGUI(qupath.lib.gui.QuPathGUI) ImageData(qupath.lib.images.ImageData) BufferedImage(java.awt.image.BufferedImage) GeneralTools(qupath.lib.common.GeneralTools) PathObjects(qupath.lib.objects.PathObjects) TMACoreObject(qupath.lib.objects.TMACoreObject) WorkflowStep(qupath.lib.plugins.workflow.WorkflowStep) Collectors(java.util.stream.Collectors) File(java.io.File) PathObjectTools(qupath.lib.objects.PathObjectTools) PathObject(qupath.lib.objects.PathObject) DefaultTMAGrid(qupath.lib.objects.hierarchy.DefaultTMAGrid) List(java.util.List) BooleanProperty(javafx.beans.property.BooleanProperty) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) QP(qupath.lib.scripting.QP) StringProperty(javafx.beans.property.StringProperty) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) PathPrefs(qupath.lib.gui.prefs.PathPrefs) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) WorkflowStep(qupath.lib.plugins.workflow.WorkflowStep) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) File(java.io.File)

Example 4 with QuPathGUI

use of qupath.lib.gui.QuPathGUI in project qupath by qupath.

the class QPEx method copyToClipboard.

/**
 * Try to copy an object to the clipboard.
 * This will attempt to perform a smart conversion; for example, if a window is provided a snapshot will be taken
 * and copied as an image.
 * @param o the object to copy
 */
public static void copyToClipboard(Object o) {
    if (!Platform.isFxApplicationThread()) {
        Object o2 = o;
        Platform.runLater(() -> copyToClipboard(o2));
        return;
    }
    ClipboardContent content = new ClipboardContent();
    // Handle things that are (or could become) images
    if (o instanceof BufferedImage)
        o = SwingFXUtils.toFXImage((BufferedImage) o, null);
    if (o instanceof QuPathGUI)
        o = ((QuPathGUI) o).getStage();
    if (o instanceof QuPathViewer)
        o = ((QuPathViewer) o).getView();
    if (o instanceof Window)
        o = ((Window) o).getScene();
    if (o instanceof Scene)
        o = ((Scene) o).snapshot(null);
    if (o instanceof Node)
        o = ((Node) o).snapshot(null, null);
    if (o instanceof Image)
        content.putImage((Image) o);
    // Handle files
    List<File> files = null;
    if (o instanceof File)
        files = Arrays.asList((File) o);
    else if (o instanceof File[])
        files = Arrays.asList((File[]) o);
    else if (o instanceof Collection) {
        files = new ArrayList<>();
        for (var something : (Collection<?>) o) {
            if (something instanceof File)
                files.add((File) something);
        }
    }
    if (files != null && !files.isEmpty())
        content.putFiles(files);
    // Handle URLs
    if (o instanceof URL)
        content.putUrl(((URL) o).toString());
    // Always put a String representation
    content.putString(o.toString());
    Clipboard.getSystemClipboard().setContent(content);
}
Also used : Window(javafx.stage.Window) ClipboardContent(javafx.scene.input.ClipboardContent) Node(javafx.scene.Node) Scene(javafx.scene.Scene) BufferedImage(java.awt.image.BufferedImage) Image(javafx.scene.image.Image) BufferedImage(java.awt.image.BufferedImage) QuPathGUI(qupath.lib.gui.QuPathGUI) URL(java.net.URL) Collection(java.util.Collection) PathRootObject(qupath.lib.objects.PathRootObject) PathObject(qupath.lib.objects.PathObject) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) PathDetectionObject(qupath.lib.objects.PathDetectionObject) TMACoreObject(qupath.lib.objects.TMACoreObject) File(java.io.File) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer)

Example 5 with QuPathGUI

use of qupath.lib.gui.QuPathGUI in project qupath by qupath.

the class TMASummaryViewer method setTMAEntriesFromOpenProject.

private void setTMAEntriesFromOpenProject() {
    QuPathGUI qupath = QuPathGUI.getInstance();
    if (qupath == null || qupath.getProject() == null || qupath.getProject().getImageList().isEmpty()) {
        Dialogs.showNoProjectError("Show TMA summary");
        return;
    }
    Project<BufferedImage> project = qupath.getProject();
    List<TMAEntry> entries = new ArrayList<>();
    for (ProjectImageEntry<BufferedImage> imageEntry : project.getImageList()) {
        if (imageEntry.hasImageData()) {
            try {
                ImageData<BufferedImage> imageData = imageEntry.readImageData();
                entries.addAll(getEntriesForTMAData(imageData));
            } catch (IOException e) {
                logger.error("Unable to read ImageData for {} ({})", imageEntry.getImageName(), e.getLocalizedMessage());
            }
        }
    }
    setTMAEntries(entries);
    stage.setTitle("TMA Viewer: " + project.getName());
}
Also used : TMAEntry(qupath.lib.gui.tma.TMAEntries.TMAEntry) ArrayList(java.util.ArrayList) IOException(java.io.IOException) QuPathGUI(qupath.lib.gui.QuPathGUI) BufferedImage(java.awt.image.BufferedImage)

Aggregations

QuPathGUI (qupath.lib.gui.QuPathGUI)8 BufferedImage (java.awt.image.BufferedImage)6 PathObject (qupath.lib.objects.PathObject)6 File (java.io.File)5 IOException (java.io.IOException)5 ArrayList (java.util.ArrayList)5 List (java.util.List)5 GeneralTools (qupath.lib.common.GeneralTools)5 Dialogs (qupath.lib.gui.dialogs.Dialogs)5 Collection (java.util.Collection)4 Collectors (java.util.stream.Collectors)4 DefaultScriptableWorkflowStep (qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep)4 Map (java.util.Map)3 Graphics2D (java.awt.Graphics2D)2 RenderingHints (java.awt.RenderingHints)2 URI (java.net.URI)2 Arrays (java.util.Arrays)2 Collections (java.util.Collections)2 LinkedHashMap (java.util.LinkedHashMap)2 BooleanProperty (javafx.beans.property.BooleanProperty)2