Search in sources :

Example 81 with PathObject

use of qupath.lib.objects.PathObject in project qupath by qupath.

the class PathObjectHierarchyView method selectSingleObject.

/**
 * Select just one object within the tree - everything else will be cleared.
 *
 * @param pathObjectSelected
 */
private void selectSingleObject(final PathObject pathObjectSelected) {
    if (pathObjectSelected == null)
        return;
    // Search for a path to select the object... opening the tree accordingly
    List<PathObject> ancestors = PathObjectTools.getAncestorList(pathObjectSelected);
    if (ancestors.isEmpty() || treeView.getRoot() == null)
        return;
    List<TreeItem<PathObject>> treeItems = new ArrayList<>();
    treeItems.add(treeView.getRoot());
    TreeItem<PathObject> deepestItem = null;
    while (!ancestors.isEmpty()) {
        PathObject pathObject = ancestors.remove(0);
        TreeItem<PathObject> found = null;
        for (TreeItem<PathObject> treeItem : treeItems) {
            if (treeItem.getValue() == pathObject) {
                found = treeItem;
                break;
            }
        }
        // We can't get any deeper
        if (found == null)
            return;
        deepestItem = found;
        treeItems = found.getChildren();
    }
    if (deepestItem != null && deepestItem.getValue() == pathObjectSelected) {
        TreeItem<PathObject> parent = deepestItem.getParent();
        while (parent != null) {
            parent.setExpanded(true);
            parent = parent.getParent();
        }
        // deepestItem.setExpanded(true);
        int row = treeView.getRow(deepestItem);
        treeView.getSelectionModel().clearAndSelect(row);
        treeView.scrollTo(row);
    }
}
Also used : PathObject(qupath.lib.objects.PathObject)

Example 82 with PathObject

use of qupath.lib.objects.PathObject in project qupath by qupath.

the class TestCompositeClassifier method test_classifyPathObjects.

@Test
public void test_classifyPathObjects() {
    MeasurementList ml1 = MeasurementListFactory.createMeasurementList(16, MeasurementList.MeasurementListType.GENERAL);
    MeasurementList ml2 = MeasurementListFactory.createMeasurementList(16, MeasurementList.MeasurementListType.GENERAL);
    MeasurementList ml3 = MeasurementListFactory.createMeasurementList(16, MeasurementList.MeasurementListType.GENERAL);
    MeasurementList ml4 = MeasurementListFactory.createMeasurementList(16, MeasurementList.MeasurementListType.GENERAL);
    // Adding measurement to list2 (all measurements)
    ml2.addMeasurement("intensityMeasurement1", 0.0);
    ml2.addMeasurement("intensityMeasurement2", 0.2);
    ml2.addMeasurement("intensityMeasurement3", 0.6);
    ml2.addMeasurement("intensityMeasurement4", -4.6);
    // Adding measurement to list3 (missing intensityMeasurement3)
    ml3.addMeasurement("intensityMeasurement1", -1.0);
    ml3.addMeasurement("intensityMeasurement2", 0.9999);
    ml3.addMeasurement("intensityMeasurement4", 0.999);
    // Adding measurement to list4 (missing intensityMeasurement4)
    ml4.addMeasurement("intensityMeasurement1", 0.2);
    ml4.addMeasurement("intensityMeasurement2", 0.3);
    ml4.addMeasurement("intensityMeasurement3", 0.5);
    // Create annotation objects
    PathObject obj1 = PathObjects.createAnnotationObject(ROIs.createRectangleROI(0, 0, 5, 5, ImagePlane.getDefaultPlane()), null, ml1);
    PathObject obj2 = PathObjects.createAnnotationObject(ROIs.createRectangleROI(10, 10, 5, 5, ImagePlane.getDefaultPlane()), pathClass1, ml2);
    PathObject obj3 = PathObjects.createAnnotationObject(ROIs.createEllipseROI(100, 100, 5, 5, ImagePlane.getDefaultPlane()), pathClass2, ml3);
    PathObject obj4 = PathObjects.createAnnotationObject(ROIs.createLineROI(0, 0, ImagePlane.getDefaultPlane()), pathClass3, ml4);
    // Classify objects and check classification manually
    var objs = Arrays.asList(obj1, obj2, obj3, obj4);
    // Composite classifier 1
    var classifiedObjs1 = cp1.classifyPathObjects(objs);
    // Last classifier classified one object only (obj4)
    assertEquals(1, classifiedObjs1);
    // Unchanged
    assertEquals(null, obj1.getPathClass());
    assertEquals(PathClassFactory.getNegative(pathClass1), obj2.getPathClass());
    assertEquals(PathClassFactory.getPositive(pathClass2), obj3.getPathClass());
    assertEquals(PathClassFactory.getPositive(pathClass3), obj4.getPathClass());
    // Composite classifier 2
    var classifiedObjs2 = cp2.classifyPathObjects(objs);
    assertEquals(4, classifiedObjs2);
    // Missing measurement -> unchanged
    assertEquals(null, obj1.getPathClass());
    assertEquals(PathClassFactory.getNegative(pathClass1), obj2.getPathClass());
    assertEquals(PathClassFactory.getPositive(pathClass2), obj3.getPathClass());
    // Missing measurement -> reset
    assertEquals(pathClass3, obj4.getPathClass());
    // Invalid composite classifier
    var classifiedObjs3 = cpInvalid.classifyPathObjects(objs);
    assertEquals(0, classifiedObjs3);
}
Also used : PathObject(qupath.lib.objects.PathObject) MeasurementList(qupath.lib.measurements.MeasurementList) Test(org.junit.jupiter.api.Test)

