Search in sources :

Example 81 with PathObjectHierarchy

use of qupath.lib.objects.hierarchy.PathObjectHierarchy in project qupath by qupath.

the class DragDropImportListener method handleFileDropImpl.

private void handleFileDropImpl(final QuPathViewer viewer, final List<File> list) throws IOException {
    // Shouldn't occur... but keeps FindBugs happy to check
    if (list == null) {
        logger.warn("No files given!");
        return;
    }
    // Check if we have only jar files
    int nJars = 0;
    for (File file : list) {
        if (file.getName().toLowerCase().endsWith(".jar"))
            nJars++;
    }
    if (nJars == list.size()) {
        if (qupath.canInstallExtensions())
            qupath.installExtensions(list);
        else
            Dialogs.showErrorMessage("Install extensions", "Sorry, extensions can only be installed when QuPath is run as a standalone application.");
        return;
    }
    // Try to get a hierarchy for importing ROIs
    ImageData<BufferedImage> imageData = viewer == null ? null : viewer.getImageData();
    PathObjectHierarchy hierarchy = imageData == null ? null : imageData.getHierarchy();
    // Some consumers can only handle one file
    boolean singleFile = list.size() == 1;
    // Gather together the extensions - if this has length one, we know all the files have the same extension
    Set<String> allExtensions = list.stream().map(f -> GeneralTools.getExtension(f).orElse("")).collect(Collectors.toSet());
    // If we have a zipped file, create a set that includes the files within the zip image
    // This helps us determine whether or not a zip file contains an image or objects, for example
    Set<String> allUnzippedExtensions = allExtensions;
    if (allExtensions.contains(".zip")) {
        allUnzippedExtensions = list.stream().flatMap(f -> {
            try {
                return PathIO.unzippedExtensions(f.toPath()).stream();
            } catch (IOException e) {
                logger.debug(e.getLocalizedMessage(), e);
                return Arrays.stream(new String[0]);
            }
        }).collect(Collectors.toSet());
    }
    // Extract the first (and possibly only) file
    File file = list.get(0);
    String fileName = file.getName().toLowerCase();
    // Check if this is a hierarchy file
    if (singleFile && (fileName.endsWith(PathPrefs.getSerializationExtension()))) {
        // If we have a different path, open as a new image
        if (viewer == null) {
            Dialogs.showErrorMessage("Load data", "Please drag the file onto a specific viewer to open!");
            return;
        }
        try {
            // Check if we should be importing objects or opening the file
            if (imageData != null) {
                var dialog = new Dialog<ButtonType>();
                var btOpen = new ButtonType("Open image");
                var btImport = new ButtonType("Import objects");
                dialog.getDialogPane().getButtonTypes().setAll(btOpen, btImport, ButtonType.CANCEL);
                dialog.setTitle("Open data");
                dialog.setHeaderText("What do you want to do with the data file?");
                dialog.setContentText("You can\n" + " 1. Open the image in the current viewer\n" + " 2. Import objects and add them to the current image");
                // dialog.setHeaderText("What do you want to do?");
                var choice = dialog.showAndWait().orElse(ButtonType.CANCEL);
                if (choice == ButtonType.CANCEL)
                    return;
                if (choice == btImport) {
                    var pathObjects = PathIO.readObjects(file);
                    hierarchy.addPathObjects(pathObjects);
                    return;
                }
            }
            qupath.openSavedData(viewer, file, false, true);
        } catch (Exception e) {
            Dialogs.showErrorMessage("Load data", e);
        }
        return;
    }
    // Check if this is a directory - if so, look for a single project file
    if (singleFile && file.isDirectory()) {
        // Identify all files in the directory, and also all potential project files
        File[] filesInDirectory = file.listFiles(f -> !f.isHidden());
        List<File> projectFiles = Arrays.stream(filesInDirectory).filter(f -> f.isFile() && f.getAbsolutePath().toLowerCase().endsWith(ProjectIO.getProjectExtension())).collect(Collectors.toList());
        if (projectFiles.size() == 1) {
            file = projectFiles.get(0);
            logger.warn("Selecting project file {}", file);
        } else if (projectFiles.size() > 1) {
            // Prompt to select which project file to open
            logger.debug("Multiple project files found in directory {}", file);
            String[] fileNames = projectFiles.stream().map(f -> f.getName()).toArray(n -> new String[n]);
            String selectedName = Dialogs.showChoiceDialog("Select project", "Select project to open", fileNames, fileNames[0]);
            if (selectedName == null)
                return;
            file = new File(file, selectedName);
        } else if (filesInDirectory.length == 0) {
            // If we have an empty directory, offer to set it as a project
            if (Dialogs.showYesNoDialog("Create project", "Create project for empty directory?")) {
                Project<BufferedImage> project = Projects.createProject(file, BufferedImage.class);
                qupath.setProject(project);
                if (!project.isEmpty())
                    project.syncChanges();
                return;
            } else
                // Can't do anything else with an empty folder
                return;
        }
    }
    // Check if this is a project
    if (singleFile && (fileName.endsWith(ProjectIO.getProjectExtension()))) {
        try {
            Project<BufferedImage> project = ProjectIO.loadProject(file, BufferedImage.class);
            qupath.setProject(project);
        } catch (Exception e) {
            // Dialogs.showErrorMessage("Project error", e);
            logger.error("Could not open as project file: {}, opening in the Script Editor instead", e);
            qupath.getScriptEditor().showScript(file);
        }
        return;
    }
    // Check if it is an object file in GeoJSON format (.geojson)
    if (PathIO.getObjectFileExtensions(false).containsAll(allUnzippedExtensions)) {
        if (imageData == null || hierarchy == null) {
            qupath.getScriptEditor().showScript(file);
            logger.info("Opening the dragged file in the Script Editor as there is no currently opened image in the viewer");
            // Dialogs.showErrorMessage("Open object file", "Please open an image first to import objects!");
            return;
        }
        List<PathObject> pathObjects = new ArrayList<>();
        List<WorkflowStep> steps = new ArrayList<>();
        for (var tempFile : list) {
            try {
                var tempObjects = PathIO.readObjects(tempFile);
                if (tempObjects.isEmpty()) {
                    logger.warn("No objects found in {}, opening the dragged file in the Script Editor instead", tempFile.getAbsolutePath());
                    qupath.getScriptEditor().showScript(file);
                    return;
                }
                pathObjects.addAll(tempObjects);
                // Add step to workflow
                Map<String, String> map = new HashMap<>();
                map.put("path", file.getPath());
                String method = "Import objects";
                String methodString = String.format("%s(%s%s%s)", "importObjectsFromFile", "\"", GeneralTools.escapeFilePath(tempFile.getPath()), "\"");
                steps.add(new DefaultScriptableWorkflowStep(method, map, methodString));
            } catch (IOException | IllegalArgumentException e) {
                Dialogs.showErrorNotification("Object import", e.getLocalizedMessage());
                return;
            }
        }
        // Ask confirmation to user
        int nObjects = pathObjects.size();
        String message = nObjects == 1 ? "Add object to the hierarchy?" : String.format("Add %d objects to the hierarchy?", nObjects);
        var confirm = Dialogs.showConfirmDialog("Add to hierarchy", message);
        if (!confirm)
            return;
        // Add objects to hierarchy
        hierarchy.addPathObjects(pathObjects);
        imageData.getHistoryWorkflow().addSteps(steps);
        return;
    }
    // Check if this is TMA dearraying data file
    if (singleFile && (fileName.endsWith(TMADataIO.TMA_DEARRAYING_DATA_EXTENSION))) {
        if (hierarchy == null)
            Dialogs.showErrorMessage("TMA grid import", "Please open an image first before importing a dearrayed TMA grid!");
        else {
            TMAGrid tmaGrid = TMADataIO.importDearrayedTMAData(file);
            if (tmaGrid != null) {
                if (hierarchy.isEmpty() || Dialogs.showYesNoDialog("TMA grid import", "Set TMA grid for existing hierarchy?"))
                    hierarchy.setTMAGrid(tmaGrid);
            } else
                Dialogs.showErrorMessage("TMA grid import", "Could not parse TMA grid from " + file.getName());
        }
        return;
    }
    // Open file with an extension supported by the Script Editor
    ScriptEditor scriptEditor = qupath.getScriptEditor();
    if (scriptEditor instanceof DefaultScriptEditor && ((DefaultScriptEditor) scriptEditor).supportsFile(file)) {
        scriptEditor.showScript(file);
        return;
    }
    // Check handlers
    for (DropHandler<File> handler : dropHandlers) {
        if (handler.handleDrop(viewer, list))
            return;
    }
    // Assume we have images
    if (singleFile && file.isFile()) {
        // Try to open as an image, if the extension is known
        if (viewer == null) {
            Dialogs.showErrorMessage("Open image", "Please drag the file only a specific viewer to open!");
            return;
        }
        qupath.openImage(viewer, file.getAbsolutePath(), true, true);
        return;
    } else if (qupath.getProject() != null) {
        // Try importing multiple images to a project
        String[] potentialFiles = list.stream().filter(f -> f.isFile()).map(f -> f.getAbsolutePath()).toArray(String[]::new);
        if (potentialFiles.length > 0) {
            ProjectCommands.promptToImportImages(qupath, potentialFiles);
            return;
        }
    }
    if (qupath.getProject() == null) {
        if (list.size() > 1) {
            Dialogs.showErrorMessage("Drag & drop", "Could not handle multiple file drop - if you want to handle multiple images, you need to create a project first");
            return;
        }
    }
    if (list.size() > 1)
        Dialogs.showErrorMessage("Drag & drop", "Sorry, I couldn't figure out what to do with these files - try opening one at a time");
    else
        Dialogs.showErrorMessage("Drag & drop", "Sorry, I couldn't figure out what to do with " + list.get(0).getName());
}
Also used : EventHandler(javafx.event.EventHandler) Scene(javafx.scene.Scene) Arrays(java.util.Arrays) ButtonType(javafx.scene.control.ButtonType) LoggerFactory(org.slf4j.LoggerFactory) HashMap(java.util.HashMap) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) ScriptEditor(qupath.lib.gui.scripting.ScriptEditor) DragEvent(javafx.scene.input.DragEvent) TransferMode(javafx.scene.input.TransferMode) Projects(qupath.lib.projects.Projects) ArrayList(java.util.ArrayList) Dialogs(qupath.lib.gui.dialogs.Dialogs) TMADataIO(qupath.lib.gui.tma.TMADataIO) Dragboard(javafx.scene.input.Dragboard) Map(java.util.Map) QuPathGUI(qupath.lib.gui.QuPathGUI) ImageData(qupath.lib.images.ImageData) Logger(org.slf4j.Logger) Dialog(javafx.scene.control.Dialog) BufferedImage(java.awt.image.BufferedImage) GeneralTools(qupath.lib.common.GeneralTools) Node(javafx.scene.Node) Set(java.util.Set) IOException(java.io.IOException) WorkflowStep(qupath.lib.plugins.workflow.WorkflowStep) ProjectCommands(qupath.lib.gui.commands.ProjectCommands) Collectors(java.util.stream.Collectors) File(java.io.File) PathObject(qupath.lib.objects.PathObject) List(java.util.List) Project(qupath.lib.projects.Project) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) ProjectIO(qupath.lib.projects.ProjectIO) DefaultScriptEditor(qupath.lib.gui.scripting.DefaultScriptEditor) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) PathPrefs(qupath.lib.gui.prefs.PathPrefs) PathIO(qupath.lib.io.PathIO) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) WorkflowStep(qupath.lib.plugins.workflow.WorkflowStep) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) DefaultScriptEditor(qupath.lib.gui.scripting.DefaultScriptEditor) BufferedImage(java.awt.image.BufferedImage) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) Dialog(javafx.scene.control.Dialog) ButtonType(javafx.scene.control.ButtonType) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) IOException(java.io.IOException) IOException(java.io.IOException) ScriptEditor(qupath.lib.gui.scripting.ScriptEditor) DefaultScriptEditor(qupath.lib.gui.scripting.DefaultScriptEditor) PathObject(qupath.lib.objects.PathObject) File(java.io.File)

