Search in sources :

Example 1 with Plot2

use of ij.gui.Plot2 in project GDSC-SMLM by aherbert.

the class PSFCreator method run.

/*
	 * (non-Javadoc)
	 * 
	 * @see ij.plugin.filter.PlugInFilter#run(ij.process.ImageProcessor)
	 */
public void run(ImageProcessor ip) {
    loadConfiguration();
    BasePoint[] spots = getSpots();
    if (spots.length == 0) {
        IJ.error(TITLE, "No spots without neighbours within " + (boxRadius * 2) + "px");
        return;
    }
    ImageStack stack = getImageStack();
    final int width = imp.getWidth();
    final int height = imp.getHeight();
    final int currentSlice = imp.getSlice();
    // Adjust settings for a single maxima
    config.setIncludeNeighbours(false);
    fitConfig.setDuplicateDistance(0);
    ArrayList<double[]> centres = new ArrayList<double[]>(spots.length);
    int iterations = 1;
    LoessInterpolator loess = new LoessInterpolator(smoothing, iterations);
    // TODO - The fitting routine may not produce many points. In this instance the LOESS interpolator
    // fails to smooth the data very well. A higher bandwidth helps this but perhaps 
    // try a different smoothing method.
    // For each spot
    Utils.log(TITLE + ": " + imp.getTitle());
    Utils.log("Finding spot locations...");
    Utils.log("  %d spot%s without neighbours within %dpx", spots.length, ((spots.length == 1) ? "" : "s"), (boxRadius * 2));
    StoredDataStatistics averageSd = new StoredDataStatistics();
    StoredDataStatistics averageA = new StoredDataStatistics();
    Statistics averageRange = new Statistics();
    MemoryPeakResults allResults = new MemoryPeakResults();
    allResults.setName(TITLE);
    allResults.setBounds(new Rectangle(0, 0, width, height));
    MemoryPeakResults.addResults(allResults);
    for (int n = 1; n <= spots.length; n++) {
        BasePoint spot = spots[n - 1];
        final int x = (int) spot.getX();
        final int y = (int) spot.getY();
        MemoryPeakResults results = fitSpot(stack, width, height, x, y);
        allResults.addAllf(results.getResults());
        if (results.size() < 5) {
            Utils.log("  Spot %d: Not enough fit results %d", n, results.size());
            continue;
        }
        // Get the results for the spot centre and width
        double[] z = new double[results.size()];
        double[] xCoord = new double[z.length];
        double[] yCoord = new double[z.length];
        double[] sd = new double[z.length];
        double[] a = new double[z.length];
        int i = 0;
        for (PeakResult peak : results.getResults()) {
            z[i] = peak.getFrame();
            xCoord[i] = peak.getXPosition() - x;
            yCoord[i] = peak.getYPosition() - y;
            sd[i] = FastMath.max(peak.getXSD(), peak.getYSD());
            a[i] = peak.getAmplitude();
            i++;
        }
        // Smooth the amplitude plot
        double[] smoothA = loess.smooth(z, a);
        // Find the maximum amplitude
        int maximumIndex = findMaximumIndex(smoothA);
        // Find the range at a fraction of the max. This is smoothed to find the X/Y centre
        int start = 0, stop = smoothA.length - 1;
        double limit = smoothA[maximumIndex] * amplitudeFraction;
        for (int j = 0; j < smoothA.length; j++) {
            if (smoothA[j] > limit) {
                start = j;
                break;
            }
        }
        for (int j = smoothA.length; j-- > 0; ) {
            if (smoothA[j] > limit) {
                stop = j;
                break;
            }
        }
        averageRange.add(stop - start + 1);
        // Extract xy centre coords and smooth
        double[] smoothX = new double[stop - start + 1];
        double[] smoothY = new double[smoothX.length];
        double[] smoothSd = new double[smoothX.length];
        double[] newZ = new double[smoothX.length];
        for (int j = start, k = 0; j <= stop; j++, k++) {
            smoothX[k] = xCoord[j];
            smoothY[k] = yCoord[j];
            smoothSd[k] = sd[j];
            newZ[k] = z[j];
        }
        smoothX = loess.smooth(newZ, smoothX);
        smoothY = loess.smooth(newZ, smoothY);
        smoothSd = loess.smooth(newZ, smoothSd);
        // Since the amplitude is not very consistent move from this peak to the 
        // lowest width which is the in-focus spot.
        maximumIndex = findMinimumIndex(smoothSd, maximumIndex - start);
        // Find the centre at the amplitude peak
        double cx = smoothX[maximumIndex] + x;
        double cy = smoothY[maximumIndex] + y;
        int cz = (int) newZ[maximumIndex];
        double csd = smoothSd[maximumIndex];
        double ca = smoothA[maximumIndex + start];
        // The average should weight the SD using the signal for each spot
        averageSd.add(smoothSd[maximumIndex]);
        averageA.add(ca);
        if (ignoreSpot(n, z, a, smoothA, xCoord, yCoord, sd, newZ, smoothX, smoothY, smoothSd, cx, cy, cz, csd)) {
            Utils.log("  Spot %d was ignored", n);
            continue;
        }
        // Store result - it may have been moved interactively
        maximumIndex += this.slice - cz;
        cz = (int) newZ[maximumIndex];
        csd = smoothSd[maximumIndex];
        ca = smoothA[maximumIndex + start];
        Utils.log("  Spot %d => x=%.2f, y=%.2f, z=%d, sd=%.2f, A=%.2f\n", n, cx, cy, cz, csd, ca);
        centres.add(new double[] { cx, cy, cz, csd, n });
    }
    if (interactiveMode) {
        imp.setSlice(currentSlice);
        imp.setOverlay(null);
        // Hide the amplitude and spot plots
        Utils.hide(TITLE_AMPLITUDE);
        Utils.hide(TITLE_PSF_PARAMETERS);
    }
    if (centres.isEmpty()) {
        String msg = "No suitable spots could be identified centres";
        Utils.log(msg);
        IJ.error(TITLE, msg);
        return;
    }
    // Find the limits of the z-centre
    int minz = (int) centres.get(0)[2];
    int maxz = minz;
    for (double[] centre : centres) {
        if (minz > centre[2])
            minz = (int) centre[2];
        else if (maxz < centre[2])
            maxz = (int) centre[2];
    }
    IJ.showStatus("Creating PSF image");
    // Create a stack that can hold all the data.
    ImageStack psf = createStack(stack, minz, maxz, magnification);
    // For each spot
    Statistics stats = new Statistics();
    boolean ok = true;
    for (int i = 0; ok && i < centres.size(); i++) {
        double progress = (double) i / centres.size();
        final double increment = 1.0 / (stack.getSize() * centres.size());
        IJ.showProgress(progress);
        double[] centre = centres.get(i);
        // Extract the spot
        float[][] spot = new float[stack.getSize()][];
        Rectangle regionBounds = null;
        for (int slice = 1; slice <= stack.getSize(); slice++) {
            ImageExtractor ie = new ImageExtractor((float[]) stack.getPixels(slice), width, height);
            if (regionBounds == null)
                regionBounds = ie.getBoxRegionBounds((int) centre[0], (int) centre[1], boxRadius);
            spot[slice - 1] = ie.crop(regionBounds);
        }
        int n = (int) centre[4];
        final float b = getBackground(n, spot);
        if (!subtractBackgroundAndWindow(spot, b, regionBounds.width, regionBounds.height, centre, loess)) {
            Utils.log("  Spot %d was ignored", n);
            continue;
        }
        stats.add(b);
        // Adjust the centre using the crop
        centre[0] -= regionBounds.x;
        centre[1] -= regionBounds.y;
        // This takes a long time so this should track progress
        ok = addToPSF(maxz, magnification, psf, centre, spot, regionBounds, progress, increment, centreEachSlice);
    }
    if (interactiveMode) {
        Utils.hide(TITLE_INTENSITY);
    }
    IJ.showProgress(1);
    if (threadPool != null) {
        threadPool.shutdownNow();
        threadPool = null;
    }
    if (!ok || stats.getN() == 0)
        return;
    final double avSd = getAverage(averageSd, averageA, 2);
    Utils.log("  Average background = %.2f, Av. SD = %s px", stats.getMean(), Utils.rounded(avSd, 4));
    normalise(psf, maxz, avSd * magnification, false);
    IJ.showProgress(1);
    psfImp = Utils.display("PSF", psf);
    psfImp.setSlice(maxz);
    psfImp.resetDisplayRange();
    psfImp.updateAndDraw();
    double[][] fitCom = new double[2][psf.getSize()];
    Arrays.fill(fitCom[0], Double.NaN);
    Arrays.fill(fitCom[1], Double.NaN);
    double fittedSd = fitPSF(psf, loess, maxz, averageRange.getMean(), fitCom);
    // Compute the drift in the PSF:
    // - Use fitted centre if available; otherwise find CoM for each frame
    // - express relative to the average centre
    double[][] com = calculateCentreOfMass(psf, fitCom, nmPerPixel / magnification);
    double[] slice = Utils.newArray(psf.getSize(), 1, 1.0);
    String title = TITLE + " CoM Drift";
    Plot2 plot = new Plot2(title, "Slice", "Drift (nm)");
    plot.addLabel(0, 0, "Red = X; Blue = Y");
    //double[] limitsX = Maths.limits(com[0]);
    //double[] limitsY = Maths.limits(com[1]);
    double[] limitsX = getLimits(com[0]);
    double[] limitsY = getLimits(com[1]);
    plot.setLimits(1, psf.getSize(), Math.min(limitsX[0], limitsY[0]), Math.max(limitsX[1], limitsY[1]));
    plot.setColor(Color.red);
    plot.addPoints(slice, com[0], Plot.DOT);
    plot.addPoints(slice, loess.smooth(slice, com[0]), Plot.LINE);
    plot.setColor(Color.blue);
    plot.addPoints(slice, com[1], Plot.DOT);
    plot.addPoints(slice, loess.smooth(slice, com[1]), Plot.LINE);
    Utils.display(title, plot);
    // TODO - Redraw the PSF with drift correction applied. 
    // This means that the final image should have no drift.
    // This is relevant when combining PSF images. It doesn't matter too much for simulations 
    // unless the drift is large.
    // Add Image properties containing the PSF details
    final double fwhm = getFWHM(psf, maxz);
    psfImp.setProperty("Info", XmlUtils.toXML(new PSFSettings(maxz, nmPerPixel / magnification, nmPerSlice, stats.getN(), fwhm, createNote())));
    Utils.log("%s : z-centre = %d, nm/Pixel = %s, nm/Slice = %s, %d images, PSF SD = %s nm, FWHM = %s px\n", psfImp.getTitle(), maxz, Utils.rounded(nmPerPixel / magnification, 3), Utils.rounded(nmPerSlice, 3), stats.getN(), Utils.rounded(fittedSd * nmPerPixel, 4), Utils.rounded(fwhm));
    createInteractivePlots(psf, maxz, nmPerPixel / magnification, fittedSd * nmPerPixel);
    IJ.showStatus("");
}
Also used : ImageStack(ij.ImageStack) BasePoint(gdsc.core.match.BasePoint) ArrayList(java.util.ArrayList) StoredDataStatistics(gdsc.core.utils.StoredDataStatistics) Rectangle(java.awt.Rectangle) Plot2(ij.gui.Plot2) Statistics(gdsc.core.utils.Statistics) StoredDataStatistics(gdsc.core.utils.StoredDataStatistics) DescriptiveStatistics(org.apache.commons.math3.stat.descriptive.DescriptiveStatistics) Point(java.awt.Point) BasePoint(gdsc.core.match.BasePoint) PeakResult(gdsc.smlm.results.PeakResult) LoessInterpolator(org.apache.commons.math3.analysis.interpolation.LoessInterpolator) MemoryPeakResults(gdsc.smlm.results.MemoryPeakResults) ImageExtractor(gdsc.core.utils.ImageExtractor) PSFSettings(gdsc.smlm.ij.settings.PSFSettings)

