Search in sources :

Example 6 with ImageData

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

the class PathIO method readImageDataSerialized.

@SuppressWarnings("unchecked")
private static <T> ImageData<T> readImageDataSerialized(final InputStream stream, ImageData<T> imageData, ImageServer<T> server, Class<T> cls) throws IOException {
    long startTime = System.currentTimeMillis();
    Locale locale = Locale.getDefault(Category.FORMAT);
    boolean localeChanged = false;
    try (ObjectInputStream inStream = new ObjectInputStream(new BufferedInputStream(stream))) {
        ServerBuilder<T> serverBuilder = null;
        PathObjectHierarchy hierarchy = null;
        ImageData.ImageType imageType = null;
        ColorDeconvolutionStains stains = null;
        Workflow workflow = null;
        Map<String, Object> propertyMap = null;
        String firstLine = inStream.readUTF();
        // int versionNumber = -1;
        if (!firstLine.startsWith("Data file version")) {
            logger.error("Input stream does not contain valid QuPath data!");
        }
        // else {
        // // Could try to parse version number... although frankly, at this time, we don't really care...
        // try {
        // versionNumber = NumberFormat.getInstance(Locale.US).parse(firstLine.substring("Data file version".length()).trim()).intValue();
        // } catch (Exception e) {
        // logger.warn("Unable to parse version number from {}", firstLine);
        // }
        // }
        String serverString = (String) inStream.readObject();
        // Don't log warnings if we are provided with a server
        serverBuilder = extractServerBuilder(serverString, server == null);
        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);
                // If we have a Locale, then set it
                if (input instanceof Locale) {
                    if (input != locale) {
                        Locale.setDefault(Category.FORMAT, (Locale) input);
                        localeChanged = true;
                    }
                } else if (input instanceof PathObjectHierarchy)
                    hierarchy = (PathObjectHierarchy) input;
                else if (input instanceof ImageData.ImageType)
                    imageType = (ImageData.ImageType) input;
                else if (input instanceof String && "EOF".equals(input)) {
                    // else if ("EOF".equals(input)) {
                    break;
                // }
                } else if (input instanceof ColorDeconvolutionStains)
                    stains = (ColorDeconvolutionStains) input;
                else if (input instanceof Workflow)
                    workflow = (Workflow) input;
                else if (input instanceof Map)
                    propertyMap = (Map<String, Object>) input;
                else if (input == null) {
                    logger.debug("Null object will be skipped");
                } else
                    logger.warn("Unsupported object of class {} will be skipped: {}", input.getClass().getName(), input);
            } catch (ClassNotFoundException e) {
                logger.error("Unable to find class: " + e.getLocalizedMessage(), e);
            } catch (EOFException e) {
                // Try to recover from EOFExceptions - we may already have enough info
                logger.error("Reached end of file...");
                if (hierarchy == null)
                    logger.error(e.getLocalizedMessage(), e);
                break;
            }
        }
        // Create an entirely new ImageData if necessary
        var existingBuilder = imageData == null || imageData.getServer() == null ? null : imageData.getServer().getBuilder();
        if (imageData == null || !Objects.equals(serverBuilder, existingBuilder)) {
            // Create a new server if we need to
            if (server == null) {
                try {
                    server = serverBuilder.build();
                } catch (Exception e) {
                    logger.error(e.getLocalizedMessage());
                }
                ;
                if (server == null) {
                    logger.error("Warning: Unable to build server with " + serverBuilder);
                // throw new RuntimeException("Warning: Unable to create server for path " + serverPath);
                }
            }
            // TODO: Make this less clumsy... but for now we need to ensure we have a fully-initialized hierarchy (which deserialization alone doesn't achieve)
            PathObjectHierarchy hierarchy2 = new PathObjectHierarchy();
            hierarchy2.setHierarchy(hierarchy);
            hierarchy = hierarchy2;
            imageData = new ImageData<>(server, hierarchy, imageType);
        } else {
            if (imageType != null)
                imageData.setImageType(imageType);
            // Set the new hierarchy
            if (hierarchy != null)
                imageData.getHierarchy().setHierarchy(hierarchy);
        }
        // Set the other properties we have just read
        if (workflow != null) {
            imageData.getHistoryWorkflow().clear();
            imageData.getHistoryWorkflow().addSteps(workflow.getSteps());
        }
        if (stains != null) {
            imageData.setColorDeconvolutionStains(stains);
        }
        if (propertyMap != null) {
            for (Entry<String, Object> entry : propertyMap.entrySet()) imageData.setProperty(entry.getKey(), entry.getValue());
        }
        long endTime = System.currentTimeMillis();
        // if (hierarchy == null) {
        // logger.error(String.format("%s does not contain a valid QUPath object hierarchy!", file.getAbsolutePath()));
        // return null;
        // }
        logger.debug(String.format("Hierarchy with %d object(s) read in %.2f seconds", hierarchy.nObjects(), (endTime - startTime) / 1000.));
    } catch (ClassNotFoundException e1) {
        logger.warn("Class not found reading image data", e1);
    } finally {
        if (localeChanged)
            Locale.setDefault(Category.FORMAT, locale);
    }
    return imageData;
}
Also used : Locale(java.util.Locale) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Workflow(qupath.lib.plugins.workflow.Workflow) EOFException(java.io.EOFException) FileNotFoundException(java.io.FileNotFoundException) IOException(java.io.IOException) BufferedInputStream(java.io.BufferedInputStream) ImageData(qupath.lib.images.ImageData) EOFException(java.io.EOFException) PathObject(qupath.lib.objects.PathObject) Map(java.util.Map) HashMap(java.util.HashMap) ObjectInputStream(java.io.ObjectInputStream) ColorDeconvolutionStains(qupath.lib.color.ColorDeconvolutionStains)