Example 82 with PathObjectHierarchy

use of qupath.lib.objects.hierarchy.PathObjectHierarchy in project qupath by qupath.

the class TMAGridOverlay method paintOverlay.

@Override
public void paintOverlay(final Graphics2D g, final ImageRegion imageRegion, final double downsampleFactor, final ImageData<BufferedImage> imageData, final boolean paintCompletely) {
    if (!isVisible())
        return;
    PathObjectHierarchy hierarchy = imageData == null ? null : imageData.getHierarchy();
    if (hierarchy == null)
        return;
    TMAGrid tmaGrid = hierarchy.getTMAGrid();
    if (tmaGrid == null)
        return;
    Graphics2D g2d = (Graphics2D) g.create();
    // Set alpha composite if needed
    setAlphaComposite(g2d);
    // Rectangle serverBounds = imageRegion.getBounds();
    // Ensure antialias is on...?
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    PathHierarchyPaintingHelper.paintTMAGrid(g2d, tmaGrid, getOverlayOptions(), hierarchy.getSelectionModel(), downsampleFactor);
    g2d.dispose();
}
Also used : PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) Graphics2D(java.awt.Graphics2D)

Example 83 with PathObjectHierarchy

use of qupath.lib.objects.hierarchy.PathObjectHierarchy in project qupath by qupath.