Example 83 with PathObject

use of qupath.lib.objects.PathObject in project qupath by qupath.

the class SummaryMeasurementTableCommand method showTable.

/**
 * Show a measurement table for the specified image data.
 * @param imageData the image data
 * @param type the object type to show
 */
public void showTable(ImageData<BufferedImage> imageData, Class<? extends PathObject> type) {
    if (imageData == null) {
        Dialogs.showNoImageError("Show measurement table");
        return;
    }
    final PathObjectHierarchy hierarchy = imageData.getHierarchy();
    ObservableMeasurementTableData model = new ObservableMeasurementTableData();
    model.setImageData(imageData, imageData == null ? Collections.emptyList() : imageData.getHierarchy().getObjects(null, type));
    SplitPane splitPane = new SplitPane();
    HistogramDisplay histogramDisplay = new HistogramDisplay(model, true);
    // table.setTableMenuButtonVisible(true);
    TableView<PathObject> table = new TableView<>();
    table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
    table.getSelectionModel().getSelectedItems().addListener(new ListChangeListener<PathObject>() {

        @Override
        public void onChanged(ListChangeListener.Change<? extends PathObject> c) {
            synchronizeSelectionModelToTable(hierarchy, c, table);
        }
    });
    StringProperty displayedName = new SimpleStringProperty(ServerTools.getDisplayableImageName(imageData.getServer()));
    var title = Bindings.createStringBinding(() -> {
        if (type == null)
            return "Results " + displayedName.get();
        else
            return PathObjectTools.getSuitableName(type, false) + " results - " + displayedName.get();
    }, displayedName);
    // Handle double-click as a way to center on a ROI
    // var enter = new KeyCodeCombination(KeyCode.ENTER);
    table.setRowFactory(params -> {
        var row = new TableRow<PathObject>();
        row.setOnMouseClicked(e -> {
            if (e.getClickCount() == 2) {
                maybeCenterROI(row.getItem());
            }
        });
        // });
        return row;
    });
    // Create columns according to the table model
    // for (int i = 0; i < model.getColumnCount(); i++) {
    // // Add string column
    // if (model.getColumnClass(i).equals(String.class)) {
    // TableColumn<PathObject, String> col = null;
    // col = new TableColumn<>(model.getColumnName(i));
    // col.setCellValueFactory(new Callback<CellDataFeatures<PathObject, String>, ObservableValue<String>>() {
    // public ObservableValue<String> call(CellDataFeatures<PathObject, String> val) {
    // return new SimpleStringProperty(val.getValue().getDisplayedName());
    // }
    // });
    // col.setCellFactory(column -> new BasicTableCell<String>());
    // table.getColumns().add(col);
    // }
    // }
    boolean tmaCoreList = TMACoreObject.class.isAssignableFrom(type);
    // Add TMA core columns, if suitable
    if (tmaCoreList) {
        TableColumn<PathObject, ROI> col = new TableColumn<>("Image");
        col.setCellValueFactory(val -> new SimpleObjectProperty<>(val.getValue().getROI()));
        double maxWidth = maxDimForTMACore;
        double padding = 10;
        col.setCellFactory(column -> new TMACoreTableCell(table, imageData.getServer(), maxWidth, padding));
        col.widthProperty().addListener((v, o, n) -> table.refresh());
        col.setMaxWidth(maxWidth + padding * 2);
        table.getColumns().add(col);
        // While here, make sure we have fewer bins - don't usually have all that many cores
        histogramDisplay.setNumBins(10);
    }
    // Create numeric columns
    for (String columnName : model.getAllNames()) {
        // Add column
        if (model.isStringMeasurement(columnName)) {
            TableColumn<PathObject, String> col = new TableColumn<>(columnName);
            col.setCellValueFactory(column -> model.createStringMeasurement(column.getValue(), column.getTableColumn().getText()));
            col.setCellFactory(column -> new BasicTableCell<>());
            table.getColumns().add(col);
        } else {
            TableColumn<PathObject, Number> col = new TableColumn<>(columnName);
            col.setCellValueFactory(column -> model.createNumericMeasurement(column.getValue(), column.getTableColumn().getText()));
            col.setCellFactory(column -> new NumericTableCell<PathObject>(histogramDisplay));
            table.getColumns().add(col);
        }
    }
    // Set the PathObjects - need to deal with sorting, since a FilteredList won't handle it directly
    SortedList<PathObject> items = new SortedList<>(model.getItems());
    items.comparatorProperty().bind(table.comparatorProperty());
    table.setItems(items);
    List<ButtonBase> buttons = new ArrayList<>();
    ToggleButton btnHistogram = new ToggleButton("Show histograms");
    btnHistogram.selectedProperty().addListener((v, o, n) -> {
        if (n) {
            Pane paneHistograms = histogramDisplay.getPane();
            splitPane.getItems().add(paneHistograms);
        } else if (histogramDisplay != null)
            splitPane.getItems().remove(histogramDisplay.getPane());
    });
    buttons.add(btnHistogram);
    // Button btnScatterplot = new Button("Show scatterplots");
    // btnScatterplot.setOnAction(e -> {
    // SwingUtilities.invokeLater(() -> {
    // JDialog dialog = new ScatterplotDisplay(null, "Scatterplots: " + displayedName, model).getDialog();
    // dialog.setLocationRelativeTo(null);
    // dialog.setVisible(true);
    // });
    // });
    // buttons.add(btnScatterplot);
    Button btnCopy = new Button("Copy to clipboard");
    btnCopy.setOnAction(e -> {
        // TODO: Deal with repetition immediately below...
        Set<String> excludeColumns = new HashSet<>();
        for (TableColumn<?, ?> col : table.getColumns()) {
            if (!col.isVisible())
                excludeColumns.add(col.getText());
        }
        copyTableContentsToClipboard(model, excludeColumns);
    });
    buttons.add(btnCopy);
    Button btnSave = new Button("Save");
    btnSave.setOnAction(e -> {
        Set<String> excludeColumns = new HashSet<>();
        for (TableColumn<?, ?> col : table.getColumns()) {
            if (!col.isVisible())
                excludeColumns.add(col.getText());
        }
        File fileOutput = promptForOutputFile();
        if (fileOutput == null)
            return;
        if (saveTableModel(model, fileOutput, excludeColumns)) {
            WorkflowStep step;
            String includeColumns;
            if (excludeColumns.isEmpty())
                includeColumns = "";
            else {
                List<String> includeColumnList = new ArrayList<>(model.getAllNames());
                includeColumnList.removeAll(excludeColumns);
                includeColumns = ", " + includeColumnList.stream().map(s -> "'" + s + "'").collect(Collectors.joining(", "));
            }
            String path = qupath.getProject() == null ? fileOutput.toURI().getPath() : fileOutput.getParentFile().toURI().getPath();
            if (type == TMACoreObject.class) {
                step = new DefaultScriptableWorkflowStep("Save TMA measurements", String.format("saveTMAMeasurements('%s'%s)", path, includeColumns));
            } else if (type == PathAnnotationObject.class) {
                step = new DefaultScriptableWorkflowStep("Save annotation measurements", String.format("saveAnnotationMeasurements('%s\'%s)", path, includeColumns));
            } else if (type == PathDetectionObject.class) {
                step = new DefaultScriptableWorkflowStep("Save detection measurements", String.format("saveDetectionMeasurements('%s'%s)", path, includeColumns));
            } else {
                step = new DefaultScriptableWorkflowStep("Save measurements", String.format("saveMeasurements('%s', %s%s)", path, type == null ? null : type.getName(), includeColumns));
            }
            imageData.getHistoryWorkflow().addStep(step);
        }
    });
    buttons.add(btnSave);
    Stage frame = new Stage();
    frame.initOwner(qupath.getStage());
    frame.titleProperty().bind(title);
    BorderPane paneTable = new BorderPane();
    paneTable.setCenter(table);
    // Add text field to filter visible columns
    TextField tfColumnFilter = new TextField();
    GridPane paneFilter = new GridPane();
    paneFilter.add(new Label("Column filter"), 0, 0);
    paneFilter.add(tfColumnFilter, 1, 0);
    GridPane.setHgrow(tfColumnFilter, Priority.ALWAYS);
    paneFilter.setHgap(5);
    if (tmaCoreList) {
        CheckBox cbHideMissing = new CheckBox("Hide missing cores");
        paneFilter.add(cbHideMissing, 2, 0);
        cbHideMissing.selectedProperty().addListener((v, o, n) -> {
            if (n) {
                model.setPredicate(p -> (!(p instanceof TMACoreObject)) || !((TMACoreObject) p).isMissing());
            } else
                model.setPredicate(null);
        });
        cbHideMissing.setSelected(true);
    }
    paneFilter.setPadding(new Insets(2, 5, 2, 5));
    paneTable.setBottom(paneFilter);
    StringProperty columnFilter = tfColumnFilter.textProperty();
    columnFilter.addListener((v, o, n) -> {
        String val = n.toLowerCase().trim();
        if (val.isEmpty()) {
            for (TableColumn<?, ?> col : table.getColumns()) {
                if (!col.isVisible())
                    col.setVisible(true);
            }
            return;
        }
        for (TableColumn<?, ?> col : table.getColumns()) {
            col.setVisible(col.getText().toLowerCase().contains(val));
        }
    });
    BorderPane pane = new BorderPane();
    // pane.setCenter(table);
    splitPane.getItems().add(paneTable);
    pane.setCenter(splitPane);
    GridPane panelButtons = PaneTools.createColumnGridControls(buttons.toArray(new ButtonBase[0]));
    pane.setBottom(panelButtons);
    PathObjectHierarchyListener listener = new PathObjectHierarchyListener() {

        @Override
        public void hierarchyChanged(PathObjectHierarchyEvent event) {
            if (event.isChanging())
                return;
            if (!Platform.isFxApplicationThread()) {
                Platform.runLater(() -> hierarchyChanged(event));
                return;
            }
            if (imageData != null)
                displayedName.set(ServerTools.getDisplayableImageName(imageData.getServer()));
            model.refreshEntries();
            table.refresh();
            if (histogramDisplay != null)
                histogramDisplay.refreshHistogram();
        }
    };
    QuPathViewer viewer = qupath.getViewer();
    TableViewerListener tableViewerListener = new TableViewerListener(viewer, table);
    frame.setOnShowing(e -> {
        hierarchy.addPathObjectListener(listener);
        viewer.addViewerListener(tableViewerListener);
    });
    frame.setOnHiding(e -> {
        hierarchy.removePathObjectListener(listener);
        viewer.removeViewerListener(tableViewerListener);
    });
    Scene scene = new Scene(pane, 600, 500);
    frame.setScene(scene);
    frame.show();
    // Add ability to remove entries from table
    ContextMenu menu = new ContextMenu();
    Menu menuLimitClasses = new Menu("Show classes");
    menu.setOnShowing(e -> {
        Set<PathClass> representedClasses = model.getBackingListEntries().stream().map(p -> p.getPathClass() == null ? null : p.getPathClass().getBaseClass()).collect(Collectors.toCollection(() -> new HashSet<>()));
        representedClasses.remove(null);
        if (representedClasses.isEmpty()) {
            menuLimitClasses.setVisible(false);
        } else {
            menuLimitClasses.setVisible(true);
        }
        menuLimitClasses.getItems().clear();
        List<PathClass> sortedClasses = new ArrayList<>(representedClasses);
        Collections.sort(sortedClasses);
        MenuItem miClass = new MenuItem("All");
        miClass.setOnAction(e2 -> {
            model.setPredicate(null);
            histogramDisplay.refreshHistogram();
        });
        menuLimitClasses.getItems().add(miClass);
        for (PathClass pathClass : sortedClasses) {
            miClass = new MenuItem(pathClass.getName());
            miClass.setOnAction(e2 -> {
                model.setPredicate(p -> pathClass.isAncestorOf(p.getPathClass()));
                histogramDisplay.refreshHistogram();
            });
            menuLimitClasses.getItems().add(miClass);
        }
    });
    if (type != TMACoreObject.class) {
        menu.getItems().add(menuLimitClasses);
        table.setContextMenu(menu);
    }
}
Also used : Button(javafx.scene.control.Button) Pos(javafx.geometry.Pos) Arrays(java.util.Arrays) ImageServer(qupath.lib.images.servers.ImageServer) ServerTools(qupath.lib.images.servers.ServerTools) LoggerFactory(org.slf4j.LoggerFactory) HistogramDisplay(qupath.lib.gui.charts.HistogramDisplay) PathTableData(qupath.lib.gui.measure.PathTableData) MultipleSelectionModel(javafx.scene.control.MultipleSelectionModel) ObservableMeasurementTableData(qupath.lib.gui.measure.ObservableMeasurementTableData) ListChangeListener(javafx.collections.ListChangeListener) ContextMenu(javafx.scene.control.ContextMenu) PathObjectHierarchyEvent(qupath.lib.objects.hierarchy.events.PathObjectHierarchyEvent) Map(java.util.Map) TableView(javafx.scene.control.TableView) QuPathGUI(qupath.lib.gui.QuPathGUI) SortedList(javafx.collections.transformation.SortedList) Pane(javafx.scene.layout.Pane) Shape(java.awt.Shape) PrintWriter(java.io.PrintWriter) SplitPane(javafx.scene.control.SplitPane) TextField(javafx.scene.control.TextField) MenuItem(javafx.scene.control.MenuItem) BufferedImage(java.awt.image.BufferedImage) ButtonBase(javafx.scene.control.ButtonBase) Collection(java.util.Collection) Set(java.util.Set) Canvas(javafx.scene.canvas.Canvas) WorkflowStep(qupath.lib.plugins.workflow.WorkflowStep) QuPathViewerListener(qupath.lib.gui.viewer.QuPathViewerListener) Collectors(java.util.stream.Collectors) StandardCharsets(java.nio.charset.StandardCharsets) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) PathDetectionObject(qupath.lib.objects.PathDetectionObject) PathObject(qupath.lib.objects.PathObject) Platform(javafx.application.Platform) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer) Priority(javafx.scene.layout.Priority) List(java.util.List) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) ToggleButton(javafx.scene.control.ToggleButton) Clipboard(javafx.scene.input.Clipboard) GuiTools(qupath.lib.gui.tools.GuiTools) ClipboardContent(javafx.scene.input.ClipboardContent) BorderPane(javafx.scene.layout.BorderPane) StringProperty(javafx.beans.property.StringProperty) TableViewSelectionModel(javafx.scene.control.TableView.TableViewSelectionModel) Scene(javafx.scene.Scene) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) Bindings(javafx.beans.binding.Bindings) ArrayList(java.util.ArrayList) TableColumn(javafx.scene.control.TableColumn) HashSet(java.util.HashSet) LinkedHashMap(java.util.LinkedHashMap) Dialogs(qupath.lib.gui.dialogs.Dialogs) TableCell(javafx.scene.control.TableCell) Insets(javafx.geometry.Insets) GridPane(javafx.scene.layout.GridPane) ImageData(qupath.lib.images.ImageData) Logger(org.slf4j.Logger) Label(javafx.scene.control.Label) GeneralTools(qupath.lib.common.GeneralTools) RegionRequest(qupath.lib.regions.RegionRequest) TableRow(javafx.scene.control.TableRow) CheckBox(javafx.scene.control.CheckBox) PathClass(qupath.lib.objects.classes.PathClass) IOException(java.io.IOException) TMACoreObject(qupath.lib.objects.TMACoreObject) PathObjectSelectionModel(qupath.lib.objects.hierarchy.events.PathObjectSelectionModel) File(java.io.File) PathObjectTools(qupath.lib.objects.PathObjectTools) Menu(javafx.scene.control.Menu) ROI(qupath.lib.roi.interfaces.ROI) SelectionMode(javafx.scene.control.SelectionMode) Stage(javafx.stage.Stage) SimpleObjectProperty(javafx.beans.property.SimpleObjectProperty) SwingFXUtils(javafx.embed.swing.SwingFXUtils) PathObjectHierarchyListener(qupath.lib.objects.hierarchy.events.PathObjectHierarchyListener) Collections(java.util.Collections) Image(javafx.scene.image.Image) PathPrefs(qupath.lib.gui.prefs.PathPrefs) ContentDisplay(javafx.scene.control.ContentDisplay) PaneTools(qupath.lib.gui.tools.PaneTools) WorkflowStep(qupath.lib.plugins.workflow.WorkflowStep) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) SortedList(javafx.collections.transformation.SortedList) ArrayList(java.util.ArrayList) Label(javafx.scene.control.Label) PathObjectHierarchyEvent(qupath.lib.objects.hierarchy.events.PathObjectHierarchyEvent) SplitPane(javafx.scene.control.SplitPane) ListChangeListener(javafx.collections.ListChangeListener) PathAnnotationObject(qupath.lib.objects.PathAnnotationObject) Button(javafx.scene.control.Button) ToggleButton(javafx.scene.control.ToggleButton) Stage(javafx.stage.Stage) TextField(javafx.scene.control.TextField) TableView(javafx.scene.control.TableView) HashSet(java.util.HashSet) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer) GridPane(javafx.scene.layout.GridPane) TMACoreObject(qupath.lib.objects.TMACoreObject) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) Scene(javafx.scene.Scene) ROI(qupath.lib.roi.interfaces.ROI) TableColumn(javafx.scene.control.TableColumn) Pane(javafx.scene.layout.Pane) SplitPane(javafx.scene.control.SplitPane) BorderPane(javafx.scene.layout.BorderPane) GridPane(javafx.scene.layout.GridPane) HistogramDisplay(qupath.lib.gui.charts.HistogramDisplay) PathObject(qupath.lib.objects.PathObject) CheckBox(javafx.scene.control.CheckBox) TableRow(javafx.scene.control.TableRow) File(java.io.File) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) BorderPane(javafx.scene.layout.BorderPane) Insets(javafx.geometry.Insets) PathObjectHierarchyListener(qupath.lib.objects.hierarchy.events.PathObjectHierarchyListener) StringProperty(javafx.beans.property.StringProperty) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) ContextMenu(javafx.scene.control.ContextMenu) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) PathClass(qupath.lib.objects.classes.PathClass) ContextMenu(javafx.scene.control.ContextMenu) Menu(javafx.scene.control.Menu) ToggleButton(javafx.scene.control.ToggleButton) MenuItem(javafx.scene.control.MenuItem) ButtonBase(javafx.scene.control.ButtonBase) ObservableMeasurementTableData(qupath.lib.gui.measure.ObservableMeasurementTableData)

Example 84 with PathObject

use of qupath.lib.objects.PathObject in project qupath by qupath.

the class SummaryMeasurementTableCommand method synchronizeSelectionModelToTable.

private void synchronizeSelectionModelToTable(final PathObjectHierarchy hierarchy, final ListChangeListener.Change<? extends PathObject> change, final TableView<PathObject> table) {
    if (synchronizingTableToModel || hierarchy == null)
        return;
    PathObjectSelectionModel model = hierarchy.getSelectionModel();
    if (model == null) {
        return;
    }
    boolean wasSynchronizingToTree = synchronizingModelToTable;
    try {
        synchronizingModelToTable = true;
        // Check - was anything removed?
        boolean removed = false;
        if (change != null) {
            while (change.next()) removed = removed | change.wasRemoved();
        }
        MultipleSelectionModel<PathObject> treeModel = table.getSelectionModel();
        List<PathObject> selectedItems = treeModel.getSelectedItems();
        // If we just have no selected items, and something was removed, then clear the selection
        if (selectedItems.isEmpty() && removed) {
            model.clearSelection();
            return;
        }
        // if (selectedItems.size() == 1 && removed) {
        if (selectedItems.size() == 1) {
            model.setSelectedObject(selectedItems.get(0), false);
            return;
        }
        // If we have multiple selected items, we need to ensure that everything in the tree matches with everything in the selection model
        Set<PathObject> toSelect = new HashSet<>(treeModel.getSelectedItems());
        PathObject primary = treeModel.getSelectedItem();
        model.setSelectedObjects(toSelect, primary);
    } finally {
        synchronizingModelToTable = wasSynchronizingToTree;
    }
}
Also used : PathObject(qupath.lib.objects.PathObject) PathObjectSelectionModel(qupath.lib.objects.hierarchy.events.PathObjectSelectionModel) HashSet(java.util.HashSet)

Example 85 with PathObject

use of qupath.lib.objects.PathObject in project qupath by qupath.

the class PathHierarchyImageServer method readTile.

@Override
protected BufferedImage readTile(TileRequest tileRequest) throws IOException {
    RegionRequest request = tileRequest.getRegionRequest();
    // long startTime = System.currentTimeMillis();
    // Get connections
    Object o = options.getShowConnections() ? imageData.getProperty(DefaultPathObjectConnectionGroup.KEY_OBJECT_CONNECTIONS) : null;
    PathObjectConnections connections = (o instanceof PathObjectConnections) ? (PathObjectConnections) o : null;
    List<PathObject> pathObjects = new ArrayList<>(getObjectsToPaint(request));
    if (pathObjects == null || pathObjects.isEmpty()) {
        // We can only return null if no connections - otherwise we might still need to draw something
        if (connections == null) {
            return null;
        }
    }
    // Because levels *can* change, we need to extract them first to avoid breaking the contract for comparable
    // in a multithreaded environment
    var levels = pathObjects.stream().collect(Collectors.toMap(p -> p, p -> p.getLevel()));
    var comparator = DefaultPathObjectComparator.getInstance().thenComparingInt(p -> levels.get(p));
    Collections.sort(pathObjects, comparator);
    // Collections.sort(pathObjects, new HierarchyOverlay.DetectionComparator());
    double downsampleFactor = request.getDownsample();
    int width = tileRequest.getTileWidth();
    int height = tileRequest.getTileHeight();
    BufferedImage img = createDefaultRGBImage(width, height);
    Graphics2D g2d = img.createGraphics();
    g2d.setClip(0, 0, width, height);
    // g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    double scale = 1.0 / downsampleFactor;
    // g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.scale(scale, scale);
    g2d.translate(-request.getX(), -request.getY());
    // Note we don't want to pass a selection model, as selections shouldn't be included
    if (pathObjects != null && !pathObjects.isEmpty())
        PathHierarchyPaintingHelper.paintSpecifiedObjects(g2d, AwtTools.getBounds(request), pathObjects, options, null, downsampleFactor);
    // See if we have any connections to draw
    if (connections != null) {
        PathHierarchyPaintingHelper.paintConnections(connections, hierarchy, g2d, imageData.isFluorescence() ? ColorToolsAwt.TRANSLUCENT_WHITE : ColorToolsAwt.TRANSLUCENT_BLACK, downsampleFactor);
    }
    g2d.dispose();
    // System.out.println("Single tile image creation time: " + (endTime - startTime)/1000.);
    return img;
}
Also used : ImageServer(qupath.lib.images.servers.ImageServer) DefaultPathObjectConnectionGroup(qupath.lib.objects.DefaultPathObjectConnectionGroup) ImageChannel(qupath.lib.images.servers.ImageChannel) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) ArrayList(java.util.ArrayList) PathObjectConnections(qupath.lib.objects.PathObjectConnections) Graphics2D(java.awt.Graphics2D) GeneratingImageServer(qupath.lib.images.servers.GeneratingImageServer) ImageResolutionLevel(qupath.lib.images.servers.ImageServerMetadata.ImageResolutionLevel) TileRequest(qupath.lib.images.servers.TileRequest) PathHierarchyPaintingHelper(qupath.lib.gui.viewer.PathHierarchyPaintingHelper) URI(java.net.URI) ColorToolsAwt(qupath.lib.color.ColorToolsAwt) ImageData(qupath.lib.images.ImageData) BufferedImage(java.awt.image.BufferedImage) RegionRequest(qupath.lib.regions.RegionRequest) PixelType(qupath.lib.images.servers.PixelType) Collection(java.util.Collection) AwtTools(qupath.lib.awt.common.AwtTools) IOException(java.io.IOException) DefaultPathObjectComparator(qupath.lib.objects.DefaultPathObjectComparator) UUID(java.util.UUID) ServerBuilder(qupath.lib.images.servers.ImageServerBuilder.ServerBuilder) Collectors(java.util.stream.Collectors) OverlayOptions(qupath.lib.gui.viewer.OverlayOptions) AbstractTileableImageServer(qupath.lib.images.servers.AbstractTileableImageServer) PathDetectionObject(qupath.lib.objects.PathDetectionObject) PathObject(qupath.lib.objects.PathObject) List(java.util.List) Collections(java.util.Collections) ImageServerMetadata(qupath.lib.images.servers.ImageServerMetadata) PathObjectConnections(qupath.lib.objects.PathObjectConnections) PathObject(qupath.lib.objects.PathObject) ArrayList(java.util.ArrayList) PathDetectionObject(qupath.lib.objects.PathDetectionObject) PathObject(qupath.lib.objects.PathObject) RegionRequest(qupath.lib.regions.RegionRequest) BufferedImage(java.awt.image.BufferedImage) Graphics2D(java.awt.Graphics2D)

Aggregations

PathObject (qupath.lib.objects.PathObject)182 ArrayList (java.util.ArrayList)84 ROI (qupath.lib.roi.interfaces.ROI)74 PathObjectHierarchy (qupath.lib.objects.hierarchy.PathObjectHierarchy)61 List (java.util.List)48 BufferedImage (java.awt.image.BufferedImage)37 IOException (java.io.IOException)37 PathClass (qupath.lib.objects.classes.PathClass)37 Collectors (java.util.stream.Collectors)35 PathAnnotationObject (qupath.lib.objects.PathAnnotationObject)34 Map (java.util.Map)33 Logger (org.slf4j.Logger)33 LoggerFactory (org.slf4j.LoggerFactory)33 ImageData (qupath.lib.images.ImageData)31 TMACoreObject (qupath.lib.objects.TMACoreObject)31 Collection (java.util.Collection)29 Collections (java.util.Collections)29 HashMap (java.util.HashMap)28 PathObjectTools (qupath.lib.objects.PathObjectTools)26 Arrays (java.util.Arrays)25