Example 7 with ImageData

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

the class GuiTools method promptToClearAllSelectedObjects.

/**
 * Prompt user to select all currently-selected objects (except TMA core objects).
 *
 * @param imageData
 * @return
 */
public static boolean promptToClearAllSelectedObjects(final ImageData<?> imageData) {
    // Get all non-TMA core objects
    PathObjectHierarchy hierarchy = imageData.getHierarchy();
    Collection<PathObject> selectedRaw = hierarchy.getSelectionModel().getSelectedObjects();
    List<PathObject> selected = selectedRaw.stream().filter(p -> !(p instanceof TMACoreObject)).collect(Collectors.toList());
    if (selected.isEmpty()) {
        if (selectedRaw.size() > selected.size())
            Dialogs.showErrorMessage("Delete selected objects", "No valid objects selected! \n\nNote: Individual TMA cores cannot be deleted with this method.");
        else
            Dialogs.showErrorMessage("Delete selected objects", "No objects selected!");
        return false;
    }
    int n = selected.size();
    String message;
    if (n == 1)
        message = "Delete selected object?";
    else
        message = "Delete " + n + " selected objects?";
    if (Dialogs.showYesNoDialog("Delete objects", message)) {
        // Check for descendants
        List<PathObject> children = new ArrayList<>();
        for (PathObject temp : selected) {
            children.addAll(temp.getChildObjects());
        }
        children.removeAll(selected);
        boolean keepChildren = true;
        if (!children.isEmpty()) {
            Dialogs.DialogButton response = Dialogs.showYesNoCancelDialog("Delete objects", "Keep descendant objects?");
            if (response == Dialogs.DialogButton.CANCEL)
                return false;
            keepChildren = response == Dialogs.DialogButton.YES;
        }
        hierarchy.removeObjects(selected, keepChildren);
        hierarchy.getSelectionModel().clearSelection();
        imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Delete selected objects", "clearSelectedObjects(" + keepChildren + ");"));
        if (keepChildren)
            logger.info(selected.size() + " object(s) deleted");
        else
            logger.info(selected.size() + " object(s) deleted with descendants");
        imageData.getHistoryWorkflow().addStep(new DefaultScriptableWorkflowStep("Delete selected objects", "clearSelectedObjects();"));
        logger.info(selected.size() + " object(s) deleted");
        return true;
    } else
        return false;
}
Also used : CheckComboBox(org.controlsfx.control.CheckComboBox) UnaryOperator(java.util.function.UnaryOperator) TextFormatter(javafx.scene.control.TextFormatter) PathRootObject(qupath.lib.objects.PathRootObject) ParameterList(qupath.lib.plugins.parameters.ParameterList) PointsROI(qupath.lib.roi.PointsROI) Matcher(java.util.regex.Matcher) Action(java.awt.Desktop.Action) ColorTools(qupath.lib.common.ColorTools) GraphicsContext(javafx.scene.canvas.GraphicsContext) Canvas(javafx.scene.canvas.Canvas) Screen(javafx.stage.Screen) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) Platform(javafx.application.Platform) CountDownLatch(java.util.concurrent.CountDownLatch) Transparency(java.awt.Transparency) Clipboard(javafx.scene.input.Clipboard) SimpleDoubleProperty(javafx.beans.property.SimpleDoubleProperty) StringProperty(javafx.beans.property.StringProperty) Callable(java.util.concurrent.Callable) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) DefaultColorDeconvolutionStains(qupath.lib.color.ColorDeconvolutionStains.DefaultColorDeconvolutionStains) Bindings(javafx.beans.binding.Bindings) NumberFormat(java.text.NumberFormat) ArrayList(java.util.ArrayList) LinkedHashMap(java.util.LinkedHashMap) Slider(javafx.scene.control.Slider) SplitAnnotationsPlugin(qupath.lib.plugins.objects.SplitAnnotationsPlugin) GridPane(javafx.scene.layout.GridPane) Color(javafx.scene.paint.Color) GeneralTools(qupath.lib.common.GeneralTools) Commands(qupath.lib.gui.commands.Commands) Node(javafx.scene.Node) CheckBox(javafx.scene.control.CheckBox) IOException(java.io.IOException) File(java.io.File) PathObjectTools(qupath.lib.objects.PathObjectTools) Menu(javafx.scene.control.Menu) ROI(qupath.lib.roi.interfaces.ROI) SimpleObjectProperty(javafx.beans.property.SimpleObjectProperty) ObservableValue(javafx.beans.value.ObservableValue) Image(javafx.scene.image.Image) Button(javafx.scene.control.Button) ImageServer(qupath.lib.images.servers.ImageServer) CombineOp(qupath.lib.roi.RoiTools.CombineOp) DoubleBinding(javafx.beans.binding.DoubleBinding) ListCell(javafx.scene.control.ListCell) CheckMenuItem(javafx.scene.control.CheckMenuItem) LoggerFactory(org.slf4j.LoggerFactory) RenderingHints(java.awt.RenderingHints) Side(javafx.geometry.Side) ContextMenu(javafx.scene.control.ContextMenu) URI(java.net.URI) QuPathGUI(qupath.lib.gui.QuPathGUI) TextField(javafx.scene.control.TextField) MenuItem(javafx.scene.control.MenuItem) BufferedImage(java.awt.image.BufferedImage) Collection(java.util.Collection) Spinner(javafx.scene.control.Spinner) Collectors(java.util.stream.Collectors) StainVector(qupath.lib.color.StainVector) PathObject(qupath.lib.objects.PathObject) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer) List(java.util.List) Robot(javafx.scene.robot.Robot) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) Pattern(java.util.regex.Pattern) ClipboardContent(javafx.scene.input.ClipboardContent) Scene(javafx.scene.Scene) ListView(javafx.scene.control.ListView) TextArea(javafx.scene.control.TextArea) ParsePosition(java.text.ParsePosition) DoubleProperty(javafx.beans.property.DoubleProperty) ColorDeconvolutionHelper(qupath.lib.color.ColorDeconvolutionHelper) Function(java.util.function.Function) Dialogs(qupath.lib.gui.dialogs.Dialogs) ColorDeconvolutionStains(qupath.lib.color.ColorDeconvolutionStains) SwingUtilities(javax.swing.SwingUtilities) Graphics2D(java.awt.Graphics2D) ActionTools(qupath.lib.gui.ActionTools) Tooltip(javafx.scene.control.Tooltip) ColorPicker(javafx.scene.control.ColorPicker) ImageData(qupath.lib.images.ImageData) Desktop(java.awt.Desktop) ObjectProperty(javafx.beans.property.ObjectProperty) Logger(org.slf4j.Logger) Label(javafx.scene.control.Label) WritableImage(javafx.scene.image.WritableImage) TMACoreObject(qupath.lib.objects.TMACoreObject) DoubleSpinnerValueFactory(javafx.scene.control.SpinnerValueFactory.DoubleSpinnerValueFactory) SimpleBooleanProperty(javafx.beans.property.SimpleBooleanProperty) Stage(javafx.stage.Stage) SpinnerValueFactory(javafx.scene.control.SpinnerValueFactory) SwingFXUtils(javafx.embed.swing.SwingFXUtils) Window(javafx.stage.Window) Collections(java.util.Collections) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) TMACoreObject(qupath.lib.objects.TMACoreObject) ArrayList(java.util.ArrayList) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) PathObject(qupath.lib.objects.PathObject) Dialogs(qupath.lib.gui.dialogs.Dialogs)