the class AbstractPathROITool method mousePressed.

@Override
public void mousePressed(MouseEvent e) {
    super.mousePressed(e);
    if (!e.isPrimaryButtonDown() || e.isConsumed()) {
        return;
    }
    var viewer = getViewer();
    PathObjectHierarchy hierarchy = viewer.getHierarchy();
    if (hierarchy == null)
        return;
    PathObject currentObject = viewer.getSelectedObject();
    ROI currentROI = currentObject == null ? null : currentObject.getROI();
    RoiEditor editor = viewer.getROIEditor();
    boolean adjustingPolygon = (currentROI instanceof PolygonROI || currentROI instanceof PolylineROI) && editor.getROI() == currentROI && (editor.isTranslating() || editor.hasActiveHandle());
    // If we're adjusting a polygon/polyline with an appropriate tool, return at leave it up to the tool to handle the custom things
    if (adjustingPolygon) {
        if (viewer.getActiveTool() == PathTools.POLYGON || viewer.getActiveTool() == PathTools.POLYLINE)
            return;
        else {
            viewer.getHierarchy().getSelectionModel().clearSelection();
            viewer.getHierarchy().fireHierarchyChangedEvent(currentObject);
        }
    }
    // Find out the coordinates in the image domain
    Point2D p2 = mouseLocationToImage(e, false, requestPixelSnapping());
    double xx = p2.getX();
    double yy = p2.getY();
    if (xx < 0 || yy < 0 || xx >= viewer.getServerWidth() || yy >= viewer.getServerHeight())
        return;
    // If we are double-clicking & we don't have a polygon, see if we can access a ROI
    if (!PathPrefs.selectionModeProperty().get() && e.getClickCount() > 1) {
        // Reset parent... for now
        resetConstrainedAreaParent();
        tryToSelect(xx, yy, e.getClickCount() - 2, false);
        e.consume();
        return;
    }
    // Set the current parent object based on the first click
    setConstrainedAreaParent(hierarchy, xx, yy, Collections.emptyList());
    // Create a new annotation
    PathObject pathObject = createNewAnnotation(e, xx, yy);
    if (pathObject == null)
        return;
    // Start editing the ROI immediately
    editor.setROI(pathObject.getROI());
    editor.grabHandle(xx, yy, viewer.getMaxROIHandleSize() * 1.5, e.isShiftDown());
}
Also used : PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) PolygonROI(qupath.lib.roi.PolygonROI) PathObject(qupath.lib.objects.PathObject) RoiEditor(qupath.lib.roi.RoiEditor) PolylineROI(qupath.lib.roi.PolylineROI) Point2D(java.awt.geom.Point2D) PolylineROI(qupath.lib.roi.PolylineROI) ROI(qupath.lib.roi.interfaces.ROI) PolygonROI(qupath.lib.roi.PolygonROI)