Example 2 with Plot2

use of ij.gui.Plot2 in project GDSC-SMLM by aherbert.

the class PSFDrift method displayPlot.

private double[][] displayPlot(String title, String yLabel, double[] x, double[] y, double[] se, LoessInterpolator loess, int start, int end) {
    // Extract non NaN numbers
    double[] newX = new double[x.length];
    double[] newY = new double[x.length];
    int c = 0;
    for (int i = 0; i < x.length; i++) if (!Double.isNaN(y[i])) {
        newX[c] = x[i];
        newY[c] = y[i];
        c++;
    }
    newX = Arrays.copyOf(newX, c);
    newY = Arrays.copyOf(newY, c);
    title = TITLE + " " + title;
    Plot2 plot = new Plot2(title, "z (nm)", yLabel);
    double[] limitsx = Maths.limits(x);
    double[] limitsy = new double[2];
    if (se != null) {
        if (c > 0) {
            limitsy = new double[] { newY[0] - se[0], newY[0] + se[0] };
            for (int i = 1; i < newY.length; i++) {
                limitsy[0] = Maths.min(limitsy[0], newY[i] - se[i]);
                limitsy[1] = Maths.max(limitsy[1], newY[i] + se[i]);
            }
        }
    } else {
        if (c > 0)
            limitsy = Maths.limits(newY);
    }
    double rangex = Math.max(0.05 * (limitsx[1] - limitsx[0]), 0.1);
    double rangey = Math.max(0.05 * (limitsy[1] - limitsy[0]), 0.1);
    plot.setLimits(limitsx[0] - rangex, limitsx[1] + rangex, limitsy[0] - rangey, limitsy[1] + rangey);
    if (loess == null) {
        addPoints(plot, Plot.LINE, newX, newY, x[start], x[end]);
    } else {
        addPoints(plot, Plot.DOT, newX, newY, x[start], x[end]);
        newY = loess.smooth(newX, newY);
        addPoints(plot, Plot.LINE, newX, newY, x[start], x[end]);
    }
    if (se != null) {
        plot.setColor(Color.magenta);
        for (int i = 0; i < x.length; i++) {
            if (!Double.isNaN(y[i]))
                plot.drawLine(x[i], y[i] - se[i], x[i], y[i] + se[i]);
        }
        // Draw the start and end lines for the valid range
        plot.setColor(Color.green);
        plot.drawLine(x[start], limitsy[0], x[start], limitsy[1]);
        plot.drawLine(x[end], limitsy[0], x[end], limitsy[1]);
    } else {
        // draw a line for the recall limit
        plot.setColor(Color.magenta);
        plot.drawLine(limitsx[0] - rangex, recallLimit, limitsx[1] + rangex, recallLimit);
    }
    PlotWindow pw = Utils.display(title, plot);
    if (Utils.isNewWindow())
        idList[idCount++] = pw.getImagePlus().getID();
    return new double[][] { newX, newY };
}
Also used : PlotWindow(ij.gui.PlotWindow) Plot2(ij.gui.Plot2)

