Search in sources :

Example 6 with MeasurementList

use of qupath.lib.measurements.MeasurementList in project qupath by qupath.

the class PathObjectTestWrapper method test_getMeasurementList.

// @Test
public void test_getMeasurementList(PathObject myPO, MeasurementList ML) {
    MeasurementList myPOML = myPO.getMeasurementList();
    assertEquals(myPOML, ML);
}
Also used : MeasurementList(qupath.lib.measurements.MeasurementList)

Example 7 with MeasurementList

use of qupath.lib.measurements.MeasurementList in project qupath by qupath.

the class ObjectMeasurements method addCellShapeMeasurements.

private static void addCellShapeMeasurements(PathCellObject cell, PixelCalibration cal, Collection<ShapeFeatures> features) {
    var roiNucleus = cell.getNucleusROI();
    var roiCell = cell.getROI();
    try (MeasurementList ml = cell.getMeasurementList()) {
        if (roiNucleus != null) {
            addShapeMeasurements(ml, roiNucleus, cal, "Nucleus: ", features);
        }
        if (roiCell != null) {
            addShapeMeasurements(ml, roiCell, cal, "Cell: ", features);
        }
        if (roiNucleus != null && roiCell != null && features.contains(ShapeFeatures.NUCLEUS_CELL_RATIO)) {
            double pixelWidth = cal.getPixelWidth().doubleValue();
            double pixelHeight = cal.getPixelHeight().doubleValue();
            double nucleusCellAreaRatio = GeneralTools.clipValue(roiNucleus.getScaledArea(pixelWidth, pixelHeight) / roiCell.getScaledArea(pixelWidth, pixelHeight), 0, 1);
            ml.putMeasurement("Nucleus/Cell area ratio", nucleusCellAreaRatio);
        }
    }
}
Also used : MeasurementList(qupath.lib.measurements.MeasurementList)

Example 8 with MeasurementList

use of qupath.lib.measurements.MeasurementList in project qupath by qupath.

the class SmoothFeaturesPlugin method smoothMeasurements.

/**
 * Using the centroids of the ROIs within PathObjects, 'smooth' measurements by summing up the corresponding measurements of
 * nearby objects, weighted by centroid distance.
 *
 * @param pathObjects
 * @param measurements
 * @param fwhmPixels
 * @param fwhmString
 * @param withinClass
 * @param useLegacyNames
 */