Example 8 with ImageData

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

the class QuPathViewer method setImageData.

/**
 * Set the current image for this viewer.
 * @param imageDataNew
 */
public void setImageData(ImageData<BufferedImage> imageDataNew) {
    if (this.imageDataProperty.get() == imageDataNew)
        return;
    imageDataChanging.set(true);
    // Remove listeners for previous hierarchy
    ImageData<BufferedImage> imageDataOld = this.imageDataProperty.get();
    if (imageDataOld != null) {
        imageDataOld.getHierarchy().removePathObjectListener(this);
        imageDataOld.getHierarchy().getSelectionModel().removePathObjectSelectionListener(this);
    }
    // Determine if the server has remained the same, so we can avoid shifting the viewer
    boolean sameServer = false;
    if (imageDataOld != null && imageDataNew != null && imageDataOld.getServerPath().equals(imageDataNew.getServerPath()))
        sameServer = true;
    this.imageDataProperty.set(imageDataNew);
    ImageServer<BufferedImage> server = imageDataNew == null ? null : imageDataNew.getServer();
    PathObjectHierarchy hierarchy = imageDataNew == null ? null : imageDataNew.getHierarchy();
    long startTime = System.currentTimeMillis();
    if (imageDisplay != null) {
        boolean keepDisplay = PathPrefs.keepDisplaySettingsProperty().get();
        // This is a bit of a hack to avoid calling internal methods for ImageDisplay
        // See https://github.com/qupath/qupath/issues/601
        boolean displaySet = false;
        if (imageDataNew != null && keepDisplay) {
            if (imageDisplay.getImageData() != null && serversCompatible(imageDataNew.getServer(), imageDisplay.getImageData().getServer())) {
                imageDisplay.setImageData(imageDataNew, keepDisplay);
                displaySet = true;
            } else {
                for (var viewer : QuPathGUI.getInstance().getViewers()) {
                    if (this == viewer || viewer.getImageData() == null)
                        continue;
                    var tempServer = viewer.getServer();
                    var currentServer = imageDataNew.getServer();
                    if (serversCompatible(tempServer, currentServer)) {
                        var json = viewer.getImageDisplay().toJSON(false);
                        imageDataNew.setProperty(ImageDisplay.class.getName(), json);
                        imageDisplay.setImageData(imageDataNew, false);
                        displaySet = true;
                        break;
                    }
                }
            }
        }
        if (!displaySet)
            imageDisplay.setImageData(imageDataNew, keepDisplay);
        // See https://github.com/qupath/qupath/issues/843
        if (server != null && !server.isRGB()) {
            var colors = imageDisplay.availableChannels().stream().filter(c -> c instanceof DirectServerChannelInfo).map(c -> c.getColor()).collect(Collectors.toList());
            if (server.nChannels() == colors.size())
                updateServerChannels(server, colors);
        }
    }
    long endTime = System.currentTimeMillis();
    logger.debug("Setting ImageData time: {} ms", endTime - startTime);
    initializeForServer(server);
    if (!sameServer) {
        setDownsampleFactorImpl(getZoomToFitDownsampleFactor(), -1, -1);
        centerImage();
    }
    fireImageDataChanged(imageDataOld, imageDataNew);
    if (imageDataNew != null) {
        // hierarchyPainter = new PathHierarchyPainter(hierarchy);
        hierarchy.addPathObjectListener(this);
        hierarchy.getSelectionModel().addPathObjectSelectionListener(this);
    }
    setSelectedObject(null);
    // TODO: Consider shifting, fixing magnification, repainting etc.
    if (isShowing())
        repaint();
    logger.info("Image data set to {}", imageDataNew);
}
Also used : Color(java.awt.Color) GridOverlay(qupath.lib.gui.viewer.overlays.GridOverlay) Change(javafx.collections.ListChangeListener.Change) Rectangle2D(java.awt.geom.Rectangle2D) StackPane(javafx.scene.layout.StackPane) ColorConvertOp(java.awt.image.ColorConvertOp) ListChangeListener(javafx.collections.ListChangeListener) ImageRegion(qupath.lib.regions.ImageRegion) SimpleIntegerProperty(javafx.beans.property.SimpleIntegerProperty) ColorToolsAwt(qupath.lib.color.ColorToolsAwt) ColorTools(qupath.lib.common.ColorTools) GraphicsContext(javafx.scene.canvas.GraphicsContext) Set(java.util.Set) Canvas(javafx.scene.canvas.Canvas) KeyEvent(javafx.scene.input.KeyEvent) AffineTransform(java.awt.geom.AffineTransform) Observable(javafx.beans.Observable) AbstractOverlay(qupath.lib.gui.viewer.overlays.AbstractOverlay) ImageInputStream(javax.imageio.stream.ImageInputStream) Platform(javafx.application.Platform) RoiEditor(qupath.lib.roi.RoiEditor) BooleanProperty(javafx.beans.property.BooleanProperty) SimpleDoubleProperty(javafx.beans.property.SimpleDoubleProperty) ObservableList(javafx.collections.ObservableList) RectangleROI(qupath.lib.roi.RectangleROI) FXCollections(javafx.collections.FXCollections) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) IntegerProperty(javafx.beans.property.IntegerProperty) ArrayList(java.util.ArrayList) AlphaComposite(java.awt.AlphaComposite) TextAlignment(javafx.scene.text.TextAlignment) Stroke(java.awt.Stroke) PathOverlay(qupath.lib.gui.viewer.overlays.PathOverlay) ImageReader(javax.imageio.ImageReader) GeneralTools(qupath.lib.common.GeneralTools) RegionRequest(qupath.lib.regions.RegionRequest) AwtTools(qupath.lib.awt.common.AwtTools) PathObjectSelectionListener(qupath.lib.objects.hierarchy.events.PathObjectSelectionListener) IOException(java.io.IOException) File(java.io.File) PathObjectTools(qupath.lib.objects.PathObjectTools) Cursor(javafx.scene.Cursor) ROI(qupath.lib.roi.interfaces.ROI) PathTools(qupath.lib.gui.viewer.tools.PathTools) SimpleObjectProperty(javafx.beans.property.SimpleObjectProperty) PixelCalibration(qupath.lib.images.servers.PixelCalibration) ObservableValue(javafx.beans.value.ObservableValue) PathObjectHierarchyListener(qupath.lib.objects.hierarchy.events.PathObjectHierarchyListener) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) PathPrefs(qupath.lib.gui.prefs.PathPrefs) LookupOp(java.awt.image.LookupOp) ImageServerMetadata(qupath.lib.images.servers.ImageServerMetadata) EventHandler(javafx.event.EventHandler) ImageServer(qupath.lib.images.servers.ImageServer) Point2D(java.awt.geom.Point2D) IIOMetadata(javax.imageio.metadata.IIOMetadata) LoggerFactory(org.slf4j.LoggerFactory) RenderingHints(java.awt.RenderingHints) TMAGridOverlay(qupath.lib.gui.viewer.overlays.TMAGridOverlay) PixelClassificationOverlay(qupath.lib.gui.viewer.overlays.PixelClassificationOverlay) InvalidationListener(javafx.beans.InvalidationListener) DefaultImageRegionStore(qupath.lib.gui.images.stores.DefaultImageRegionStore) ObservableMeasurementTableData(qupath.lib.gui.measure.ObservableMeasurementTableData) PathObjectHierarchyEvent(qupath.lib.objects.hierarchy.events.PathObjectHierarchyEvent) DirectServerChannelInfo(qupath.lib.display.DirectServerChannelInfo) ImageIO(javax.imageio.ImageIO) NoninvertibleTransformException(java.awt.geom.NoninvertibleTransformException) Method(java.lang.reflect.Method) QuPathGUI(qupath.lib.gui.QuPathGUI) Pane(javafx.scene.layout.Pane) MoveTool(qupath.lib.gui.viewer.tools.MoveTool) Shape(java.awt.Shape) Composite(java.awt.Composite) Image(java.awt.Image) BufferedImage(java.awt.image.BufferedImage) Collection(java.util.Collection) Collectors(java.util.stream.Collectors) PathDetectionObject(qupath.lib.objects.PathDetectionObject) PathObject(qupath.lib.objects.PathObject) Objects(java.util.Objects) List(java.util.List) GuiTools(qupath.lib.gui.tools.GuiTools) ColorToolsFX(qupath.lib.gui.tools.ColorToolsFX) Graphics(java.awt.Graphics) ImagePlane(qupath.lib.regions.ImagePlane) SimpleLongProperty(javafx.beans.property.SimpleLongProperty) ReadOnlyLongProperty(javafx.beans.property.ReadOnlyLongProperty) Rectangle(java.awt.Rectangle) ReadOnlyObjectProperty(javafx.beans.property.ReadOnlyObjectProperty) MouseEvent(javafx.scene.input.MouseEvent) ImageChannel(qupath.lib.images.servers.ImageChannel) DoubleProperty(javafx.beans.property.DoubleProperty) ImageRenderer(qupath.lib.gui.images.stores.ImageRenderer) ByteLookupTable(java.awt.image.ByteLookupTable) ColorSpace(java.awt.color.ColorSpace) ImageDisplay(qupath.lib.display.ImageDisplay) ImageRegionStoreHelpers(qupath.lib.gui.images.stores.ImageRegionStoreHelpers) HashSet(java.util.HashSet) Graphics2D(java.awt.Graphics2D) Tooltip(javafx.scene.control.Tooltip) ImageData(qupath.lib.images.ImageData) ICC_Profile(java.awt.color.ICC_Profile) KeyCode(javafx.scene.input.KeyCode) ObjectProperty(javafx.beans.property.ObjectProperty) Logger(org.slf4j.Logger) Iterator(java.util.Iterator) WritableImage(javafx.scene.image.WritableImage) HierarchyOverlay(qupath.lib.gui.viewer.overlays.HierarchyOverlay) LongProperty(javafx.beans.property.LongProperty) PathHierarchyImageServer(qupath.lib.gui.images.servers.PathHierarchyImageServer) TMACoreObject(qupath.lib.objects.TMACoreObject) SimpleBooleanProperty(javafx.beans.property.SimpleBooleanProperty) SwingFXUtils(javafx.embed.swing.SwingFXUtils) ChangeListener(javafx.beans.value.ChangeListener) Collections(java.util.Collections) TileListener(qupath.lib.gui.images.stores.TileListener) PathTool(qupath.lib.gui.viewer.tools.PathTool) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) DirectServerChannelInfo(qupath.lib.display.DirectServerChannelInfo) BufferedImage(java.awt.image.BufferedImage) ImageDisplay(qupath.lib.display.ImageDisplay)