Example 3 with Plot2

use of ij.gui.Plot2 in project GDSC-SMLM by aherbert.

the class Noise method drawPlot.

/**
	 * Build a plot of the noise estimate from the current frame.
	 * Limit the preview to 100 frames.
	 */
private void drawPlot() {
    NoiseEstimator.Method method1 = NoiseEstimator.Method.values()[algorithm];
    NoiseEstimator.Method method2 = NoiseEstimator.Method.values()[algorithm2];
    IJ.showStatus("Estimating noise ...");
    boolean twoMethods = method1 != method2;
    boolean preserveResiduals = method1.name().contains("Residuals") && method2.name().contains("Residuals") && twoMethods;
    int start = imp.getCurrentSlice();
    int end = FastMath.min(imp.getStackSize(), start + 100);
    int size = end - start + 1;
    double[] xValues = new double[size];
    double[] yValues1 = new double[size];
    double[] yValues2 = (twoMethods) ? new double[size] : null;
    ImageStack stack = imp.getImageStack();
    Rectangle bounds = imp.getProcessor().getRoi();
    float[] buffer = null;
    for (int slice = start, i = 0; slice <= end; slice++, i++) {
        IJ.showProgress(i, size);
        final ImageProcessor ip = stack.getProcessor(slice);
        buffer = ImageConverter.getData(ip.getPixels(), ip.getWidth(), ip.getHeight(), bounds, buffer);
        final NoiseEstimator ne = new NoiseEstimator(buffer, bounds.width, bounds.height);
        ne.preserveResiduals = preserveResiduals;
        ne.setRange(lowestPixelsRange);
        xValues[i] = slice;
        yValues1[i] = ne.getNoise(method1);
        if (twoMethods)
            yValues2[i] = ne.getNoise(method2);
    }
    IJ.showProgress(1);
    IJ.showStatus("Plotting noise ...");
    // Get limits
    double[] a = Tools.getMinMax(xValues);
    double[] b1 = Tools.getMinMax(yValues1);
    if (twoMethods) {
        double[] b2 = Tools.getMinMax(yValues2);
        b1[0] = FastMath.min(b1[0], b2[0]);
        b1[1] = FastMath.max(b1[1], b2[1]);
    }
    String title = imp.getTitle() + " Noise";
    Plot2 plot = new Plot2(title, "Slice", "Noise", xValues, yValues1);
    double range = b1[1] - b1[0];
    if (range == 0)
        range = 1;
    plot.setLimits(a[0], a[1], b1[0] - 0.05 * range, b1[1] + 0.05 * range);
    plot.setColor(Color.blue);
    plot.draw();
    String label = String.format("Blue = %s", Utils.rounded(new Statistics(yValues1).getMean()));
    if (twoMethods) {
        plot.setColor(Color.red);
        plot.addPoints(xValues, yValues2, Plot2.LINE);
        label += String.format(", Red = %s", Utils.rounded(new Statistics(yValues2).getMean()));
    }
    plot.addLabel(0, 0, label);
    Utils.display(title, plot);
    IJ.showStatus("");
}
Also used : ImageStack(ij.ImageStack) Rectangle(java.awt.Rectangle) Plot2(ij.gui.Plot2) Statistics(gdsc.core.utils.Statistics) ImageProcessor(ij.process.ImageProcessor) NoiseEstimator(gdsc.core.utils.NoiseEstimator)

