package edu.stanford.rsl.conrad.utils; import ij.ImagePlus; import ij.process.FloatProcessor; import java.text.NumberFormat; import java.util.HashMap; public abstract class FloatArrayUtil { private static HashMap<Integer, float[]> arrayBuffer = new HashMap<Integer, float[]>(); 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, float [] 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 float [] convolve(float [] input, float [] kernel){ int offset = ((kernel.length) / 2); float [] revan = new float [input.length - (2* offset)]; float 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++){ float 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, float [] 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++){ float [] 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 float 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(float [] 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(float [] 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 float 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 forceSymmetryRealArray(float [] 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(float x, float [] array){ float min = (float) Double.MAX_VALUE; int pos = 0; for (int i = 0; i < array.length; i++){ float 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 float covarianceOfArrays(float [] x, float [] y){ float meanX = computeMean(x); float meanY = computeMean(y); float 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 float correlateArrays(float [] x, float [] y){ float meanX = computeMean(x); float meanY = computeMean(y); float covariance = 0; float 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 = (float) CONRAD.SMALL_VALUE; if (varY == 0) varY = (float) CONRAD.SMALL_VALUE; return (float) (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 float computeSSIMArrays(float [] x, float [] y){ float meanX = computeMean(x); float meanY = computeMean(y); float covariance = 0; float 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 = (float) CONRAD.SMALL_VALUE; if (varY == 0) varY = (float) CONRAD.SMALL_VALUE; return (float) ((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 float concordanceCorrelateArrays(float [] x, float [] y){ float meanX = computeMean(x); float meanY = computeMean(y); float covariance = 0; float 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 = (float) CONRAD.SMALL_VALUE; if (varY == 0) varY = (float) CONRAD.SMALL_VALUE; return (float) ((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 float computeMeanSquareError(float [] x, float [] y){ float 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 float computeRootMeanSquareError(float [] x, float [] y){ return (float) Math.sqrt(computeMeanSquareError(x, y)); } public static void suppressCenter(float [] 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(float [] weights, float 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 float computeMean (float [] values, int start, int end){ float 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 float computeAverageIncrement(float [] array){ float increment = 0; for (int i = 1; i < array.length; i++){ float 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 float[] meanFilter(float [] weights, int context){ float meanFiltered [] = new float [weights.length]; float 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 float[] gaussianFilter(float [] weights, float sigma){ float meanFiltered [] = new float [weights.length]; int center = (int) Math.floor(sigma * 1.5) + 1; float kernel [] = new float [(int) Math.ceil(center*2 +1)]; float kernelSum = 0; for (int j = 0; j < (center*2) + 1; j++){ kernel[j] = (float) (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++){ float 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 float computeStddev(float[] array, float mean){ float stddev = 0; for (int i = 0; i < array.length; i++){ stddev += Math.pow(array[i] - mean, 2); } return (float) Math.sqrt(stddev / array.length); } public static float computeStddev(float[] array, float mean, int start, int end){ float stddev = 0; for (int i = start; i < end; i++){ stddev += Math.pow(array[i] - mean, 2); } return (float) Math.sqrt(stddev / (end-start)); } /** * Computes the mean value of a given array * @param array the array * @return the mean value as float */ public static float computeMean(float[] array){ float mean = 0; for (int i = 0; i < array.length; i++){ mean += array[i]; } return mean / array.length; } /** * 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 float [] minAndMaxOfArray(float [] array){ float [] revan = new float [2]; revan[0] = Float.MAX_VALUE; revan[1] = -Float.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 float minOfArray(float [] array){ float min = Float.MAX_VALUE; for (int i = 0; i < array.length; i++){ if (array[i] < min) { min = array[i]; } } return min; } /** * forces monotony onto the input array * @param array the array * @param rising force rising monotony? */ protected void forceMonotony(float [] array, boolean rising){ float lastValid = array[0]; for (int i=0;i< array.length; i++){ float 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(float[] sum, float[] 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 float [] add(float[] sum, float 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 float [] divide(float[] array, float 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 float [] multiply(float[] array, float 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(float[] array, float[] 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(float[] array) { for (int i =0; i < array.length; i++){ array[i] = (float) 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 float [] divide(float[] array, float[] 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(float[] array) { for (int i =0; i < array.length; i++){ array[i] = (float) Math.log(array[i]); } } /** * Prints the contents of the float array on standard out. * @param array * @param nf the NumberFormat */ public static void print(float[] 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, float[] 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, float [] 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 (float [] 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 float [] pow(float[] array, float exp) { for (int i =0; i < array.length; i++){ array[i] = (float) Math.pow((double)array[i], exp); } return array; } public static float[] min(float [] array, float 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(float[],String) */ public static String toString(float [] 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(float[] 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). */