Example 84 with PathObjectHierarchy

use of qupath.lib.objects.hierarchy.PathObjectHierarchy in project qupath by qupath.

the class PathIO method readHierarchy.

/**
 * Read a {@link PathObjectHierarchy} from a saved data file (omitting all other contents).
 *
 * @param fileIn
 * @return
 * @throws IOException
 */
public static PathObjectHierarchy readHierarchy(final InputStream fileIn) throws IOException {
    Locale locale = Locale.getDefault(Category.FORMAT);
    boolean localeChanged = false;
    try (ObjectInputStream inStream = new ObjectInputStream(new BufferedInputStream(fileIn))) {
        if (!inStream.readUTF().startsWith("Data file version")) {
            logger.error("Input stream is not from a valid QuPath data file!");
        }
        while (true) {
            // logger.debug("Starting read: " + inStream.available());
            try {
                // Try to read a relevant object from the stream
                Object input = inStream.readObject();
                logger.debug("Read: {}", input);
                // Set locale - may be needed (although probably isn't...)
                if (input instanceof Locale) {
                    if (input != locale) {
                        Locale.setDefault(Category.FORMAT, (Locale) input);
                        localeChanged = true;
                    }
                } else if (input instanceof PathObjectHierarchy) {
                    /* This would ideally be unnecessary, but it's needed to ensure that the PathObjectHierarchy
						 * has been property initialized.  We can't count on the deserialized hierarchy being immediately functional.
						 */
                    PathObjectHierarchy hierarchy = new PathObjectHierarchy();
                    hierarchy.setHierarchy((PathObjectHierarchy) input);
                    return hierarchy;
                }
            } catch (ClassNotFoundException e) {
                logger.error("Unable to find class", e);
            } catch (EOFException e) {
                logger.error("Reached end of file unexpectedly...");
            }
        }
    } finally {
        if (localeChanged)
            Locale.setDefault(Category.FORMAT, locale);
    }
}
Also used : Locale(java.util.Locale) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) BufferedInputStream(java.io.BufferedInputStream) EOFException(java.io.EOFException) PathObject(qupath.lib.objects.PathObject) ObjectInputStream(java.io.ObjectInputStream)

Example 85 with PathObjectHierarchy

use of qupath.lib.objects.hierarchy.PathObjectHierarchy in project qupath by qupath.

the class ClassifierBuilderPane method updateRetainedObjectsMap.

/**
 * Update the retained objects map using the data from the current image.
 */
private void updateRetainedObjectsMap() {
    PathObjectHierarchy hierarchy = getHierarchy();
    if (hierarchy != null) {
        Map<PathClass, List<PathObject>> mapCurrent = PathClassificationLabellingHelper.getClassificationMap(hierarchy, paramsUpdate.getBooleanParameterValue("trainFromPoints"));
        // Add in any retained objects, if we have some
        PathClassificationLabellingHelper.countObjectsInMap(mapCurrent);
        // int retainedImageCount = retainedObjectsMap.addToTrainingMap(map, getImageData().getServerPath());
        retainedObjectsMap.put(getMapKey(getImageData()), mapCurrent);
        updateRetainedObjectsLabel();
    }
}
Also used : PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) PathClass(qupath.lib.objects.classes.PathClass) ParameterList(qupath.lib.plugins.parameters.ParameterList) List(java.util.List) ArrayList(java.util.ArrayList)

Aggregations

PathObjectHierarchy (qupath.lib.objects.hierarchy.PathObjectHierarchy)85 PathObject (qupath.lib.objects.PathObject)57 ArrayList (java.util.ArrayList)38 ROI (qupath.lib.roi.interfaces.ROI)31 List (java.util.List)24 Collectors (java.util.stream.Collectors)24 TMACoreObject (qupath.lib.objects.TMACoreObject)23 BufferedImage (java.awt.image.BufferedImage)22 IOException (java.io.IOException)22 Logger (org.slf4j.Logger)22 LoggerFactory (org.slf4j.LoggerFactory)22 ImageData (qupath.lib.images.ImageData)20 PathAnnotationObject (qupath.lib.objects.PathAnnotationObject)20 Collection (java.util.Collection)19 Collections (java.util.Collections)19 Map (java.util.Map)19 File (java.io.File)17 Arrays (java.util.Arrays)16 PathObjectTools (qupath.lib.objects.PathObjectTools)16 GeneralTools (qupath.lib.common.GeneralTools)15