Example 4 with Plot2

use of ij.gui.Plot2 in project GDSC-SMLM by aherbert.

the class PSFCalculator method plotProfile.

/**
	 * @param airyWidth The Airy width
	 * @param factor Factor used to scale the Airy approximation using the Gaussian 
	 */
private void plotProfile(double airyWidth, double factor) {
    if (x == null) {
        x = Utils.newArray(200, -10, 0.1);
        y = new double[x.length];
        y2 = new double[x.length];
        for (int i = 0; i < x.length; i++) {
            y[i] = AiryPattern.intensity(x[i]);
        }
    }
    double[] x2 = new double[x.length];
    for (int i = 0; i < x2.length; i++) {
        x2[i] = x[i] * airyWidth;
        y2[i] = AiryPattern.intensityGaussian(x[i] / factor);
    }
    String title = "PSF profile";
    Plot2 p = new Plot2(title, "px", "", x2, y);
    p.addLabel(0, 0, "Blue = Airy; Red = Gaussian");
    p.setColor(Color.RED);
    p.addPoints(x2, y2, Plot2.LINE);
    final double sd = airyWidth * AIRY_TO_GAUSSIAN * factor;
    //intensityGaussian(1);
    final double sdHeight = 0.606530659;
    p.drawLine(-sd, 0, -sd, sdHeight);
    p.drawLine(sd, 0, sd, sdHeight);
    p.setColor(Color.BLUE);
    Utils.display(title, p);
}
Also used : Plot2(ij.gui.Plot2)

