package gdsc.colocalisation;
/*-----------------------------------------------------------------------------
* GDSC Plugins for ImageJ
*
* Copyright (C) 2011 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 2 of the License, or
* (at your option) any later version.
*---------------------------------------------------------------------------*/
import ij.IJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.gui.Roi;
import ij.process.ImageProcessor;
import java.awt.Rectangle;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
/**
* Class that allow the threshold for colocalisation analysis to be calculated for two images. A regression is performed
* between the two images. If the correlation is positive a search is performed to iteratively reduce two image
* thresholds until all pixels below these thresholds produce a negative correlation.
*
* Based on the Colocalisation_Threshold plugin at
* http://www.uhnres.utoronto.ca/facilities/wcif/imagej/colour_analysis.htm#6.3 Colocalisation Threshold
*/
public class ColocalisationThreshold
{
private ImagePlus imp1;
private ImagePlus imp2;
private int roiIndex = 0;
private int maxIterations = 30;
private boolean includeNullPixels = false;
private boolean includeSaturatedPixels = false;
private boolean exhaustiveSearch = false;
private ArrayList<ThresholdResult> results;
private boolean correlated = false;
private int threshold1;
private int threshold2;
private double m;
private double b;
private double rTotal;
private double rBelowThreshold;
private double rAboveThreshold;
private double rThreshold = 0;
private double convergenceTolerance = 0.001;
private double searchTolerance = 0.05;
private int ch1Max = 0;
private int ch2Max = 0;
private int ch1Min = Integer.MAX_VALUE;
private int ch2Min = Integer.MAX_VALUE;
/**
* Constructor
*
* @param imp1
* Image 1
* @param imp2
* Image 2
*/
public ColocalisationThreshold(ImagePlus imp1, ImagePlus imp2)
{
init(imp1, imp2, 0);
}
/**
* Constructor
*
* @param imp1
* Image 1
* @param imp2
* Image 2
* @param roiIndex
* Contains the index of the image ROI that should be analysed (0,1,2)
*/
public ColocalisationThreshold(ImagePlus imp1, ImagePlus imp2, int roiIndex)
{
init(imp1, imp2, roiIndex);
}
private void init(ImagePlus imp1, ImagePlus imp2, int roiIndex)
{
if (imp1 == null || imp2 == null)
{
throw new NullPointerException("Input images must not be null");
}
if (imp1.getWidth() != imp2.getWidth() || imp1.getHeight() != imp2.getHeight())
{
throw new RuntimeException("Input images must have the same dimensions");
}
if (imp1.getStackSize() != imp2.getImageStackSize())
{
throw new RuntimeException("Input images must have the same stack size");
}
this.imp1 = imp1;
this.imp2 = imp2;
this.roiIndex = roiIndex;
}
/**
* Performs iterative correlation thresholding. Thresholds are lowered until all pixels below these thresholds
* produce a negative correlation.
*
* @return true if correlated
*/
public boolean correlate()
{
IJ.log("=-=-=-=-=-=-=");
IJ.log("Performing correlation ...");
// Set results to default
threshold1 = 0;
threshold2 = 0;
rBelowThreshold = Double.NaN;
m = 0;
b = 0;
correlated = false;
int ch1;
int ch2;
int ch3;
ch1Max = 0;
ch2Max = 0;
ch1Min = Integer.MAX_VALUE;
ch2Min = Integer.MAX_VALUE;
// Set up the ROI
ImageProcessor ipMask = null;
Rectangle roiRect = null;
if (roiIndex != 0)
{
ImagePlus roiImage = (roiIndex == 1) ? imp1 : imp2;
Roi roi = roiImage.getRoi();
if (roi != null)
{
roiRect = roi.getBounds();
// Use a mask for an irregular ROI
if (roi.getType() != Roi.RECTANGLE)
{
ipMask = roiImage.getMask();
}
}
else
{
// Reset the choice for next time
roiIndex = 0;
}
}
// Speed up the processing by extracting the pixels arrays for the ROI into a new stack.
// This will allow the array to be iterated over directly.
int size = countPixels(roiRect, ipMask);
int[] i1 = new int[size];
int[] i2 = new int[size];
extractPixels(roiRect, ipMask, i1, i2);
// start regression
long startTime = System.currentTimeMillis();
// A regression is required using all the non-zero pixels
long ch1Sum = 0;
long ch2Sum = 0;
long ch3Sum = 0;
int nZero = 0;
// get means
for (int i = i1.length; i-- > 0;)
{
ch1 = i1[i];
ch2 = i2[i];
if (ch1Max < ch1)
ch1Max = ch1;
if (ch1Min > ch1)
ch1Min = ch1;
if (ch2Max < ch2)
ch2Max = ch2;
if (ch2Min > ch2)
ch2Min = ch2;
ch3 = ch1 + ch2;
ch1Sum += ch1;
ch2Sum += ch2;
ch3Sum += ch3;
if (ch1 == 0 && ch2 == 0)
nZero++;
}
int n = (includeNullPixels) ? size : size - nZero;
double ch1Mean = ch1Sum / (double) n;
double ch2Mean = ch2Sum / (double) n;
double ch3Mean = ch3Sum / (double) n;
// TODO - Add a check to ensure that the sum will not lose precision as the total aggregates.
// This could be done by keeping a BigDecimal to store the overall sum. When the rolling total
// reaches the a specified precision limit for a double then it should be added to the
// BigDecimal and reset. The precision limit could be set using the value of the mean,
// e.g. 1e10 times bigger than the mean.
// Calculate variances
double ch1mch1MeanSqSum = 0;
double ch2mch2MeanSqSum = 0;
double ch1mch2MeanSqSum = 0;
double ch3mch3MeanSqSum = 0;
for (int i = i1.length; i-- > 0;)
{
ch1 = i1[i];
ch2 = i2[i];
ch3 = ch1 + ch2;
ch1mch1MeanSqSum += (ch1 - ch1Mean) * (ch1 - ch1Mean);
ch2mch2MeanSqSum += (ch2 - ch2Mean) * (ch2 - ch2Mean);
ch1mch2MeanSqSum += (ch1 - ch1Mean) * (ch2 - ch2Mean);
ch3mch3MeanSqSum += (ch3 - ch3Mean) * (ch3 - ch3Mean);
}
rTotal = ch1mch2MeanSqSum / Math.sqrt(ch1mch1MeanSqSum * ch2mch2MeanSqSum);
IJ.log("R = " + IJ.d2s(rTotal, 4));
if (rTotal < searchTolerance || rTotal < rThreshold)
{
// No correlation at all
return correlationResult(false, "No correlation found.");
}
// http://mathworld.wolfram.com/Covariance.html
// ?2 = X2?(X)2
// = E[X2]?(E[X])2
// var (x+y) = var(x)+var(y)+2(covar(x,y));
// 2(covar(x,y)) = var(x+y) - var(x)-var(y);
double ch1Var = ch1mch1MeanSqSum / (n - 1);
double ch2Var = ch2mch2MeanSqSum / (n - 1);
double ch3Var = ch3mch3MeanSqSum / (n - 1);
double ch1ch2covar = 0.5 * (ch3Var - (ch1Var + ch2Var));
// do regression
// See:Dissanaike and Wang
// http://papers.ssrn.com/sol3/papers.cfm?abstract_id=407560
double denom = 2 * ch1ch2covar;
double num = ch2Var - ch1Var +
Math.sqrt((ch2Var - ch1Var) * (ch2Var - ch1Var) + (4 * ch1ch2covar * ch1ch2covar));
m = num / denom;
b = ch2Mean - m * ch1Mean;
IJ.log("Channel 2 = Channel 1 * " + IJ.d2s(m, 4) + ((b < 0) ? " " : " +") + IJ.d2s(b, 4));
IJ.log("Channel 1 range " + ch1Min + " - " + ch1Max);
IJ.log("Channel 2 range " + ch2Min + " - " + ch2Max);
double ch2MinCalc = ch1Min * m + b;
double ch2MaxCalc = ch1Max * m + b;
IJ.log("Channel 2 calculated range " + IJ.d2s(ch2MinCalc, 0) + " - " + IJ.d2s(ch2MaxCalc, 0));
// Set-up for convergence
// The magnitude of b represents the difference of mean random overlap between channels
// after correction for their correlation, i.e. difference in noise.
// If the noise in channel 2 is higher than channel 1 (i.e. b is positive) then the channel 1
// threshold can approach zero.
int lowerThreshold = (int) Math.ceil((ch2Min - b) / m);
int upperThreshold = (int) Math.floor((ch2Max - b) / m);
IJ.log("Channel 1 interpolated range " + lowerThreshold + " - " + upperThreshold);
if (lowerThreshold < ch1Min)
lowerThreshold = 0;
if (upperThreshold > ch1Max)
upperThreshold = ch1Max;
IJ.log("Channel 1 search range " + lowerThreshold + " - " + upperThreshold);
int currentThreshold = upperThreshold;
if (rTotal == 1.0)
{
// Perfect correlation. The default results are OK.
IJ.log("Perfect correlation found (did you select the same input image for both channels?).");
}
else if (upperThreshold - lowerThreshold < 1)
{
IJ.log("No range to search for positive correlations.");
}
else
{
boolean result;
if (exhaustiveSearch)
{
result = findThresholdExhaustive(i1, i2, lowerThreshold, upperThreshold, currentThreshold);
}
else
{
result = findThreshold(i1, i2, lowerThreshold, upperThreshold, currentThreshold);
}
if (!result)
return false;
}
double seconds = (System.currentTimeMillis() - startTime) / 1000.0;
return correlationResult(true, "Threshold calculation time = " + seconds);
}
public static double calculateCorrelation(long sumX, long sumXY, long sumXX, long sumYY, long sumY, long n)
{
BigInteger nSumXY = BigInteger.valueOf(sumXY).multiply(BigInteger.valueOf(n));
BigInteger nSumXX = BigInteger.valueOf(sumXX).multiply(BigInteger.valueOf(n));
BigInteger nSumYY = BigInteger.valueOf(sumYY).multiply(BigInteger.valueOf(n));
nSumXY = nSumXY.subtract(BigInteger.valueOf(sumX).multiply(BigInteger.valueOf(sumY)));
nSumXX = nSumXX.subtract(BigInteger.valueOf(sumX).multiply(BigInteger.valueOf(sumX)));
nSumYY = nSumYY.subtract(BigInteger.valueOf(sumY).multiply(BigInteger.valueOf(sumY)));
BigInteger product = nSumXX.multiply(nSumYY);
return nSumXY.doubleValue() / Math.sqrt(product.doubleValue());
// double pearsons1 = n * sumXY - (sumX * sumY);
// double pearsons2 = n * sumXX - (sumX * sumX);
// double pearsons3 = n * sumYY - (sumY * sumY);
// return pearsons1 / (Math.sqrt(pearsons2 * pearsons3));
}
private int countPixels(Rectangle roiRect, ImageProcessor ipMask)
{
int count = 0;
int nslices = imp1.getStackSize();
int width = imp1.getWidth();
int height = imp1.getHeight();
int rwidth, rheight;
if (roiRect == null)
{
rwidth = width;
rheight = height;
}
else
{
rwidth = roiRect.width;
rheight = roiRect.height;
}
for (int y = rheight; y-- > 0;)
{
for (int x = rwidth; x-- > 0;)
{
if (ipMask == null || ipMask.get(x, y) != 0)
{
count++;
}
}
}
return count * nslices;
}
private void extractPixels(Rectangle roiRect, ImageProcessor ipMask, int[] i1, int[] i2)
{
int count = 0;
int nslices = imp1.getStackSize();
int width = imp1.getWidth();
int height = imp1.getHeight();
int xOffset, yOffset, rwidth, rheight;
if (roiRect == null)
{
xOffset = 0;
yOffset = 0;
rwidth = width;
rheight = height;
}
else
{
xOffset = roiRect.x;
yOffset = roiRect.y;
rwidth = roiRect.width;
rheight = roiRect.height;
}
ImageStack img1 = imp1.getStack();
ImageStack img2 = imp2.getStack();
for (int s = 1; s <= nslices; s++)
{
ImageProcessor ip1 = img1.getProcessor(s);
ImageProcessor ip2 = img2.getProcessor(s);
for (int y = rheight; y-- > 0;)
{
for (int x = rwidth; x-- > 0;)
{
if (ipMask == null || ipMask.get(x, y) != 0)
{
i1[count] = ip1.getPixel(x + xOffset, y + yOffset);
i2[count] = ip2.getPixel(x + xOffset, y + yOffset);
count++;
}
}
}
}
}
private boolean findThreshold(int[] i1, int[] i2, int lowerThreshold, int upperThreshold, int currentThreshold)
{
int ch1, ch2;
// Create the results and add the global correlation - this will be the fallback result
results = new ArrayList<ThresholdResult>(maxIterations);
results.add(new ThresholdResult(0, 0, rTotal, Double.NaN));
results.add(new ThresholdResult(ch1Max, ch2Max, Double.NaN, rTotal));
// We already have the complete correlation so move the threshold down for the first step.
int newThreshold = decreaseThreshold(currentThreshold, lowerThreshold);
int iteration = 1;
while (iteration <= maxIterations)
{
if (IJ.escapePressed())
{
IJ.beep();
return correlationResult(false, "IJ plugin cancelled");
}
// Check if the threshold has been updated.
if (currentThreshold == newThreshold || isRepeatThreshold(results, newThreshold))
{
IJ.log("Convergence at threshold " + newThreshold);
break;
}
currentThreshold = newThreshold;
// Set new thresholds
threshold1 = currentThreshold;
threshold2 = (int) Math.round(((double) threshold1 * m) + b);
// A = both channels above the threshold
// B = either channel below the threshold
long ch1SumA = 0, ch2SumA = 0;
int nZeroA = 0, nA = 0;
long ch1SumB = 0, ch2SumB = 0;
int nZeroB = 0, nB = 0;
// Get means
for (int i = i1.length; i-- > 0;)
{
ch1 = i1[i];
ch2 = i2[i];
if ((ch1 < (threshold1)) || (ch2 < (threshold2)))
{
if (ch1 == 0 && ch2 == 0)
{
nZeroB++;
}
else
{
ch1SumB += ch1;
ch2SumB += ch2;
}
nB++;
}
else
{
if (ch1 == 0 && ch2 == 0)
{
nZeroA++;
}
else
{
ch1SumA += ch1;
ch2SumA += ch2;
}
nA++;
}
}
if (!includeNullPixels)
{
nA -= nZeroA;
nB -= nZeroB;
}
double ch1MeanA = ch1SumA / (double) nA;
double ch2MeanA = ch2SumA / (double) nA;
double ch1MeanB = ch1SumB / (double) nB;
double ch2MeanB = ch2SumB / (double) nB;
// Calculate correlation
double ch1mch1MeanSqSumA = 0;
double ch2mch2MeanSqSumA = 0;
double ch1mch2MeanSqSumA = 0;
double ch1mch1MeanSqSumB = 0;
double ch2mch2MeanSqSumB = 0;
double ch1mch2MeanSqSumB = 0;
for (int i = i1.length; i-- > 0;)
{
ch1 = i1[i];
ch2 = i2[i];
if ((ch1 < (threshold1)) || (ch2 < (threshold2)))
{
ch1mch1MeanSqSumB += (ch1 - ch1MeanB) * (ch1 - ch1MeanB);
ch2mch2MeanSqSumB += (ch2 - ch2MeanB) * (ch2 - ch2MeanB);
ch1mch2MeanSqSumB += (ch1 - ch1MeanB) * (ch2 - ch2MeanB);
}
else
{
ch1mch1MeanSqSumA += (ch1 - ch1MeanA) * (ch1 - ch1MeanA);
ch2mch2MeanSqSumA += (ch2 - ch2MeanA) * (ch2 - ch2MeanA);
ch1mch2MeanSqSumA += (ch1 - ch1MeanA) * (ch2 - ch2MeanA);
}
}
double rA = ch1mch2MeanSqSumA / Math.sqrt(ch1mch1MeanSqSumA * ch2mch2MeanSqSumA);
double rB = ch1mch2MeanSqSumB / Math.sqrt(ch1mch1MeanSqSumB * ch2mch2MeanSqSumB);
// if r is not a number then set divide by zero to be true
boolean divByZero;
if (Double.isNaN(rB) || nB == 0)
{
divByZero = true;
}
else
{
divByZero = false;
results.add(new ThresholdResult(threshold1, threshold2, rA, rB));
}
IJ.log(iteration + ", c1(threshold)=" + threshold1 + ", c2(threshold)=" + threshold2 + " : r=" +
IJ.d2s(rA, 4) + ", r2=" + ((divByZero) ? "NaN" : IJ.d2s(rB, 4)));
// Change threshold max
// Ensure that the correlation above the thresholds is positive and below is negative.
// If there are no pixels below the threshold then a divide by zero error occurs.
if (divByZero || rB < rThreshold) // || r < r2)
{
lowerThreshold = currentThreshold;
newThreshold = increaseThreshold(currentThreshold, upperThreshold);
}
else
{
upperThreshold = currentThreshold;
newThreshold = decreaseThreshold(currentThreshold, lowerThreshold);
}
// If our r is close to our level of tolerance then set threshold has been found.
// Only true if the correlation above the threshold is positive.
if (Math.abs(rB - rThreshold) < Math.abs(convergenceTolerance) && rA > 0)
{
IJ.log("Correlation below tolerance ... quitting");
break;
}
iteration++;
}
if (iteration > maxIterations)
{
IJ.log("Maximum iterations reached");
}
findResultThreshold();
return true;
}
private boolean findThresholdExhaustive(int[] i1, int[] i2, int lowerThreshold, int upperThreshold,
int currentThreshold)
{
int ch1, ch2;
// Create the results and add the global correlation - this will be the fallback result
results = new ArrayList<ThresholdResult>(maxIterations);
results.add(new ThresholdResult(0, 0, rTotal, Double.NaN));
results.add(new ThresholdResult(ch1Max, ch2Max, Double.NaN, rTotal));
// Enumerate over the threshold range
int interval = (int) Math.ceil((1.0 * (upperThreshold - lowerThreshold)) / maxIterations);
int iteration = 1;
for (currentThreshold = lowerThreshold; currentThreshold <= upperThreshold; currentThreshold += interval)
{
if (IJ.escapePressed())
{
IJ.beep();
return correlationResult(false, "IJ plugin cancelled");
}
// Set new thresholds
threshold1 = currentThreshold;
threshold2 = (int) Math.round(((double) threshold1 * m) + b);
// A = both channels above the threshold
// B = either channel below the threshold
long ch1SumA = 0, ch2SumA = 0;
int nZeroA = 0, nA = 0;
long ch1SumB = 0, ch2SumB = 0;
int nZeroB = 0, nB = 0;
// Get means
for (int i = i1.length; i-- > 0;)
{
ch1 = i1[i];
ch2 = i2[i];
if ((ch1 < (threshold1)) || (ch2 < (threshold2)))
{
if (ch1 == 0 && ch2 == 0)
{
nZeroB++;
}
else
{
ch1SumB += ch1;
ch2SumB += ch2;
}
nB++;
}
else
{
if (ch1 == 0 && ch2 == 0)
{
nZeroA++;
}
else
{
ch1SumA += ch1;
ch2SumA += ch2;
}
nA++;
}
}
if (!includeNullPixels)
{
nA -= nZeroA;
nB -= nZeroB;
}
double ch1MeanA = ch1SumA / (double) nA;
double ch2MeanA = ch2SumA / (double) nA;
double ch1MeanB = ch1SumB / (double) nB;
double ch2MeanB = ch2SumB / (double) nB;
// Calculate correlation
double ch1mch1MeanSqSumA = 0;
double ch2mch2MeanSqSumA = 0;
double ch1mch2MeanSqSumA = 0;
double ch1mch1MeanSqSumB = 0;
double ch2mch2MeanSqSumB = 0;
double ch1mch2MeanSqSumB = 0;
for (int i = i1.length; i-- > 0;)
{
ch1 = i1[i];
ch2 = i2[i];
if ((ch1 < (threshold1)) || (ch2 < (threshold2)))
{
ch1mch1MeanSqSumB += (ch1 - ch1MeanB) * (ch1 - ch1MeanB);
ch2mch2MeanSqSumB += (ch2 - ch2MeanB) * (ch2 - ch2MeanB);
ch1mch2MeanSqSumB += (ch1 - ch1MeanB) * (ch2 - ch2MeanB);
}
else
{
ch1mch1MeanSqSumA += (ch1 - ch1MeanA) * (ch1 - ch1MeanA);
ch2mch2MeanSqSumA += (ch2 - ch2MeanA) * (ch2 - ch2MeanA);
ch1mch2MeanSqSumA += (ch1 - ch1MeanA) * (ch2 - ch2MeanA);
}
}
double rA = ch1mch2MeanSqSumA / Math.sqrt(ch1mch1MeanSqSumA * ch2mch2MeanSqSumA);
double rB = ch1mch2MeanSqSumB / Math.sqrt(ch1mch1MeanSqSumB * ch2mch2MeanSqSumB);
// Only add a results if there is a correlation below the threshold
if (!(Double.isNaN(rB) || nB == 0))
{
results.add(new ThresholdResult(threshold1, threshold2, rA, rB));
}
IJ.log(iteration + ", c1(threshold)=" + threshold1 + ", c2(threshold)=" + threshold2 + " : r=" +
IJ.d2s(rA, 4) + ", r2=" + IJ.d2s(rB, 4));
if (Double.isNaN(rA))
break;
iteration++;
}
findResultThreshold();
return true;
}
/**
* Find the first threshold with a correlation within the limit of the target correlation.
*/
private void findResultThreshold()
{
// Results are sorted from high to low threshold.
Collections.sort(results);
rBelowThreshold = 2;
for (ThresholdResult result : results)
{
// Look for results where the correlation above the threshold is positive
// and the correlation below the threshold (RltT) is closer to the target R-threshold.
if (result.r > rThreshold &&
//result.r2 < rThreshold &&
//Math.abs(result.r2 - rThreshold) < Math.abs(convergenceTolerance) &&
Math.abs(result.r2 - rThreshold) < Math.abs(rBelowThreshold - rThreshold))
{
rAboveThreshold = result.r;
rBelowThreshold = result.r2;
threshold1 = result.threshold1;
threshold2 = result.threshold2;
// If the RltT is within the convergence limit then this is the final result.
//if (Math.abs(result.r2 - rThreshold) < Math.abs(convergenceTolerance))
//{
// break;
//}
}
}
// Set the values in the case that no correlations satisfy the criteria.
if (rBelowThreshold == 2)
{
ThresholdResult result = results.get(results.size() - 1);
rAboveThreshold = result.r;
rBelowThreshold = result.r2;
threshold1 = result.threshold1;
threshold2 = result.threshold2;
}
}
private boolean isRepeatThreshold(ArrayList<ThresholdResult> results, int newThreshold)
{
for (ThresholdResult result : results)
{
if (result.threshold1 == newThreshold)
return true;
}
return false;
}
private boolean correlationResult(boolean result, String message)
{
IJ.log(message);
IJ.log("-=-=-=-=-=-=-");
correlated = result;
return result;
}
private int decreaseThreshold(int current, int min)
{
return current - (int) Math.ceil((current - min) / 2.0);
}
private int increaseThreshold(int current, int max)
{
return current + (int) Math.ceil((max - current) / 2.0);
}
public void setMaxIterations(int maxIterations)
{
this.maxIterations = maxIterations;
}
public int getMaxIterations()
{
return maxIterations;
}
public void setIncludeNullPixels(boolean includeNullPixels)
{
this.includeNullPixels = includeNullPixels;
}
public boolean isIncludeNullPixels()
{
return includeNullPixels;
}
public void setIncludeSaturatedPixels(boolean includeSaturatedPixels)
{
this.includeSaturatedPixels = includeSaturatedPixels;
}
public boolean isIncludeSaturatedPixels()
{
return includeSaturatedPixels;
}
public void setExhaustiveSearch(boolean exhaustiveSearch)
{
this.exhaustiveSearch = exhaustiveSearch;
}
public boolean isExhaustiveSearch()
{
return exhaustiveSearch;
}
public void setResults(ArrayList<ThresholdResult> results)
{
this.results = results;
}
public ArrayList<ThresholdResult> getResults()
{
return results;
}
public boolean isCorrelated()
{
return correlated;
}
public int getThreshold1()
{
return (correlated) ? threshold1 : 0;
}
public int getThreshold2()
{
return (correlated) ? threshold2 : 0;
}
public double getM()
{
return m;
}
public double getB()
{
return b;
}
public double getRTotal()
{
return rTotal;
}
public double getRBelowThreshold()
{
return (correlated) ? rBelowThreshold : Double.NaN;
}
public double getRAboveThreshold()
{
return (correlated) ? rAboveThreshold : Double.NaN;
}
/**
* Set the limit for the correlation below the threshold. The search will stop when the correlation for the pixels
* below threshold is with the convergence tolerance distance of this R, i.e. is R = R-threshold +/- tolerance.
*
* @param r
*/
public void setRThreshold(double r)
{
this.rThreshold = r;
}
public double getRThreshold()
{
return rThreshold;
}
/**
* Set the tolerance for convergence. The search will stop when the correlation for the pixels below threshold is
* with the convergence tolerance distance of this R, i.e. is R = R-threshold +/- tolerance.
*
* @param tolerance
*/
public void setConvergenceTolerance(double tolerance)
{
this.convergenceTolerance = tolerance;
}
public double getConvergenceTolerance()
{
return convergenceTolerance;
}
public int getCh1Max()
{
return ch1Max;
}
public int getCh2Max()
{
return ch2Max;
}
public int getCh1Min()
{
return ch1Min;
}
public int getCh2Min()
{
return ch2Min;
}
/**
* Set the tolerance for performing a search. The search will not start if the total correlation for the pixels
* below this threshold.
*
* @param searchTolerance
*/
public void setSearchTolerance(double searchTolerance)
{
this.searchTolerance = searchTolerance;
}
public double getSearchTolerance()
{
return searchTolerance;
}
/**
* Store the results of the correlation for a specified threshold
*/
public class ThresholdResult implements Comparable<ThresholdResult>
{
public int threshold1;
public int threshold2;
public double r;
public double r2;
public ThresholdResult(int threshold1, int threshold2, double r, double r2)
{
this.threshold1 = threshold1;
this.threshold2 = threshold2;
this.r = r;
this.r2 = r2;
}
public int compareTo(ThresholdResult otherResult) throws ClassCastException
{
return otherResult.threshold1 - this.threshold1;
}
}
}