package gdsc.smlm.results.filter;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
/*-----------------------------------------------------------------------------
* GDSC SMLM Software
*
* Copyright (C) 2016 Alex Herbert
* Genome Damage and Stability Centre
* University of Sussex, UK
*
* 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.
*---------------------------------------------------------------------------*/
/**
* Support direct filtering of PreprocessedPeakResult objects.
* <p>
* The decision to support for filtering as both a DirectFilter and Filter at the same time is left to the implementing
* class. It is not a requirement.
*/
public abstract class DirectFilter extends Filter implements IDirectFilter
{
@XStreamOmitField
private int result = 0;
@XStreamOmitField
private float strength = Float.NaN;
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.filter.IDirectFilter#setup()
*/
public void setup()
{
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.filter.IDirectFilter#setup(int)
*/
public void setup(final int flags)
{
}
/**
* Check if all of the given bits are set in the flags
*
* @param flags
* @param bits
* @return True if all are set
*/
public static boolean areSet(final int flags, final int bits)
{
return (flags & bits) == bits;
}
/**
* Check if any of the given bits are set in the flags
*
* @param flags
* @param bits
* @return True if any are set
*/
public static boolean anySet(final int flags, final int bits)
{
return (flags & bits) != 0;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.filter.IDirectFilter#accept(gdsc.smlm.results.filter.PreprocessedPeakResult)
*/
final public boolean accept(final PreprocessedPeakResult peak)
{
return (result = validate(peak)) == 0;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.filter.IDirectFilter#validate(gdsc.smlm.results.filter.PreprocessedPeakResult)
*/
public abstract int validate(final PreprocessedPeakResult peak);
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.filter.Filter#getFilterType()
*/
public FilterType getFilterType()
{
return FilterType.DIRECT;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.filter.IDirectFilter#getResult()
*/
public int getResult()
{
return result;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.filter.IDirectFilter#copy()
*/
public IDirectFilter copy()
{
return (IDirectFilter) clone();
}
/**
* Generate a status message using all the properties of the peak that failed validation
*
* @param peak
* The peak
* @param flags
* The validation flags
* @return The message
*/
public static String getStatusMessage(PreprocessedPeakResult peak, int flags)
{
if (flags == 0)
return "";
StringBuilder sb = new StringBuilder();
//@formatter:off
if (areSet(flags, V_AMPLITUDE)) append(sb, "Amplitude", peak.getAmplitude());
if (areSet(flags, V_PHOTONS)) append(sb, "Photon", peak.getPhotons());
if (areSet(flags, V_SNR)) append(sb, "SNR", peak.getSNR());
if (areSet(flags, V_NOISE)) append(sb, "Noise", peak.getNoise());
if (areSet(flags, V_LOCATION_VARIANCE)) append(sb, "Precision", Math.sqrt(peak.getLocationVariance()));
if (areSet(flags, V_LOCATION_VARIANCE2)) append(sb, "Precision2", Math.sqrt(peak.getLocationVariance2()));
if (areSet(flags, V_SD)) append(sb, "SD", peak.getSD());
if (areSet(flags, V_BACKGROUND)) append(sb, "Background", peak.getBackground());
if (areSet(flags, V_AMPLITUDE)) append(sb, "Amplitude", peak.getAmplitude());
if (areSet(flags, V_ANGLE)) append(sb, "Angle", peak.getAngle());
if (areSet(flags, V_X)) append(sb, "X", peak.getX());
if (areSet(flags, V_Y)) append(sb, "Y", peak.getY());
if (areSet(flags, V_X_RELATIVE_SHIFT)) append(sb, "X Relative Shift", Math.sqrt(peak.getXRelativeShift2()));
if (areSet(flags, V_Y_RELATIVE_SHIFT)) append(sb, "Y Relative Shift", Math.sqrt(peak.getYRelativeShift2()));
if (areSet(flags, V_X_SD)) append(sb, "X SD", peak.getXSD());
if (areSet(flags, V_Y_SD)) append(sb, "Y SD", peak.getYSD());
if (areSet(flags, V_X_SD_FACTOR)) append(sb, "X SD Factor", peak.getXSDFactor());
if (areSet(flags, V_Y_SD_FACTOR)) append(sb, "Y SD Factor", peak.getYSDFactor());
//@formatter:on
return sb.toString();
}
private static void append(StringBuilder sb, String name, double value)
{
if (sb.length() != 0)
sb.append("; ");
sb.append(name);
sb.append('=');
sb.append(value);
}
/**
* Generate a message using all flags that are set
*
* @param flags
* The validation flags
* @return The message
*/
public static String getFlagMessage(int flags)
{
if (flags == 0)
return "";
StringBuilder sb = new StringBuilder();
//@formatter:off
if (areSet(flags, V_AMPLITUDE)) append(sb, "Amplitude");
if (areSet(flags, V_PHOTONS)) append(sb, "Photon");
if (areSet(flags, V_SNR)) append(sb, "SNR");
if (areSet(flags, V_NOISE)) append(sb, "Noise");
if (areSet(flags, V_LOCATION_VARIANCE)) append(sb, "Precision");
if (areSet(flags, V_LOCATION_VARIANCE2)) append(sb, "Precision2");
if (areSet(flags, V_SD)) append(sb, "SD");
if (areSet(flags, V_BACKGROUND)) append(sb, "Background");
if (areSet(flags, V_AMPLITUDE)) append(sb, "Amplitude");
if (areSet(flags, V_ANGLE)) append(sb, "Angle");
if (areSet(flags, V_X)) append(sb, "X");
if (areSet(flags, V_Y)) append(sb, "Y");
if (areSet(flags, V_X_RELATIVE_SHIFT)) append(sb, "X Relative Shift");
if (areSet(flags, V_Y_RELATIVE_SHIFT)) append(sb, "Y Relative Shift");
if (areSet(flags, V_X_SD)) append(sb, "X SD");
if (areSet(flags, V_Y_SD)) append(sb, "Y SD");
if (areSet(flags, V_X_SD_FACTOR)) append(sb, "X SD Factor");
if (areSet(flags, V_Y_SD_FACTOR)) append(sb, "Y SD Factor");
//@formatter:on
return sb.toString();
}
private static void append(StringBuilder sb, String name)
{
if (sb.length() != 0)
sb.append("; ");
sb.append(name);
}
/**
* Gets the strength. This is used in the {@link #weakestUnsafe(Filter)} method before the parameters are compared.
*
* @return the strength
*/
public float getStrength()
{
return strength;
}
/**
* Sets the strength. This is used in the {@link #weakestUnsafe(DirectFilter)} method before the parameters are
* compared.
*
* @param strength
* the new strength
*/
public void setStrength(float strength)
{
this.strength = strength;
}
/**
* Compute strength using the limits on the parameters. Strength is computed using the distance from the lower/upper
* bounds inside the range for each parameter. The bounds to choose is determined by the method
* {@link #lowerBoundOrientation(int)}.
* <p>
* Warning: No checks are made for the input arrays to be null or incorrect length.
* <p>
* If lower is equal or above upper then this index is ignored.
*
* @param lower
* the lower limit
* @param upper
* the upper limit
* @return the strength
*/
public float computeStrength(double[] lower, double[] upper)
{
double s = 0;
double[] p = getParameters();
for (int i = 0; i < p.length; i++)
{
final double range = upper[i] - lower[i];
if (range <= 0)
// Ignore this as it will produce bad strength data
continue;
final int o = lowerBoundOrientation(i);
if (o < 0)
{
s += (p[i] - lower[i]) / range;
}
else if (o > 0)
{
s += (upper[i] - p[i]) / range;
}
}
return (float) s;
}
/**
* Get the lower bound orientation for the given parameter index. This is the orientation of the bound for the
* weakest parameter. E.g. if a parameter must be low to be weak, the orientation is -1.
*
* @param index
* the index
* @return the lower bound orientation.
*/
public int lowerBoundOrientation(int index)
{
return -1;
}
/**
* Compare to the other filter using the strength property and return the weakest. If equal (or the strength is not
* set) then default to the {@link #weakest(Filter)} method.
* <p>
* This method does not check for null or if the other filter has a different number of parameters.
*
* @param o
* The other filter
* @return the count difference
*/
public int weakestUnsafe(DirectFilter o)
{
// // This should not happen if used correctly
// if (Float.isNaN(strength) || Float.isNaN(o.strength))
// {
// System.out.println("No strength (nan)");
// return super.weakestUnsafe(o);
// }
// if (Float.isInfinite(strength) || Float.isInfinite(o.strength))
// {
// System.out.println("No strength (inf)");
// return super.weakestUnsafe(o);
// }
//System.out.println("weakestUnsafe");
//int result = super.weakestUnsafe(o);
if (this.strength < o.strength)
{
//if (result >= 0)
// System.out.println("Strength not same as weakest");
return -1;
}
if (this.strength > o.strength)
{
//if (result <= 0)
// System.out.println("Strength not same as weakest");
return 1;
}
return super.weakestUnsafe(o);
}
}