Example 5 with Plot2

use of ij.gui.Plot2 in project GDSC-SMLM by aherbert.

the class PSFCreator method showPlots.

private void showPlots(final double[] z, final double[] a, final double[] smoothAz, final double[] smoothA, final double[] xCoord, final double[] yCoord, final double[] sd, final double[] newZ, final double[] smoothX, final double[] smoothY, double[] smoothSd, final int cz) {
    PlotWindow amplitudeWindow = null;
    // Draw a plot of the amplitude
    if (a != null) {
        Plot2 plot = new Plot2(TITLE_AMPLITUDE, "z", "Amplitude", smoothAz, smoothA);
        double[] limits2 = Maths.limits(Maths.limits(a), smoothA);
        plot.setLimits(z[0], z[z.length - 1], limits2[0], limits2[1]);
        plot.addPoints(z, a, Plot2.CIRCLE);
        // Add a line for the z-centre
        plot.setColor(Color.GREEN);
        plot.addPoints(new double[] { cz, cz }, limits2, Plot2.LINE);
        plot.setColor(Color.BLACK);
        double amplitude = Double.NaN;
        for (int i = 0; i < smoothAz.length; i++) {
            if (smoothAz[i] == cz) {
                amplitude = smoothA[i];
                break;
            }
        }
        double maxAmplitude = Double.NaN;
        for (int i = 0; i < smoothAz.length; i++) {
            if (smoothAz[i] == zCentre) {
                maxAmplitude = smoothA[i];
                break;
            }
        }
        plot.addLabel(0, 0, String.format("Amplitude = %s (%sx). z = %s nm", Utils.rounded(amplitude), Utils.rounded(amplitude / maxAmplitude), Utils.rounded((slice - zCentre) * nmPerSlice)));
        amplitudeWindow = Utils.display(TITLE_AMPLITUDE, plot);
    }
    // Show plot of width, X centre, Y centre
    if (xCoord != null) {
        Plot2 plot = new Plot2(TITLE_PSF_PARAMETERS, "z", "px", newZ, smoothSd);
        // Get the limits
        double[] sd2 = invert(sd);
        double[] limits = Maths.limits(Maths.limits(Maths.limits(Maths.limits(xCoord), yCoord), sd), sd2);
        plot.setLimits(z[0], z[z.length - 1], limits[0], limits[1]);
        plot.addPoints(newZ, invert(smoothSd), Plot2.LINE);
        plot.addPoints(z, sd, Plot2.DOT);
        plot.addPoints(z, sd2, Plot2.DOT);
        plot.setColor(Color.BLUE);
        plot.addPoints(z, xCoord, Plot2.DOT);
        plot.addPoints(newZ, smoothX, Plot2.LINE);
        plot.setColor(Color.RED);
        plot.addPoints(z, yCoord, Plot2.DOT);
        plot.addPoints(newZ, smoothY, Plot2.LINE);
        // Add a line for the z-centre
        plot.setColor(Color.GREEN);
        plot.addPoints(new double[] { cz, cz }, limits, Plot2.LINE);
        plot.setColor(Color.BLACK);
        double width = Double.NaN;
        for (int i = 0; i < smoothSd.length; i++) {
            if (newZ[i] == cz) {
                width = smoothSd[i];
                break;
            }
        }
        plot.addLabel(0, 0, String.format("Width = %s nm (%sx). z = %s nm", Utils.rounded(width * nmPerPixel), Utils.rounded(width * nmPerPixel / psfWidth), Utils.rounded((slice - zCentre) * nmPerSlice)));
        // Check if the window will need to be aligned
        boolean alignWindows = (WindowManager.getFrame(TITLE_PSF_PARAMETERS) == null);
        PlotWindow psfWindow = Utils.display(TITLE_PSF_PARAMETERS, plot);
        if (alignWindows && psfWindow != null && amplitudeWindow != null) {
            // Put the two plots tiled together so both are visible
            Point l = psfWindow.getLocation();
            l.x = amplitudeWindow.getLocation().x;
            l.y = amplitudeWindow.getLocation().y + amplitudeWindow.getHeight();
            psfWindow.setLocation(l);
        }
    }
}
Also used : PlotWindow(ij.gui.PlotWindow) Plot2(ij.gui.Plot2) Point(java.awt.Point) BasePoint(gdsc.core.match.BasePoint) Point(java.awt.Point) BasePoint(gdsc.core.match.BasePoint)

