package edu.stanford.rsl.conrad.utils; import ij.ImagePlus; import ij.process.FloatProcessor; import java.text.NumberFormat; import java.util.Arrays; import java.util.HashMap; public abstract class DoubleArrayUtil { private static HashMap<Integer, double[]> arrayBuffer = new HashMap<Integer, double[]>(); private static int max = -1; /** * Stores an array for later visualization at index imageNumber * @param imageNumber the number * @param array the array */ public static void saveForVisualization(int imageNumber, double [] array){ arrayBuffer.put(new Integer(imageNumber), array); if (imageNumber > max) max = imageNumber; } /** * Performs a 1-D convolution of the input array with the kernel array.<BR> * New array will be only of size <br> * <pre> * output.lenght = input.length - (2 * (kernel.length/2)); * </pre> * (Note that integer arithmetic is used here)<br> * @param input the array to be convolved * @param kernel the kernel * @return the output array. */ public static double [] convolve(double [] input, double [] kernel){ int offset = ((kernel.length) / 2); double [] revan = new double [input.length - (2* offset)]; double weightSum = 0; for (int j = 0; j < kernel.length; j++){ weightSum += kernel[j]; } if (weightSum == 0) weightSum = 1; for (int i = offset; i < input.length-offset;i++){ double sum = 0; for (int j = -offset; j <= offset; j++){ sum += kernel[offset+j] * input[i+j]; } sum /= weightSum; revan [i-offset] = sum; } return revan; } /** * Displays the arrays stored with "saveForVisualization" as ImagePlus. * @param title the title of the ImagePlus * @return the reference to the ImagePlus * * @see #saveForVisualization(int imageNumber, double [] array) */ public static ImagePlus visualizeBufferedArrays(String title){ if (max >= 0) { int height = max+1; int width = arrayBuffer.get(new Integer(0)).length; FloatProcessor flo = new FloatProcessor(width, height); for (int j = 0; j <= max; j++){ double [] array = arrayBuffer.get(new Integer(j)); for (int i = 0; i < array.length; i++){ flo.putPixelValue(i, max - j, array[i]); } } return VisualizationUtil.showImageProcessor(flo, title); } else { return null; } } /** * Forces an complex double array to be symmetric. Left / first half of the array is mirrored to the right / second half * @param array the complex array */ public static void forceSymmetryComplexDoubleArray(double [] array){ // Force Symmetry int width = array.length / 2; for (int i = 0; i < (width/2); i++){ array[(width) + (2 * i)] = array[(width)-(2*i)]; array[(width) + (2 * i)+1] = array[(width)-(2*i)+1]; } } /** * tests if any of the values in the given array is NaN * @param array * @return true if the array contains at least one entry with NaN */ public static boolean isNaN(double [] array){ boolean revan = true; for (int i = 0; i < array.length; i++){ if (revan && Double.isNaN(array[i])) revan = false; } return !revan; } /** * Forces a real double array to be symmetric. Left / first half of the array is mirrored to the right / second half * @param array the real array */ public static void forceSymmetryRealDoubleArray(double [] array){ // Force Symmetry int width = array.length; for (int i = 0; i < (width/2); i++){ array[(width/2) + (i)] = array[(width/2)-(i)]; } } /** * returns the closest index in the array to the given value * @param x the value * @param array the array * @return the desired index in the array */ public static int findClosestIndex(double x, double [] array){ double min = Double.MAX_VALUE; int pos = 0; for (int i = 0; i < array.length; i++){ double dist = Math.abs(array[i]-x); if (dist < min){ min = dist; pos = i; if (dist == 0) break; } } return pos; } /** * computes the covariance between two arrays * * @param x the one array * @param y the other array * @return the correlation */ public static double covarianceOfDoubleArrays(double [] x, double [] y){ double meanX = computeMean(x); double meanY = computeMean(y); double covariance = 0; for (int i=0; i< x.length; i++){ covariance += ((x[i] - meanX) * (y[i] - meanY)) / x.length; } return covariance / x.length; } /** * computes the correlation coefficient between two arrays after Pearson * * @param x the one array * @param y the other array * @return the correlation */ public static double correlateDoubleArrays(double [] x, double [] y){ double meanX = computeMean(x); double meanY = computeMean(y); double covariance = 0; double varX = 0, varY = 0; for (int i=0; i< x.length; i++){ varX += Math.pow(x[i] - meanX, 2) / x.length; varY += Math.pow(y[i] - meanY, 2) / y.length; covariance += ((x[i] - meanX) * (y[i] - meanY)) / x.length; } if (varX == 0) varX = CONRAD.SMALL_VALUE; if (varY == 0) varY = CONRAD.SMALL_VALUE; return covariance / (Math.sqrt(varX) * Math.sqrt(varY)); } /** * computes the correlation coefficient between two arrays after Pearson * * @param x the one array * @param y the other array * @return the correlation */ public static double computeSSIMDoubleArrays(double [] x, double [] y){ double meanX = computeMean(x); double meanY = computeMean(y); double covariance = 0; double varX = 0, varY = 0; for (int i=0; i< x.length; i++){ varX += Math.pow(x[i] - meanX, 2) / x.length; varY += Math.pow(y[i] - meanY, 2) / y.length; covariance += ((x[i] - meanX) * (y[i] - meanY)) / x.length; } if (varX == 0) varX = CONRAD.SMALL_VALUE; if (varY == 0) varY = CONRAD.SMALL_VALUE; return (2*covariance *2*meanX*meanY) / ((varX+varY) * (Math.pow(meanY, 2) + Math.pow(meanX, 2))); } /** * computes the concordance correlation coefficient between two arrays * * @param x the one array * @param y the other array * @return the correlation */ public static double concordanceCorrelateDoubleArrays(double [] x, double [] y){ double meanX = computeMean(x); double meanY = computeMean(y); double covariance = 0; double varX = 0, varY = 0; for (int i=0; i< x.length; i++){ varX += Math.pow(x[i] - meanX, 2) / x.length; varY += Math.pow(y[i] - meanY, 2) / y.length; covariance += ((x[i] - meanX) * (y[i] - meanY)) / x.length; } if (varX == 0) varX = CONRAD.SMALL_VALUE; if (varY == 0) varY = CONRAD.SMALL_VALUE; return (2* covariance) / (varX + varY + Math.pow(meanX - meanY, 2)); } /** * computes the mean square error of array x to array y * * @param x the one array * @param y the other array * @return the mean square error */ public static double computeMeanSquareError(double [] x, double [] y){ double sum = 0; for (int i=0; i< x.length; i++){ sum += Math.pow(x[i] - y[i], 2); } return sum / x.length; } /** * computes the root mean square error of array x to array y * * @param x the one array * @param y the other array * @return the root mean square error. */ public static double computeRootMeanSquareError(double [] x, double [] y){ return Math.sqrt(computeMeanSquareError(x, y)); } public static void suppressCenter(double [] weights, int threshold){ for (int i = 1; i < weights.length; i++){ if (!((i < threshold) || ((weights.length - i) < threshold))) { weights[i] = (weights[threshold] + weights[weights.length-threshold]) /2; } } } /** * Removes outliers from the array which differ more than threshold from the last value. * @param weights the weight * @param threshold the threshold */ public static void removeOutliers(double [] weights, double threshold){ for (int i = 1; i < weights.length; i++){ if (Math.abs(weights[i] - weights[i - 1]) > threshold) weights[i] = weights[i - 1] + threshold * Math.signum(- weights[i - 1] + weights[i]); } } /** * computes the mean of the array "values" on the interval [start, end]. * @param values the array * @param start the start index * @param end the end index * @return the mean value */ public static double computeMean (double [] values, int start, int end){ double revan = 0; for(int i = start; i <= end; i++){ revan += values[i]; } revan /= end - start + 1; return revan; } /** * Computes the average increment of the array * @param array the array * @return the average increment */ public static double computeAverageIncrement(double [] array){ double increment = 0; for (int i = 1; i < array.length; i++){ double value = Math.abs(array[i-1] - array[i]); if (value > 180) { value -= 360; value = Math.abs(value); } increment += value; } return increment / (array.length - 1); } /** * Performs mean filtering of the array. * @param weights the array * @param context the context to be used for smoothing (from -context/2 to context/2) * @return the smoothed array */ public static double[] meanFilter(double [] weights, int context){ double meanFiltered [] = new double [weights.length]; double mean = 0; for (int i = 0; i < weights.length; i++) { if (i > context / 2){ mean -= weights[i - (context/2)]; } if (i < (weights.length -1) - (context / 2)){ mean += weights[i + (context /2)]; } if (i < context/2){ meanFiltered[i] = computeMean(weights, 0, i); } else if ((i+ context/2) >= weights.length){ meanFiltered[i] = computeMean(weights, i, weights.length - 1); } else { meanFiltered[i] = computeMean(weights, i - context/2, i + context/2); } } return meanFiltered; } /** * Gaussian smoothing of the elements of the array "weights" * @param weights the array * @param sigma the standard deviation * @return the smoothed array */ public static double[] gaussianFilter(double [] weights, double sigma){ double meanFiltered [] = new double [weights.length]; int center = (int) Math.floor(sigma * 1.5) + 1; double kernel [] = new double [(int) Math.ceil(center*2 +1)]; double kernelSum = 0; for (int j = 0; j < (center*2) + 1; j++){ kernel[j] = Math.exp(-0.5 * Math.pow((center-j) / sigma, 2))/sigma/Math.sqrt(2*Math.PI); kernelSum += kernel[j]; } for (int i =0; i< meanFiltered.length; i++){ double sum = 0; for (int j = -center; j <= center; j++){ // Out of bounds at left side if (i+j < 0) sum += kernel[j+center] * weights[0]; // Out of bounds at right side else if (i+j > weights.length-1) sum += kernel[j+center] * weights[weights.length-1]; // Convolution applied inside the valid part of the signal else sum += kernel[j+center] * weights[i+j]; } meanFiltered[i] = sum / kernelSum; } return meanFiltered; } /** * Computes the standard deviation given an array and its mean value * @param array the array * @param mean the mean value of the array * @return the standard deviation */ public static double computeStddev(double[] array, double mean){ double stddev = 0; for (int i = 0; i < array.length; i++){ stddev += Math.pow(array[i] - mean, 2); } return Math.sqrt(stddev / array.length); } /** * Computes the mean value of a given array * @param array the array * @return the mean value as double */ public static double computeMean(double[] array){ double mean = 0; for (int i = 0; i < array.length; i++){ mean += array[i]; } return mean / array.length; } /** * Computes the median value of a given array * @param array the array * @return the median value as double */ public static double computeMedian(double[] array){ double [] sorted = Arrays.copyOf(array, array.length); Arrays.sort(sorted); return sorted[sorted.length/2]; } /** * Returns the minimal and the maximal value in a given array * @param array the array * @return an array with minimal and maximal value */ public static double [] minAndMaxOfArray(double [] array){ double [] revan = new double [2]; revan[0] = Double.MAX_VALUE; revan[1] = -Double.MAX_VALUE; for (int i = 0; i < array.length; i++){ if (array[i] < revan[0]) { revan[0] = array[i]; } if (array[i] > revan[1]) { revan[1] = array[i]; } } return revan; } /** * Returns the minimal value in a given array * @param array the array * @return the minimal value */ public static double minOfArray(double [] array){ double min = Double.MAX_VALUE; for (int i = 0; i < array.length; i++){ if (array[i] < min) { min = array[i]; } } return min; } /** * Returns the maximal value in a given array * @param array the array * @return the minimal value */ public static double maxOfArray(double [] array){ double max = -Double.MAX_VALUE; for (int i = 0; i < array.length; i++){ if (array[i] > max) { max = array[i]; } } return max; } /** * forces monotony onto the input array * @param array the array * @param rising force rising monotony? */ protected void forceMonotony(double [] array, boolean rising){ double lastValid = array[0]; for (int i=0;i< array.length; i++){ double value = array[i]; if (rising) { if (value < lastValid) { value = lastValid; } else { lastValid = value; } } else { if (value > lastValid) { value = lastValid; } else { lastValid = value; } } array[i] = value; } } /** * Adds one array to the first array * @param sum the first array * @param toAdd the array to add */ public static void add(double[] sum, double[] toAdd) { for (int i =0; i < sum.length; i++){ sum[i] += toAdd[i]; } } /** * Adds a constant to the first array * @param sum the first array * @param toAdd the constant to add */ public static double [] add(double[] sum, double toAdd) { for (int i =0; i < sum.length; i++){ sum[i] += toAdd; } return sum; } /** * Divides all entries of array by divident. * @param array the array * @param divident the number used for division. */ public static double [] divide(double[] array, double divident) { for (int i =0; i < array.length; i++){ array[i] /= divident; } return array; } /** * Multiplies all entries of array by factor. * @param array the array * @param factor the number used for multiplication. */ public static double [] multiply(double[] array, double factor) { for (int i =0; i < array.length; i++){ array[i] *= factor; } return array; } /** * Multiplies all entries of the two arrays element by element.<bR> * Works in place and overwrites array. * @param array the array * @param array2 the other array. */ public static void multiply(double[] array, double[] array2) { for (int i =0; i < array.length; i++){ array[i] *= array2[i]; } } /** * Uses Math.exp() on all elements of the array * Works in place and overwrites array. * @param array the array */ public static void exp(double[] array) { for (int i =0; i < array.length; i++){ array[i] = Math.exp(array[i]); } } /** * Divides all entries of the two arrays element by element.<bR> * Works in place and overwrites array. * @param array the array * @param divident the other array. */ public static double [] divide(double[] array, double[] divident) { for (int i =0; i < array.length; i++){ array[i] /= divident[i]; } return array; } /** * Uses Math.log() on all elements of the array * Works in place and overwrites array. * @param array the array */ public static void log(double[] array) { for (int i =0; i < array.length; i++){ array[i] = Math.log(array[i]); } } /** * Prints the contents of the double array on standard out. * @param array * @param nf the NumberFormat */ public static void print(double[] array, NumberFormat nf) { System.out.print("["); for (int i =0; i < array.length; i++){ System.out.print(" " + nf.format(array[i])); } System.out.println(" ]"); } /** * Prints the array on standard out and denotes the arrays name. * @param name the name * @param array the array * @param nf the number format */ public static void print(String name, double[] array, NumberFormat nf) { System.out.print(name + " = "); print(array, nf); } /** * Prints the array on standard out. Uses NumberFormat.getInstance() for number formatting * @param name the name * @param array the array */ public static void print(String name, double [] array){ print(name, array, NumberFormat.getInstance()); } /** * Prints the array on standard out. Uses NumberFormat.getInstance() for number formatting * @param array the array */ public static void print (double [] array){ print(array, NumberFormat.getInstance()); } /** * calls Math.pow for each element of the array * @param array * @param exp the exponent. * @return reference to the input array */ public static double [] pow(double[] array, double exp) { for (int i =0; i < array.length; i++){ array[i] = Math.pow(array[i], exp); } return array; } public static double[] min(double [] array, double min) { for (int i =0; i < array.length; i++){ array[i] = Math.min(array[i], min); } return array; } /** * Converts the array to a String representation. Calls toString(array, " "). * @param array the array * @return the String representation * @see #toString(double[],String) */ public static String toString(double [] array){ return toString(array, " "); } /** * Converts the array to a String representation. delimiter is used to connect the elements of the array. * @param array the array * @param delimiter the delimiter * @return the String representation */ public static String toString(double[] array, String delimiter) { String revan = array[0] + delimiter; for(int i=1;i<array.length -1;i++){ revan += array[i] + delimiter; } revan += array[array.length-1]; return revan; } } /* * Copyright (C) 2010-2014 Andreas Maier * CONRAD is developed as an Open Source project under the GNU General Public License (GPL). */