Example 9 with ImageData

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

the class MeasurementExporter method exportMeasurements.

/**
 * Exports the measurements of one or more entries in the project.
 * This function first opens all the images in the project to store
 * all the column names and values of the measurements.
 * Then, it loops through the maps containing the values to write
 * them to the given output stream.
 * @param stream
 */
public void exportMeasurements(OutputStream stream) {
    long startTime = System.currentTimeMillis();
    Map<ProjectImageEntry<?>, String[]> imageCols = new HashMap<>();
    Map<ProjectImageEntry<?>, Integer> nImageEntries = new HashMap<>();
    List<String> allColumns = new ArrayList<>();
    Multimap<String, String> valueMap = LinkedListMultimap.create();
    String pattern = "(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)";
    for (ProjectImageEntry<?> entry : imageList) {
        try {
            ImageData<?> imageData = entry.readImageData();
            ObservableMeasurementTableData model = new ObservableMeasurementTableData();
            Collection<PathObject> pathObjects = imageData == null ? Collections.emptyList() : imageData.getHierarchy().getObjects(null, type);
            if (filter != null)
                pathObjects = pathObjects.stream().filter(filter).collect(Collectors.toList());
            model.setImageData(imageData, pathObjects);
            List<String> data = SummaryMeasurementTableCommand.getTableModelStrings(model, separator, excludeColumns);
            // Get header
            String[] header;
            String headerString = data.get(0);
            if (headerString.chars().filter(e -> e == '"').count() > 1)
                header = headerString.split(separator.equals("\t") ? "\\" + separator : separator + pattern, -1);
            else
                header = headerString.split(separator);
            imageCols.put(entry, header);
            nImageEntries.put(entry, data.size() - 1);
            for (String col : header) {
                if (!allColumns.contains(col) && !excludeColumns.contains(col))
                    allColumns.add(col);
            }
            // To keep the same column order, just delete non-relevant columns
            if (!includeOnlyColumns.isEmpty())
                allColumns.removeIf(n -> !includeOnlyColumns.contains(n));
            for (int i = 1; i < data.size(); i++) {
                String[] row;
                String rowString = data.get(i);
                // Check if some values in the row are escaped
                if (rowString.chars().filter(e -> e == '"').count() > 1)
                    row = rowString.split(separator.equals("\t") ? "\\" + separator : separator + pattern, -1);
                else
                    row = rowString.split(separator);
                // Put value in map
                for (int elem = 0; elem < row.length; elem++) {
                    if (allColumns.contains(header[elem]))
                        valueMap.put(header[elem], row[elem]);
                }
            }
        } catch (Exception e) {
            logger.error(e.getLocalizedMessage(), e);
        }
    }
    try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(stream, StandardCharsets.UTF_8))) {
        writer.write(String.join(separator, allColumns));
        writer.write(System.lineSeparator());
        Iterator[] its = new Iterator[allColumns.size()];
        for (int col = 0; col < allColumns.size(); col++) {
            its[col] = valueMap.get(allColumns.get(col)).iterator();
        }
        for (ProjectImageEntry<?> entry : imageList) {
            for (int nObject = 0; nObject < nImageEntries.get(entry); nObject++) {
                for (int nCol = 0; nCol < allColumns.size(); nCol++) {
                    if (Arrays.stream(imageCols.get(entry)).anyMatch(allColumns.get(nCol)::equals)) {
                        String val = (String) its[nCol].next();
                        // NaN values -> blank
                        if (val.equals("NaN"))
                            val = "";
                        writer.write(val);
                    }
                    if (nCol < allColumns.size() - 1)
                        writer.write(separator);
                }
                writer.write(System.lineSeparator());
            }
        }
    } catch (Exception e) {
        logger.error("Error writing to file: " + e.getLocalizedMessage(), e);
    }
    long endTime = System.currentTimeMillis();
    long timeMillis = endTime - startTime;
    String time = null;
    if (timeMillis > 1000 * 60)
        time = String.format("Total processing time: %.2f minutes", timeMillis / (1000.0 * 60.0));
    else if (timeMillis > 1000)
        time = String.format("Total processing time: %.2f seconds", timeMillis / (1000.0));
    else
        time = String.format("Total processing time: %d milliseconds", timeMillis);
    logger.info("Processed {} images", imageList.size());
    logger.info(time);
}
Also used : Arrays(java.util.Arrays) LoggerFactory(org.slf4j.LoggerFactory) HashMap(java.util.HashMap) Multimap(com.google.common.collect.Multimap) ArrayList(java.util.ArrayList) PathRootObject(qupath.lib.objects.PathRootObject) ObservableMeasurementTableData(qupath.lib.gui.measure.ObservableMeasurementTableData) Map(java.util.Map) OutputStreamWriter(java.io.OutputStreamWriter) OutputStream(java.io.OutputStream) PrintWriter(java.io.PrintWriter) LinkedListMultimap(com.google.common.collect.LinkedListMultimap) ImageData(qupath.lib.images.ImageData) Logger(org.slf4j.Logger) Iterator(java.util.Iterator) ProjectImageEntry(qupath.lib.projects.ProjectImageEntry) BufferedImage(java.awt.image.BufferedImage) Predicate(java.util.function.Predicate) Collection(java.util.Collection) FileOutputStream(java.io.FileOutputStream) Collectors(java.util.stream.Collectors) File(java.io.File) StandardCharsets(java.nio.charset.StandardCharsets) PathObject(qupath.lib.objects.PathObject) List(java.util.List) SummaryMeasurementTableCommand(qupath.lib.gui.commands.SummaryMeasurementTableCommand) Collections(java.util.Collections) PathPrefs(qupath.lib.gui.prefs.PathPrefs) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) ObservableMeasurementTableData(qupath.lib.gui.measure.ObservableMeasurementTableData) PathObject(qupath.lib.objects.PathObject) Iterator(java.util.Iterator) ProjectImageEntry(qupath.lib.projects.ProjectImageEntry) OutputStreamWriter(java.io.OutputStreamWriter) PrintWriter(java.io.PrintWriter)

Example 10 with ImageData

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

the class ObservableMeasurementTableDataTest method test.

@SuppressWarnings("javadoc")
@Test
public void test() {
    // See https://github.com/locationtech/jts/issues/571
    for (int counter = 0; counter < 50; counter++) {
        ImageData<BufferedImage> imageData = new ImageData<>(null);
        PathClass tumorClass = PathClassFactory.getPathClass(StandardPathClasses.TUMOR);
        PathClass stromaClass = PathClassFactory.getPathClass(StandardPathClasses.STROMA);
        // PathClass otherClass = PathClassFactory.getDefaultPathClass(PathClasses.OTHER);
        PathClass artefactClass = PathClassFactory.getPathClass("Artefact");
        PathObjectHierarchy hierarchy = imageData.getHierarchy();
        // Add a parent annotation
        PathObject parent = PathObjects.createAnnotationObject(ROIs.createRectangleROI(500, 500, 1000, 1000, ImagePlane.getDefaultPlane()));
        // Create 100 tumor detections
        // ROI emptyROI = ROIs.createEmptyROI();
        ROI smallROI = ROIs.createRectangleROI(500, 500, 1, 1, ImagePlane.getDefaultPlane());
        for (int i = 0; i < 100; i++) {
            if (i < 25)
                parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getNegative(tumorClass)));
            else if (i < 50)
                parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getOnePlus(tumorClass)));
            else if (i < 75)
                parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getTwoPlus(tumorClass)));
            else if (i < 100)
                parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getThreePlus(tumorClass)));
        }
        // Create 100 stroma detections
        for (int i = 0; i < 100; i++) {
            if (i < 50)
                parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getNegative(stromaClass)));
            else if (i < 60)
                parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getOnePlus(stromaClass)));
            else if (i < 70)
                parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getTwoPlus(stromaClass)));
            else if (i < 100)
                parent.addPathObject(PathObjects.createDetectionObject(smallROI, PathClassFactory.getThreePlus(stromaClass)));
        }
        // Create 50 artefact detections
        for (int i = 0; i < 50; i++) {
            parent.addPathObject(PathObjects.createDetectionObject(smallROI, artefactClass));
        }
        hierarchy.addPathObject(parent);
        ObservableMeasurementTableData model = new ObservableMeasurementTableData();
        model.setImageData(imageData, Collections.singletonList(parent));
        // Check tumor counts
        assertEquals(100, model.getNumericValue(parent, "Num Tumor (base)"), EPSILON);
        assertEquals(25, model.getNumericValue(parent, "Num Tumor: Negative"), EPSILON);
        assertEquals(25, model.getNumericValue(parent, "Num Tumor: 1+"), EPSILON);
        assertEquals(25, model.getNumericValue(parent, "Num Tumor: 2+"), EPSILON);
        assertEquals(25, model.getNumericValue(parent, "Num Tumor: 3+"), EPSILON);
        assertTrue(Double.isNaN(model.getNumericValue(parent, "Num Tumor: 4+")));
        // Check tumor H-score, Allred score & positive %
        assertEquals(150, model.getNumericValue(parent, "Tumor: H-score"), EPSILON);
        assertEquals(75, model.getNumericValue(parent, "Tumor: Positive %"), EPSILON);
        assertEquals(2, model.getNumericValue(parent, "Tumor: Allred intensity"), EPSILON);
        assertEquals(5, model.getNumericValue(parent, "Tumor: Allred proportion"), EPSILON);
        assertEquals(7, model.getNumericValue(parent, "Tumor: Allred score"), EPSILON);
        // Check tumor H-score unaffected when tumor detections added without intensity classification
        for (int i = 0; i < 10; i++) parent.addPathObject(PathObjects.createDetectionObject(smallROI, tumorClass));
        hierarchy.fireHierarchyChangedEvent(this);
        model.refreshEntries();
        // model.setImageData(imageData, Collections.singletonList(parent));
        assertEquals(100, model.getNumericValue(parent, "Num Stroma (base)"), EPSILON);
        assertEquals(50, model.getNumericValue(parent, "Num Stroma: Negative"), EPSILON);
        assertEquals(150, model.getNumericValue(parent, "Tumor: H-score"), EPSILON);
        assertEquals(75, model.getNumericValue(parent, "Tumor: Positive %"), EPSILON);
        // Check stroma scores
        assertEquals(100, model.getNumericValue(parent, "Num Stroma (base)"), EPSILON);
        assertEquals(120, model.getNumericValue(parent, "Stroma: H-score"), EPSILON);
        // Check complete scores
        assertEquals(135, model.getNumericValue(parent, "Stroma + Tumor: H-score"), EPSILON);
        // Add a new parent that completely contains the current object, and confirm complete scores agree
        PathObject parentNew = PathObjects.createAnnotationObject(ROIs.createRectangleROI(0, 0, 2000, 2000, ImagePlane.getDefaultPlane()));
        hierarchy.addPathObject(parentNew);
        model.refreshEntries();
        assertEquals(135, model.getNumericValue(parent, "Stroma + Tumor: H-score"), EPSILON);
        assertEquals(135, model.getNumericValue(parentNew, "Stroma + Tumor: H-score"), EPSILON);
        // Create a new object and demonstrate Allred dependence on a single cell
        PathObject parentAllred = PathObjects.createAnnotationObject(ROIs.createRectangleROI(4000, 4000, 1000, 1000, ImagePlane.getDefaultPlane()));
        ROI newROI = ROIs.createEllipseROI(4500, 4500, 10, 10, ImagePlane.getDefaultPlane());
        for (int i = 0; i < 100; i++) parentAllred.addPathObject(PathObjects.createDetectionObject(newROI, PathClassFactory.getNegative(tumorClass)));
        hierarchy.addPathObject(parentAllred);
        model.refreshEntries();
        assertEquals(0, model.getNumericValue(parentAllred, "Tumor: Allred score"), EPSILON);
        parentAllred.addPathObject(PathObjects.createDetectionObject(newROI, PathClassFactory.getThreePlus(tumorClass)));
        hierarchy.fireHierarchyChangedEvent(parentAllred);
        model.refreshEntries();
        assertEquals(4, model.getNumericValue(parentAllred, "Tumor: Allred score"), EPSILON);
    }
}
Also used : PathClass(qupath.lib.objects.classes.PathClass) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) ObservableMeasurementTableData(qupath.lib.gui.measure.ObservableMeasurementTableData) PathObject(qupath.lib.objects.PathObject) ImageData(qupath.lib.images.ImageData) ROI(qupath.lib.roi.interfaces.ROI) BufferedImage(java.awt.image.BufferedImage) Test(org.junit.jupiter.api.Test)

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