Search in sources :

Example 6 with TMAGrid

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

the class QuPathViewer method getImageLocationString.

/**
 * Get a string representing the image coordinates for a particular x & y location.
 * @param xx x-coordinate in the image space (not the component/viewer space)
 * @param yy y-coordinate in the image space (not the component/viewer space)
 * @param useCalibratedUnits
 * @return
 */
private String getImageLocationString(double xx, double yy, boolean useCalibratedUnits) {
    ImageServer<BufferedImage> server = getServer();
    if (server == null)
        return "";
    String units;
    if (xx < 0 || yy < 0 || xx > server.getWidth() - 1 || yy > server.getHeight() - 1)
        return "";
    double xDisplay = xx;
    double yDisplay = yy;
    PixelCalibration cal = server.getPixelCalibration();
    if (useCalibratedUnits && cal.hasPixelSizeMicrons()) {
        units = GeneralTools.micrometerSymbol();
        xDisplay *= cal.getPixelWidthMicrons();
        yDisplay *= cal.getPixelHeightMicrons();
    } else {
        units = "px";
    }
    // See if we're on top of a TMA core
    String prefix = "";
    TMAGrid tmaGrid = getHierarchy().getTMAGrid();
    if (tmaGrid != null) {
        TMACoreObject core = PathObjectTools.getTMACoreForPixel(tmaGrid, xx, yy);
        if (core != null && core.getName() != null)
            prefix = "Core: " + core.getName() + "\n";
    }
    String s = null;
    RegionRequest request = ImageRegionStoreHelpers.getTileRequest(server, xx, yy, downsampleFactor.get(), getZPosition(), getTPosition());
    if (request != null) {
        BufferedImage img = regionStore.getCachedTile(server, request);
        int xi = 0, yi = 0;
        if (img == null) {
            // Try getting a value from the thumbnail for the whole image
            BufferedImage imgThumbnail = regionStore.getCachedThumbnail(server, getZPosition(), getTPosition());
            if (imgThumbnail != null) {
                img = imgThumbnail;
                double downsample = (double) server.getWidth() / imgThumbnail.getWidth();
                xi = (int) (xx / downsample + .5);
                yi = (int) (yy / downsample + .5);
            }
        } else {
            xi = (int) ((xx - request.getX()) / request.getDownsample());
            yi = (int) ((yy - request.getY()) / request.getDownsample());
        }
        if (img != null) {
            // Make sure we are within range
            xi = Math.min(xi, img.getWidth() - 1);
            yi = Math.min(yi, img.getHeight() - 1);
            // Get the value, having applied any required color transforms
            if (imageDisplay != null)
                s = imageDisplay.getTransformedValueAsString(img, xi, yi);
        }
    }
    // Append z, t position if required
    String zString = null;
    if (server.nZSlices() > 1) {
        double zSpacing = server.getPixelCalibration().getZSpacingMicrons();
        if (!useCalibratedUnits || Double.isNaN(zSpacing))
            zString = "z = " + getZPosition();
        else
            zString = String.format("z = %.2f %s", getZPosition() * zSpacing, GeneralTools.micrometerSymbol());
    }
    String tString = null;
    if (server.nTimepoints() > 1) {
        // TODO: Consider use of TimeUnit
        // TimeUnit timeUnit = server.getTimeUnit();
        // if (!useMicrons || timeUnit == null)
        tString = "t = " + getTPosition();
    // else
    // tString = String.format("z = %.2f %s", getTPosition(), timeUnit.toString());
    }
    String dimensionString;
    if (tString == null && zString == null)
        dimensionString = "";
    else {
        dimensionString = "\n";
        if (zString != null) {
            dimensionString += zString;
            if (tString != null)
                dimensionString += ", " + tString;
        } else
            dimensionString += tString;
    }
    if (s != null)
        return String.format("%s%.2f, %.2f %s\n%s%s", prefix, xDisplay, yDisplay, units, s, dimensionString);
    else
        return String.format("%s%.2f, %.2f %s%s", prefix, xDisplay, yDisplay, units, dimensionString);
// if (s != null)
// return String.format("<html><center>%.2f, %.2f %s<br>%s", xDisplay, yDisplay, units, s);
// else
// return String.format("<html><center>%.2f, %.2f %s", xDisplay, yDisplay, units);
}
Also used : TMACoreObject(qupath.lib.objects.TMACoreObject) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) PixelCalibration(qupath.lib.images.servers.PixelCalibration) RegionRequest(qupath.lib.regions.RegionRequest) BufferedImage(java.awt.image.BufferedImage)

Example 7 with TMAGrid

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

the class QuPathViewer method getTooltipText.

private String getTooltipText(final double x, final double y) {
    // Try to show which TMA core is selected - if we have a TMA image
    PathObjectHierarchy hierarchy = getHierarchy();
    TMAGrid tmaGrid = hierarchy == null ? null : hierarchy.getTMAGrid();
    if (tmaGrid != null) {
        Point2D p = componentPointToImagePoint(x, y, null, false);
        TMACoreObject core = PathObjectTools.getTMACoreForPixel(tmaGrid, p.getX(), p.getY());
        if (core != null) {
            if (core.isMissing())
                return String.format("TMA Core %s\n(missing)", core.getName());
            else
                return String.format("TMA Core %s", core.getName());
        }
    }
    return null;
}
Also used : PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Point2D(java.awt.geom.Point2D) TMACoreObject(qupath.lib.objects.TMACoreObject) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid)

Example 8 with TMAGrid

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

the class PathClassifierTools method runClassifier.

/**
 * Apply a classifier to the detection objects in a hierarchy.
 * @param hierarchy
 * @param classifier
 */
public static void runClassifier(final PathObjectHierarchy hierarchy, final PathObjectClassifier classifier) {
    // Apply classifier to everything
    // If we have a TMA grid, do one core at a time
    long startTime = System.currentTimeMillis();
    TMAGrid tmaGrid = hierarchy.getTMAGrid();
    Collection<PathObject> pathObjects = new ArrayList<>();
    int nClassified = 0;
    // tmaGrid = null;
    if (tmaGrid != null) {
        for (TMACoreObject core : tmaGrid.getTMACoreList()) {
            pathObjects = PathObjectTools.getDescendantObjects(core, pathObjects, PathDetectionObject.class);
            nClassified += classifier.classifyPathObjects(pathObjects);
            pathObjects.clear();
        }
    } else {
        hierarchy.getObjects(pathObjects, PathDetectionObject.class);
        nClassified = classifier.classifyPathObjects(pathObjects);
    }
    long endTime = System.currentTimeMillis();
    logger.info(String.format("Classification time: %.2f seconds", (endTime - startTime) / 1000.));
    // Fire a change event for all detection objects
    if (nClassified > 0)
        hierarchy.fireObjectClassificationsChangedEvent(classifier, hierarchy.getObjects(null, PathDetectionObject.class));
    else
        logger.warn("No objects classified!");
}
Also used : PathDetectionObject(qupath.lib.objects.PathDetectionObject) PathObject(qupath.lib.objects.PathObject) TMACoreObject(qupath.lib.objects.TMACoreObject) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) ArrayList(java.util.ArrayList)

Example 9 with TMAGrid

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

the class QP method relabelTMAGrid.

/**
 * Relabel a TMA grid.  This will only be effective if enough labels are supplied for the full grid - otherwise no changes will be made.
 * <p>
 * For a TMA core at column c and row r, the label format will be 'Hc-Vr' or 'Hc-Vr', where H is the horizontal label and V the vertical label,
 * depending upon the status of the 'rowFirst' flag.
 * <p>
 * An examples of label would be 'A-1', 'A-2', 'B-1', 'B-2' etc.
 *
 * @param hierarchy The hierarchy containing the TMA grid to be relabelled.
 * @param labelsHorizontal A String containing labels for each TMA column, separated by spaces, or a numeric or alphabetic range (e.g. 1-10, or A-G)
 * @param labelsVertical A String containing labels for each TMA row, separated by spaces, or a numeric or alphabetic range (e.g. 1-10, or A-G)
 * @param rowFirst TRUE if the horizontal label should be added before the vertical label, FALSE otherwise
 * @return TRUE if there were sufficient horizontal and vertical labels to label the entire grid, FALSE otherwise.
 */
public static boolean relabelTMAGrid(final PathObjectHierarchy hierarchy, final String labelsHorizontal, final String labelsVertical, final boolean rowFirst) {
    if (hierarchy == null || hierarchy.getTMAGrid() == null) {
        logger.error("Cannot relabel TMA grid - no grid found!");
        return false;
    }
    TMAGrid grid = hierarchy.getTMAGrid();
    String[] columnLabels = PathObjectTools.parseTMALabelString(labelsHorizontal);
    String[] rowLabels = PathObjectTools.parseTMALabelString(labelsVertical);
    if (columnLabels.length < grid.getGridWidth()) {
        logger.error("Cannot relabel full TMA grid - not enough column labels specified!");
        return false;
    }
    if (rowLabels.length < grid.getGridHeight()) {
        logger.error("Cannot relabel full TMA grid - not enough row labels specified!");
        return false;
    }
    for (int r = 0; r < grid.getGridHeight(); r++) {
        for (int c = 0; c < grid.getGridWidth(); c++) {
            String name;
            if (rowFirst)
                name = rowLabels[r] + "-" + columnLabels[c];
            else
                name = columnLabels[c] + "-" + rowLabels[r];
            grid.getTMACore(r, c).setName(name);
        }
    }
    hierarchy.fireObjectsChangedEvent(null, new ArrayList<>(grid.getTMACoreList()));
    return true;
}
Also used : TMAGrid(qupath.lib.objects.hierarchy.TMAGrid)

Example 10 with TMAGrid

use of qupath.lib.objects.hierarchy.TMAGrid 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)

Aggregations

TMAGrid (qupath.lib.objects.hierarchy.TMAGrid)17 TMACoreObject (qupath.lib.objects.TMACoreObject)11 ArrayList (java.util.ArrayList)7 DefaultTMAGrid (qupath.lib.objects.hierarchy.DefaultTMAGrid)7 PathObjectHierarchy (qupath.lib.objects.hierarchy.PathObjectHierarchy)7 PathObject (qupath.lib.objects.PathObject)5 BufferedImage (java.awt.image.BufferedImage)4 IOException (java.io.IOException)4 File (java.io.File)3 HashMap (java.util.HashMap)3 List (java.util.List)3 RunningStatistics (qupath.lib.analysis.stats.RunningStatistics)2 RegionRequest (qupath.lib.regions.RegionRequest)2 Graphics2D (java.awt.Graphics2D)1 Point2D (java.awt.geom.Point2D)1 FileNotFoundException (java.io.FileNotFoundException)1 PrintWriter (java.io.PrintWriter)1 Arrays (java.util.Arrays)1 HashSet (java.util.HashSet)1 LinkedHashMap (java.util.LinkedHashMap)1