All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.ac.sussex.gdsc.smlm.results.PeakResult Maven / Gradle / Ivy

Go to download

Genome Damage and Stability Centre SMLM Package Software for single molecule localisation microscopy (SMLM)

The newest version!
/*-
 * #%L
 * Genome Damage and Stability Centre SMLM Package
 *
 * Software for single molecule localisation microscopy (SMLM)
 * %%
 * Copyright (C) 2011 - 2023 Alex Herbert
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

package uk.ac.sussex.gdsc.smlm.results;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Objects;
import org.apache.commons.lang3.ArrayUtils;
import uk.ac.sussex.gdsc.core.utils.MathUtils;

/**
 * Specifies a peak fitting result.
 */
public class PeakResult implements Serializable {
  private static final long serialVersionUID = 20190319L;

  /** Index of the background in the parameters array. */
  public static final int BACKGROUND = 0;
  /** Index of the intensity in the parameters array. */
  public static final int INTENSITY = 1;
  /** Index of the x-position in the parameters array. */
  public static final int X = 2;
  /** Index of the y-position in the parameters array. */
  public static final int Y = 3;
  /** Index of the z-position in the parameters array. */
  public static final int Z = 4;
  /** Number of standard parameters. */
  public static final int STANDARD_PARAMETERS = 5;

  private static final String[] NAMES = {"Background", "Intensity", "X", "Y", "Z"};

  private int frame;
  private int origX;
  private int origY;
  private float origValue;
  private double error;
  private float noise;
  private float meanIntensity;

  /**
   * The parameters (for the standard parameters plus any PSF specific parameters). This is never
   * null.
   */
  private float[] params;
  /**
   * The parameter standard deviations (for the standard parameters plus any PSF specific
   * parameters). This may be null or the same length as {@link #params}.
   */
  private float[] paramStdDevs;