// public static Set<String> smoothMeasurements(List<PathObject> pathObjects, List<String> measurements, double fwhmPixels) {
public static void smoothMeasurements(List<PathObject> pathObjects, List<String> measurements, double fwhmPixels, String fwhmString, boolean withinClass, boolean useLegacyNames) {
    if (measurements.isEmpty() || pathObjects.size() <= 1)
        // Collections.emptySet();
        return;
    if (fwhmString == null)
        fwhmString = String.format("%.2f px", fwhmPixels);
    double fwhmPixels2 = fwhmPixels * fwhmPixels;
    double sigmaPixels = fwhmPixels / Math.sqrt(8 * Math.log(2));
    double sigma2 = 2 * sigmaPixels * sigmaPixels;
    double maxDist = sigmaPixels * 3;
    // Maximum separation
    double maxDistSq = maxDist * maxDist;
    int nObjects = pathObjects.size();
    // int counter = 0;
    // Sort by x-coordinate - this gives us a method of breaking early
    Collections.sort(pathObjects, new Comparator<PathObject>() {

        @Override
        public int compare(PathObject o1, PathObject o2) {
            double x1 = o1.getROI().getCentroidX();
            double x2 = o2.getROI().getCentroidX();
            // System.out.println(String.format("(%.2f, %.2f) vs (%.2f, %.2f)", o1.getROI().getCentroidX(), o1.getROI().getCentroidY(), o2.getROI().getCentroidX(), o2.getROI().getCentroidY()));				}
            return Double.compare(x1, x2);
        // if (x1 > x2)
        // return 1;
        // if (x2 < x1)
        // return -1;
        // System.out.println(x1 + " vs. " + x2);
        // System.out.println(String.format("(%.2f, %.2f) vs (%.2f, %.2f)", o1.getROI().getCentroidX(), o1.getROI().getCentroidY(), o2.getROI().getCentroidX(), o2.getROI().getCentroidY()));
        // return 0;
        // return (int)Math.signum(o1.getROI().getCentroidX() - o2.getROI().getCentroidX());
        }
    });
    // Create a LUT for distances - calculating exp every time is expensive
    double[] distanceWeights = new double[(int) (maxDist + .5) + 1];
    for (int i = 0; i < distanceWeights.length; i++) {
        distanceWeights[i] = Math.exp(-(i * i) / sigma2);
    }
    System.currentTimeMillis();
    float[] xCentroids = new float[nObjects];
    float[] yCentroids = new float[nObjects];
    PathClass[] pathClasses = new PathClass[nObjects];
    int[] nearbyDetectionCounts = new int[nObjects];
    float[][] measurementsWeighted = new float[nObjects][measurements.size()];
    float[][] measurementDenominators = new float[nObjects][measurements.size()];
    float[][] measurementValues = new float[nObjects][measurements.size()];
    for (int i = 0; i < nObjects; i++) {
        PathObject pathObject = pathObjects.get(i);
        if (withinClass)
            pathClasses[i] = pathObject.getPathClass() == null ? null : pathObject.getPathClass().getBaseClass();
        ROI roi = pathObject.getROI();
        xCentroids[i] = (float) roi.getCentroidX();
        yCentroids[i] = (float) roi.getCentroidY();
        MeasurementList measurementList = pathObject.getMeasurementList();
        int ind = 0;
        for (String name : measurements) {
            float value = (float) measurementList.getMeasurementValue(name);
            // Used to cache values
            measurementValues[i][ind] = value;
            // Based on distances and measurements
            measurementsWeighted[i][ind] = value;
            // Based on distances along
            measurementDenominators[i][ind] = 1;
            ind++;
        }
    }
    String prefix, postfix, denomName, countsName;
    // Use previous syntax for naming smoothed measurements
    if (useLegacyNames) {
        prefix = "";
        postfix = String.format(" - Smoothed (FWHM %s)", fwhmString);
        denomName = String.format("Smoothed denominator (local density, FWHM %s)", fwhmString);
        countsName = String.format("Nearby detection counts (radius %s)", fwhmString);
    } else {
        prefix = String.format("Smoothed: %s: ", fwhmString);
        postfix = "";
        // prefix + "Weighted density";
        denomName = null;
        countsName = prefix + "Nearby detection counts";
    // denomName = prefix + "Denominator (local density)";
    // countsName = prefix + "Nearby detection counts";
    }
    // Loop through objects, computing predominant class based on distance weighting
    for (int i = 0; i < nObjects; i++) {
        // Extract the current class index
        PathObject pathObject = pathObjects.get(i);
        PathClass pathClass = pathClasses[i];
        MeasurementList measurementList = pathObject.getMeasurementList();
        float[] mValues = measurementValues[i];
        float[] mWeighted = measurementsWeighted[i];
        float[] mDenominator = measurementDenominators[i];
        // Compute centroid distances
        double xi = xCentroids[i];
        double yi = yCentroids[i];
        for (int j = i + 1; j < nObjects; j++) {
            double xj = xCentroids[j];
            double yj = yCentroids[j];
            // Break early if we are already too far away
            if (Math.abs(xj - xi) > maxDist) {
                break;
            }
            double distSq = (xj - xi) * (xj - xi) + (yj - yi) * (yj - yi);
            // // Check if we are close enough to have an influence
            if (distSq > maxDistSq || Double.isNaN(distSq))
                continue;
            // Check if the class is ok, if check needed
            if (withinClass && pathClass != pathClasses[j])
                continue;
            // Update the counts, if close enough
            if (distSq < fwhmPixels2) {
                nearbyDetectionCounts[i]++;
                nearbyDetectionCounts[j]++;
            }
            // Update the class weights for both objects currently being tested
            // Compute weight based on centroid distances
            // double weight = Math.exp(-distSq/sigma2);
            // * pathObjects.get(j).getClassProbability();
            double weight = distanceWeights[(int) (Math.sqrt(distSq) + .5)];
            float[] temp = measurementValues[j];
            float[] tempWeighted = measurementsWeighted[j];
            float[] tempDenominator = measurementDenominators[j];
            for (int ind = 0; ind < measurements.size(); ind++) {
                float tempVal = temp[ind];
                if (Float.isNaN(tempVal))
                    continue;
                mWeighted[ind] += tempVal * weight;
                mDenominator[ind] += weight;
                float tempVal2 = mValues[ind];
                if (Float.isNaN(tempVal2))
                    continue;
                tempWeighted[ind] += tempVal2 * weight;
                tempDenominator[ind] += weight;
            }
        }
        // Store the measurements
        int ind = 0;
        float maxDenominator = Float.NEGATIVE_INFINITY;
        for (String name : measurements) {
            // if (name.contains(" - Smoothed (FWHM ") || name.startsWith("Smoothed denominator (local density, ") || name.startsWith("Nearby detection counts"))
            // continue;
            float denominator = mDenominator[ind];
            if (denominator > maxDenominator)
                maxDenominator = denominator;
            String nameToAdd = prefix + name + postfix;
            measurementList.putMeasurement(nameToAdd, mWeighted[ind] / denominator);
            // measurementsAdded.add(nameToAdd);
            // measurementList.putMeasurement(name + " - weighted sum", mWeighted[ind]); // TODO: Support optionally providing weighted sums
            // measurementList.addMeasurement(name + " - smoothed", mWeighted[ind] / mDenominator[ind]);
            ind++;
        }
        if (pathObject instanceof PathDetectionObject && denomName != null) {
            measurementList.putMeasurement(denomName, maxDenominator);
        // measurementsAdded.add(denomName);
        }
        if (pathObject instanceof PathDetectionObject && countsName != null) {
            measurementList.putMeasurement(countsName, nearbyDetectionCounts[i]);
        // measurementsAdded.add(countsName);
        }
        measurementList.close();
    }
    System.currentTimeMillis();
// return measurementsAdded;
}
Also used : PathDetectionObject(qupath.lib.objects.PathDetectionObject) MeasurementList(qupath.lib.measurements.MeasurementList) ROI(qupath.lib.roi.interfaces.ROI) PathClass(qupath.lib.objects.classes.PathClass) PathObject(qupath.lib.objects.PathObject)

Example 9 with MeasurementList

use of qupath.lib.measurements.MeasurementList 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 10 with MeasurementList

use of qupath.lib.measurements.MeasurementList in project qupath by qupath.

the class FeatureSelectionPane method makeFeatureSelectionPanel.

BorderPane makeFeatureSelectionPanel(final QuPathGUI qupath) {
    tableFeatures.setTooltip(new Tooltip("Select object features to be used by the classifier"));
    TableColumn<SelectableFeature, String> columnName = new TableColumn<>("Feature name");
    columnName.setCellValueFactory(new PropertyValueFactory<>("featureName"));
    columnName.setEditable(false);
    TableColumn<SelectableFeature, Boolean> columnSelected = new TableColumn<>("Selected");
    columnSelected.setCellValueFactory(new PropertyValueFactory<>("selected"));
    columnSelected.setCellFactory(column -> new CheckBoxTableCell<>());
    columnSelected.setEditable(true);
    columnSelected.setResizable(false);
    columnName.prefWidthProperty().bind(tableFeatures.widthProperty().subtract(columnSelected.widthProperty()));
    tableFeatures.getColumns().add(columnName);
    tableFeatures.getColumns().add(columnSelected);
    tableFeatures.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
    tableFeatures.setEditable(true);
    ContextMenu menu = new ContextMenu();
    MenuItem itemSelect = new MenuItem("Select");
    itemSelect.setOnAction(e -> {
        for (SelectableFeature feature : tableFeatures.getSelectionModel().getSelectedItems()) feature.setSelected(true);
    });
    menu.getItems().add(itemSelect);
    MenuItem itemDeselect = new MenuItem("Deselect");
    itemDeselect.setOnAction(e -> {
        for (SelectableFeature feature : tableFeatures.getSelectionModel().getSelectedItems()) feature.setSelected(false);
    });
    menu.getItems().add(itemDeselect);
    menu.getItems().add(new SeparatorMenuItem());
    MenuItem itemDelete = new MenuItem("Delete highlighted features");
    itemDelete.setOnAction(e -> {
        List<String> highlightedFeatures = new ArrayList<>();
        for (SelectableFeature feature : tableFeatures.getSelectionModel().getSelectedItems()) highlightedFeatures.add(feature.getFeatureName());
        int nFeatures = highlightedFeatures.size();
        ImageData<?> imageData = qupath.getImageData();
        if (nFeatures == 0 || imageData == null || imageData.getHierarchy().isEmpty())
            return;
        String f = nFeatures == 1 ? "1 feature" : nFeatures + " features";
        if (!Dialogs.showYesNoDialog("Delete feature measurements", "Are you sure you want to permanently delete " + f + " from all objects?"))
            return;
        // Determine the features to remove
        // Loop through objects and delete features
        List<PathObject> changedObjects = new ArrayList<>();
        for (PathObject pathObject : imageData.getHierarchy().getFlattenedObjectList(null)) {
            // TODO: Consider if non-detection objects should be supported
            if (!pathObject.isDetection())
                continue;
            // Remove measurements & log as changed, if necessary
            MeasurementList ml = pathObject.getMeasurementList();
            int sizeBefore = ml.size();
            ml.removeMeasurements(highlightedFeatures.toArray(new String[0]));
            ml.close();
            int sizeAfter = ml.size();
            if (sizeAfter != sizeBefore)
                changedObjects.add(pathObject);
        }
        imageData.getHierarchy().fireObjectMeasurementsChangedEvent(this, changedObjects);
        tableFeatures.getSelectionModel().clearSelection();
        if (!hasFeatures())
            ensureMeasurementsUpdated();
    // classifierData.setFeaturesSelected(features, false);
    // tableFeatures.repaint();
    });
    menu.getItems().add(itemDelete);
    tableFeatures.setContextMenu(menu);
    // Button to update the features
    BorderPane panelButtons = new BorderPane();
    // Button btnUpdateFeatures = new Button("Update feature table");
    // btnUpdateFeatures.setTooltip(new Tooltip("Check all objects & available features"));
    // btnUpdateFeatures.setOnAction(e -> {
    // ensureMeasurementsUpdated();
    // if (panelIntensities != null)
    // panelIntensities.setAvailableMeasurements(getAvailableFeatures(), "mean", "dab");
    // });
    Button btnSelectAll = new Button("Select all");
    btnSelectAll.setOnAction(e -> {
        if (!hasFeatures())
            ensureMeasurementsUpdated();
        for (SelectableFeature feature : tableFeatures.getItems()) feature.setSelected(true);
    });
    Button btnSelectNone = new Button("Select none");
    btnSelectNone.setOnAction(e -> {
        if (!hasFeatures())
            ensureMeasurementsUpdated();
        for (SelectableFeature feature : tableFeatures.getItems()) feature.setSelected(false);
    });
    GridPane panelSelectButtons = PaneTools.createColumnGridControls(btnSelectAll, btnSelectNone);
    panelButtons.setTop(panelSelectButtons);
    // panelButtons.setBottom(btnUpdateFeatures);
    // btnUpdateFeatures.prefWidthProperty().bind(panelButtons.widthProperty());
    BorderPane panelFeatures = new BorderPane();
    panelFeatures.setCenter(tableFeatures);
    panelFeatures.setBottom(panelButtons);
    ensureMeasurementsUpdated();
    return panelFeatures;
}
Also used : BorderPane(javafx.scene.layout.BorderPane) GridPane(javafx.scene.layout.GridPane) Tooltip(javafx.scene.control.Tooltip) MeasurementList(qupath.lib.measurements.MeasurementList) ArrayList(java.util.ArrayList) ContextMenu(javafx.scene.control.ContextMenu) MenuItem(javafx.scene.control.MenuItem) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem) SeparatorMenuItem(javafx.scene.control.SeparatorMenuItem) TableColumn(javafx.scene.control.TableColumn) PathObject(qupath.lib.objects.PathObject) Button(javafx.scene.control.Button)

Aggregations

MeasurementList (qupath.lib.measurements.MeasurementList)24 PathObject (qupath.lib.objects.PathObject)12 ArrayList (java.util.ArrayList)9 ROI (qupath.lib.roi.interfaces.ROI)9 BufferedImage (java.awt.image.BufferedImage)6 RegionRequest (qupath.lib.regions.RegionRequest)6 List (java.util.List)5 PixelCalibration (qupath.lib.images.servers.PixelCalibration)5 IOException (java.io.IOException)4 HashMap (java.util.HashMap)4 TMACoreObject (qupath.lib.objects.TMACoreObject)4 PathClass (qupath.lib.objects.classes.PathClass)4 LinkedHashSet (java.util.LinkedHashSet)3 Map (java.util.Map)3 SimpleModifiableImage (qupath.lib.analysis.images.SimpleModifiableImage)3 ImmutableDimension (qupath.lib.geom.ImmutableDimension)3 PathCellObject (qupath.lib.objects.PathCellObject)3 ParameterList (qupath.lib.plugins.parameters.ParameterList)3 Calibration (ij.measure.Calibration)2 ImageProcessor (ij.process.ImageProcessor)2