Aggregations

Plot2 (ij.gui.Plot2)42 PlotWindow (ij.gui.PlotWindow)17 Point (java.awt.Point)9 BasePoint (gdsc.core.match.BasePoint)8 Statistics (gdsc.core.utils.Statistics)6 StoredDataStatistics (gdsc.core.utils.StoredDataStatistics)6 MemoryPeakResults (gdsc.smlm.results.MemoryPeakResults)5 WeightedObservedPoint (org.apache.commons.math3.fitting.WeightedObservedPoint)5 ClusterPoint (gdsc.core.clustering.ClusterPoint)4 PeakResultPoint (gdsc.smlm.ij.plugins.ResultsMatchCalculator.PeakResultPoint)4 PeakResult (gdsc.smlm.results.PeakResult)4 StoredData (gdsc.core.utils.StoredData)3 WindowOrganiser (ij.plugin.WindowOrganiser)3 Rectangle (java.awt.Rectangle)3 ArrayList (java.util.ArrayList)3 LoessInterpolator (org.apache.commons.math3.analysis.interpolation.LoessInterpolator)3 TooManyEvaluationsException (org.apache.commons.math3.exception.TooManyEvaluationsException)3 DescriptiveStatistics (org.apache.commons.math3.stat.descriptive.DescriptiveStatistics)3 Cluster (gdsc.core.clustering.Cluster)2 ClusteringEngine (gdsc.core.clustering.ClusteringEngine)2