  /**
   * Gets the parameter name.
   *
   * 

Valid for the standard parameters ({@code index <} {@link #STANDARD_PARAMETERS}). * * @param index the index * @return the parameter name */ public static String getParameterName(int index) { return NAMES[index]; } /** * Instantiates a new peak result. * * @param frame the frame * @param origX the original X position * @param origY the original Y position * @param origValue the original value * @param error the error * @param noise the noise * @param meanIntensity the mean intensity * @param params the params (must not be null and must have at least {@value #STANDARD_PARAMETERS} * parameters) * @param paramsStdDev the params standard deviations (if not null must match the length of the * params array) * @throws IllegalArgumentException the illegal argument exception if the parameters are invalid */ public PeakResult(int frame, int origX, int origY, float origValue, double error, float noise, float meanIntensity, float[] params, float[] paramsStdDev) { Objects.requireNonNull(params, "Parameters must not be null"); if (params.length < STANDARD_PARAMETERS) { throw new IllegalArgumentException("Parameters must contain all standard parameters"); } if (paramsStdDev != null && paramsStdDev.length != params.length) { throw new IllegalArgumentException("Parameter deviations length must match parameters"); } this.frame = frame; this.origX = origX; this.origY = origY; this.origValue = origValue; this.error = error; this.noise = noise; this.meanIntensity = meanIntensity; this.params = params; this.paramStdDevs = paramsStdDev; } /** * Simple constructor to create a result with frame, location, and intensity. * * @param frame the frame * @param x the x position * @param y the y position * @param intensity the intensity */ public PeakResult(int frame, float x, float y, float intensity) { setFrame(frame); origX = (int) x; origY = (int) y; params = new float[5]; params[X] = x; params[Y] = y; params[INTENSITY] = intensity; } /** * Simple constructor to create a result with location and intensity. * * @param x the x position * @param y the y position * @param intensity the intensity */ public PeakResult(float x, float y, float intensity) { this(0, x, y, intensity); } /** * Copy constructor. * * @param source the source */ protected PeakResult(PeakResult source) { frame = source.frame; origX = source.origX; origY = source.origY; origValue = source.origValue; error = source.error; noise = source.noise; meanIntensity = source.meanIntensity; params = source.params.clone(); paramStdDevs = ArrayUtils.clone(source.paramStdDevs); } /** * Create a copy. * * @return the copy */ public PeakResult copy() { return new PeakResult(this); } /** * Create a resized copy with the specified number of parameters. The number of parameters will * not be reduced below {@link #STANDARD_PARAMETERS}. * *

This method can be used to add or remove PSF parameters: * *

   * PeakResult r;
   * // Remove extra parameters:
   * PeakResult simple = r.resize(0);
   * // Add an extra parameter:
   * PeakResult simple = r.resize(r.getNumberOfParameters() + 1);
   * 
* * @param parameters the number of parameters * @return the resized copy */ public PeakResult resize(int parameters) { final PeakResult copy = copy(); final int length = Math.max(STANDARD_PARAMETERS, parameters); copy.resizeParameters(length); if (copy.hasParameterDeviations()) { copy.resizeParameterDeviations(length); } return copy; } /** * Creates the params array for a peak result. * * @param background the background * @param intensity the intensity * @param x the x * @param y the y * @param z the z * @return the params array */ public static float[] createParams(float background, float intensity, float x, float y, float z) { return new float[] {background, intensity, x, y, z}; } /** * Utility function to check for equality. * * @param r1 the first result * @param r2 the second result * @return true, if equal */ public static boolean equals(PeakResult r1, PeakResult r2) { if (r1 == r2) { // The same or both null return true; } if (r1 == null || r2 == null) { // At least one is not null so this is not equal return false; } // Check parameters if (r1.getNumberOfParameters() != r2.getNumberOfParameters()) { return false; } for (int i = 0; i < r1.params.length; i++) { if (r1.params[i] != r2.params[i]) { return false; } } // Check primitive fields if (r1.frame != r2.frame) { return false; } if (r1.origX != r2.origX) { return false; } if (r1.origY != r2.origY) { return false; } if (r1.origValue != r2.origValue) { return false; } if (r1.noise != r2.noise) { return false; } if (r1.meanIntensity != r2.meanIntensity) { return false; } // Check optional properties if (r1.hasId()) { if (!r2.hasId() || r1.getId() != r2.getId()) { return false; } } else if (r2.hasId()) { return false; } if (r1.hasCategory()) { if (!r2.hasCategory() || r1.getCategory() != r2.getCategory()) { return false; } } else if (r2.hasCategory()) { return false; } if (r1.hasEndFrame()) { if (!r2.hasEndFrame() || r1.getEndFrame() != r2.getEndFrame()) { return false; } } else if (r2.hasEndFrame()) { return false; } if (r1.hasPrecision()) { if (!r2.hasPrecision() || r1.getPrecision() != r2.getPrecision()) { return false; } } else if (r2.hasPrecision()) { return false; } // Check parameters deviations. Do this last as they are not often used. if (r1.paramStdDevs != null) { if (r2.paramStdDevs == null) { return false; } for (int i = 0; i < r1.paramStdDevs.length; i++) { if (r1.paramStdDevs[i] != r2.paramStdDevs[i]) { return false; } } } else if (r2.paramStdDevs != null) { return false; } return true; } /** * Gets the background for the first peak. * * @return The background for the first peak. */ public float getBackground() { return params[BACKGROUND]; } /** * Sets the background for the first peak. * * @param background the new background */ public void setBackground(float background) { params[BACKGROUND] = background; } /** * Get the intensity for the first peak. * * @return The intensity of the first peak */ public float getIntensity() { return params[INTENSITY]; } /** * Sets the intensity for the first peak. * * @param intensity the new intensity */ public void setIntensity(float intensity) { params[INTENSITY] = intensity; } /** * Gets the x position for the first peak. * * @return The x position for the first peak */ public float getXPosition() { return params[X]; } /** * Sets the x position for the first peak. * * @param x the new x position */ public void setXPosition(float x) { params[X] = x; } /** * Gets the y position for the first peak. * * @return The y position for the first peak */ public float getYPosition() { return params[Y]; } /** * Sets the y position for the first peak. * * @param y the new y position */ public void setYPosition(float y) { params[Y] = y; } /** * Gets the z position for the first peak. * * @return The z position for the first peak */ public float getZPosition() { return params[Z]; } /** * Sets the z position for the first peak. * * @param z the new z position */ public void setZPosition(float z) { params[Z] = z; } /** * Gets the time frame. * * @return The time frame */ public int getFrame() { return frame; } /** * Sets the time frame. * * @param frame The time frame */ public void setFrame(int frame) { this.frame = frame; } /** * Gets the original X position in the fitted frame. * * @return the original X position */ public int getOrigX() { return origX; } /** * Sets the original X position in the fitted frame. * * @param origX the original X position in the fitted frame. */ public void setOrigX(int origX) { this.origX = origX; } /** * Gets the original Y position in the fitted frame. * * @return the original Y position in the fitted frame. */ public int getOrigY() { return origY; } /** * Sets the original Y position in the fitted frame. * * @param origY the original Y position in the fitted frame. */ public void setOrigY(int origY) { this.origY = origY; } /** * Gets the original value in the fitted frame. * * @return the original value in the fitted frame. */ public float getOrigValue() { return origValue; } /** * Sets the original value in the fitted frame. * * @param origValue the original value in the fitted frame. */ public void setOrigValue(float origValue) { this.origValue = origValue; } /** * Gets the error. * * @return the error */ public double getError() { return error; } /** * Sets the error. * * @param error the new error */ public void setError(double error) { this.error = error; } /** * Gets the noise. * * @return the noise */ public float getNoise() { return noise; } /** * Sets the noise. * * @param noise the new noise */ public void setNoise(float noise) { this.noise = noise; } /** * Checks for noise. * * @return true, if successful */ public boolean hasNoise() { return noise > 0; } /** * Gets the mean intensity. * *

This requires a knowledge of the PSF used to create the result. It could be the peak signal * in the PSF or the average signal over a range of the PSF, e.g. the area covered from the maxima * to half-maxima for spots. * * @return the mean intensity */ public float getMeanIntensity() { return meanIntensity; } /** * Sets the mean intensity. * *

This requires a knowledge of the PSF used to create the result. It could be the peak signal * in the PSF or the average signal over a range of the PSF, e.g. the area covered from the maxima * to half-maxima for spots. * * @param meanIntensity the new mean intensity */ public void setMeanIntensity(float meanIntensity) { this.meanIntensity = meanIntensity; } /** * Checks for mean intensity. * * @return true, if successful */ public boolean hasMeanIntensity() { return meanIntensity > 0; } /** * Gets the Signal-to-Noise Ratio (SNR). This is the ratio of the average signal value to the * background standard deviation. * * @return the snr * @see #getMeanIntensity() * @see #getNoise() */ public float getSnr() { return MathUtils.div0(meanIntensity, noise); } /** * Checks for end frame. Derived classes can override this. * * @return true, if successful */ public boolean hasEndFrame() { return false; } /** * Gets the end frame. Default = {@link #getFrame()}. Derived classes can override this. * * @return The last time frame that this result corresponds to */ public int getEndFrame() { return frame; } /** * Checks for id. Derived classes can override this. * * @return true, if successful */ public boolean hasId() { return false; } /** * Gets the id. Default = 0. Derived classes can override this. * * @return The results identifier */ public int getId() { return 0; } /** * Checks for category. Derived classes can override this. * * @return true, if successful */ public boolean hasCategory() { return false; } /** * Gets the category. Default = 0. Derived classes can override this. * * @return The results category */ public int getCategory() { return 0; } /** * Checks for precision. Derived classes can override this. * * @return true, if successful */ public boolean hasPrecision() { return false; } /** * Gets the localisation precision. Default = Double.NaN. Derived classes can override this. * *

This is provided so that results can be loaded from external sources. * * @return the precision (in nm) */ public double getPrecision() { return Double.NaN; } /** * Return the true positive score for use in classification analysis. * * @return The true positive score */ public double getTruePositiveScore() { return (origValue != 0) ? 1 : 0; } /** * Return the false positive score for use in classification analysis. * * @return The false positive score */ public double getFalsePositiveScore() { return 1 - getTruePositiveScore(); } /** * Return the true negative score for use in classification analysis. * * @return The true negative score */ public double getTrueNegativeScore() { return (origValue != 0) ? 0 : 1; } /** * Return the false negative score for use in classification analysis. * * @return The false negative score */ public double getFalseNegativeScore() { return 1 - getTrueNegativeScore(); } /** * Return the squared distance to the other peak result. * * @param result The result * @return The squared distance */ public double distance2(PeakResult result) { final double dx = getXPosition() - result.getXPosition(); final double dy = getYPosition() - result.getYPosition(); return dx * dx + dy * dy; } /** * Return the squared distance to the other coordinate. * * @param x the x * @param y the y * @return The squared distance */ public double distance2(final double x, final double y) { final double dx = getXPosition() - x; final double dy = getYPosition() - y; return dx * dx + dy * dy; } /** * Return the distance to the other peak result. * * @param result The result * @return The distance */ public double distance(PeakResult result) { return Math.sqrt(distance2(result)); } /** * Return the distance to the other coordinate. * * @param x the x * @param y the y * @return The distance */ public double distance(final double x, final double y) { return Math.sqrt(distance2(x, y)); } /** * This methods return the x-position. To allow filters to use the actual shift requires either * off-setting the position with the initial fit position, or extending this class so the shift * can be stored. * * @return the x shift */ public float getXShift() { return getXPosition(); } /** * This methods return the y-position. To allow filters to use the actual shift requires either * off-setting the position with the initial fit position, or extending this class so the shift * can be stored. * * @return the y shift */ public float getYShift() { return getYPosition(); } /** * Gets the parameters. This is a direct reference to the instance parameter array so use with * caution. * * @return the parameters */ public float[] getParameters() { return params; } /** * Checks for parameter deviations. * * @return true, if successful */ public boolean hasParameterDeviations() { return paramStdDevs != null; } /** * Gets the parameter deviations. This is a direct reference to the instance parameter array so * use with caution. * * @return the parameter deviations */ public float[] getParameterDeviations() { return paramStdDevs; } /** * Gets the number of parameters. * * @return the number of parameters */ public int getNumberOfParameters() { return params.length; } /** * Gets the parameter for the given index. * * @param index the index * @return the parameter */ public float getParameter(int index) { return params[index]; } /** * Sets the parameter for the given index. * * @param index the index * @param value the value */ public void setParameter(int index, float value) { params[index] = value; } /** * Gets the parameter deviation for the given index. * * @param index the index * @return the parameter deviation */ public float getParameterDeviation(int index) { return paramStdDevs[index]; } /** * Sets the parameter deviation for the given index. * * @param index the index * @param value the value */ public void setParameterDeviation(int index, float value) { paramStdDevs[index] = value; } /** * Resize the parameters. * * @param length the new length */ void resizeParameters(int length) { params = Arrays.copyOf(params, length); } /** * Resize parameter deviations. * * @param length the length */ void resizeParameterDeviations(int length) { paramStdDevs = Arrays.copyOf(paramStdDevs, length); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy