QuimPArrayUtils.java
package com.github.celldynamics.quimp.utils;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.nio.BufferOverflowException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.stream.IntStream;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.math3.linear.Array2DRowRealMatrix;
import org.apache.commons.math3.linear.RealMatrix;
import org.apache.commons.math3.stat.StatUtils;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
/**
* Deliver simple methods operating on arrays.
*
* @author p.baniukiewicz
*
*/
public class QuimPArrayUtils {
/**
* Convert 1d float array to double.
*
* @param input array to convert
* @return Input array converted to double
* @deprecated Use ij.Tools.toDouble
*/
public static double[] float2double(float[] input) {
final double[] result = new double[input.length];
IntStream.range(0, input.length).forEach(index -> result[index] = input[index]);
return result;
}
/**
* Convert 1d double array to float.
*
* @param input array to convert
* @return Input array converted to float
*/
public static float[] double2float(double[] input) {
final float[] result = new float[input.length];
IntStream.range(0, input.length).forEach(index -> result[index] = (float) input[index]);
return result;
}
/**
* Convert 1d double array to Double.
*
* @param input array to convert
* @return Input array converted to Double
*/
public static Double[] object2double(Number[] input) {
final Double[] result = new Double[input.length];
IntStream.range(0, input.length).forEach(index -> result[index] = input[index].doubleValue());
return result;
}
/**
* Convert 2D float array to double.
*
* @param input Array to convert
* @return converted one
*/
public static double[][] float2ddouble(float[][] input) {
if (input == null) {
return null;
}
int rows = input.length;
double[][] out = new double[rows][];
IntStream.range(0, input.length).forEach(index -> out[index] = float2double(input[index]));
return out;
}
/**
* Convert 2D double array to float.
*
* @param input Array to convert
* @return converted one
*/
public static float[][] double2dfloat(double[][] input) {
if (input == null) {
return null;
}
int rows = input.length;
float[][] out = new float[rows][];
IntStream.range(0, input.length).forEach(index -> out[index] = double2float(input[index]));
return out;
}
/**
* Calculate mean of map for every row.
*
* @param map map
* @return Mean of map for every row as list.
*/
public static double[] getMeanR(double[][] map) {
double[] ret = new double[map.length];
IntStream.range(0, map.length)
.forEach(index -> ret[index] = Arrays.stream(map[index]).sum() / map[index].length);
return ret;
}
/**
* Calculate mean of map for every column. Assumes regular array.
*
* @param map map
* @return Mean of map for every row as list.
*/
public static double[] getMeanC(double[][] map) {
double[] ret = new double[map[0].length];
for (int r = 0; r < map[0].length; r++) { // for every frame
double mean = 0;
for (int f = 0; f < map.length; f++) {
mean += map[f][r];
}
ret[r] = mean / map.length;
}
return ret;
}
/**
* Calculate variance of map for every row.
*
* @param map map
* @return Variance of map for every row as list.
*/
public static double[] getVarR(double[][] map) {
double[] ret = new double[map.length];
double[] means = getMeanR(map);
for (int f = 0; f < map.length; f++) { // for every frame
double var = 0;
for (int r = 0; r < map[f].length; r++) {
var += Math.pow(means[f] - map[f][r], 2.0);
}
ret[f] = var / map[f].length;
}
return ret;
}
/**
* Remove duplicated elements from input list.
*
* @param in in
* @return New list without duplicates. Ordering may be different than in input list.
*/
public static <T> List<T> removeDuplicates(List<T> in) {
Set<T> retNoDpl = new HashSet<>(); // no duplicates allowed
retNoDpl.addAll(in); // add if not present already
ArrayList<T> retP2i = new ArrayList<>();
retP2i.addAll(retNoDpl); // convert back to list
return retP2i;
}
/**
* reverse the given array in place.
*
* @param input input
*/
public static void reverseIntArray(int[] input) {
// handling null, empty and one element array
if (input == null || input.length <= 1) {
return;
}
for (int i = 0; i < input.length / 2; i++) {
int temp = input[i]; // swap numbers
input[i] = input[input.length - 1 - i];
input[input.length - 1 - i] = temp;
}
}
/**
* Convert integer array to short.
*
* @param input input
* @return Input array converted to short
*/
public static short[] int2short(int[] input) {
short[] ret = new short[input.length];
for (int i = 0; i < input.length; i++) {
if (input[i] > Short.MAX_VALUE) {
throw new BufferOverflowException();
}
ret[i] = (short) input[i];
}
return ret;
}
/**
* Create 2D array of doubles.
*
* @param rows Number of rows
* @param cols Number of columns
* @return rows x cols array
*/
public static double[][] initDouble2dArray(int rows, int cols) {
double[][] ret;
ret = new double[rows][];
for (int r = 0; r < rows; r++) {
ret[r] = new double[cols];
}
return ret;
}
/**
* Create 2D array of integers.
*
* @param rows Number of rows
* @param cols Number of columns
* @return rows x cols array
*/
public static int[][] initInteger2dArray(int rows, int cols) {
int[][] ret;
ret = new int[rows][];
for (int r = 0; r < rows; r++) {
ret[r] = new int[cols];
}
return ret;
}
/**
* Make deep copy of 2D array.
*
* <p>If destination matrix is already initialized it must have correct size.
*
* @param source source matrix
* @param dest destination matrix, if <tt>dest</tt> is <tt>null</tt> the matrix is initialized
* in place.
* @return copy of source matrix
*/
public static double[][] copy2darray(double[][] source, double[][] dest) {
double[][] ret;
int rows = source.length;
int cols = source[0].length;
if (dest == null) {
ret = initDouble2dArray(rows, cols);
} else {
ret = dest;
}
for (int r = 0; r < source.length; r++) {
System.arraycopy(source[r], 0, ret[r], 0, source[r].length);
}
return ret;
}
/**
* Array to file.
*
* <p>Can be imported e.g. in Matlab {@code image=importdata('/tmp/image.txt');}
*
* @param a the a
* @param delim the delim
* @param outFile the out file
* @throws IOException Signals that an I/O exception has occurred.
* @see #file2Array(String, File)
*/
public static void arrayToFile(double[][] a, String delim, File outFile) throws IOException {
PrintWriter pw = new PrintWriter(new FileWriter(outFile), true); // auto flush
for (int i = 0; i < a.length; i++) {
if (i != 0) {
pw.write("\n");
}
pw.write(a[i][0] + "");
for (int j = 1; j < a[0].length; j++) {
pw.write(delim + a[i][j]);
}
}
pw.close();
}
/**
* Save RelaMatrix 2D to file.
*
* <p>Use the code in Matlab:
* {@code data = importdata('/tmp/testRealMatrix2D2File.txt')}
*
* @param matrix matrix to save
* @param outFile output file
* @throws IOException on file error
*/
public static void realMatrix2D2File(RealMatrix matrix, String outFile) throws IOException {
if (matrix instanceof Array2DRowRealMatrix) {
double[][] ref = ((Array2DRowRealMatrix) matrix).getDataRef();
arrayToFile(ref, ",", new File(outFile));
} else {
throw new IllegalArgumentException("Input matrix should be instance of Array2DRowRealMatrix");
}
}
/**
* Load map file produced by arrayToFile.
*
* <p>Array must have equal number of columns in every row.
*
* @param delim Delimiter, should be the same used in arrayToFile
* @param inFile inFile
* @return loaded file as 2D array
* @throws IOException on file error
* @see #arrayToFile(double[][], String, File)
*/
public static double[][] file2Array(String delim, File inFile) throws IOException {
LineNumberReader pw = new LineNumberReader(new FileReader(inFile));
int lines = getNumberOfLinesinFile(inFile); // get number of rows
double[][] ret = new double[lines][];
String line = pw.readLine();
while (line != null) {
StringTokenizer tk = new StringTokenizer(line, delim);
ret[pw.getLineNumber() - 1] = new double[tk.countTokens()];
int colno = 0;
while (tk.hasMoreTokens()) {
ret[pw.getLineNumber() - 1][colno++] = Double.valueOf(tk.nextToken());
}
line = pw.readLine();
}
pw.close();
return ret;
}
/**
* Return number of lines in file.
*
* @param file file
* @return Number of lines
* @throws IOException on file error
*/
public static int getNumberOfLinesinFile(File file) throws IOException {
LineNumberReader lnr = new LineNumberReader(new FileReader(file));
lnr.skip(Long.MAX_VALUE);
int lines = lnr.getLineNumber() + 1;
lnr.close();
return lines;
}
/**
* Find num peaks.
*
* @param data the data
* @param peakWidth the peak width
* @return the int
*/
public static int findNumPeaks(double[] data, int peakWidth) {
int[] peaks = new int[data.length];
int flag;
// find local peak points
for (int i = peakWidth; i < data.length - peakWidth; i++) {
flag = 0;
peaks[i] = 0;
for (int j = -peakWidth; j <= peakWidth; j++) {
if (data[i + j] > data[i]) {
flag = 1;
break;
}
}
if (flag == 0) {
peaks[i] = 1;
// System.out.println("peak at " + i);
}
}
// remove consecutive points (i.e. in flat areas)
int realPeaks = 0;
for (int i = 0; i < peaks.length; i++) {
if (peaks[i] == 1) {
realPeaks++;
if (peaks[i + 1] == 1) {
realPeaks--;
}
}
}
return realPeaks;
}
/**
* Array max.
*
* @param a the a
* @return the int
*/
public static int arrayMax(int[] a) {
return Arrays.stream(a).max().getAsInt();
}
/**
* Array max.
*
* @param a the a
* @return the double
*/
public static double arrayMax(double[] a) {
double max = a[0];
if (a.length == 1) {
return max;
}
for (int i = 1; i < a.length; i++) {
if (max < a[i]) {
max = a[i];
}
}
return max;
}
/**
* Array max.
*
* @param a the a
* @return the double
*/
public static double array2dMax(double[][] a) {
double max = arrayMax(a[0]);
if (a.length == 1) {
return max;
}
for (int i = 1; i < a.length; i++) {
double rmax = arrayMax(a[i]);
if (max < rmax) {
max = rmax;
}
}
return max;
}
/**
* Find index of minimal element.
*
* @param a array to search in
* @return index of min(a)
*/
public static int minArrayIndex(double[] a) {
// find the index of the min
double min = a[0];
int imin = 0;
if (a.length == 1) {
return imin;
}
for (int i = 1; i < a.length; i++) {
if (min > a[i]) {
min = a[i];
imin = i;
}
}
return imin;
}
/**
* Find index of minimal element and the element itself.
*
* @param a array to search in
* @return [min,index]
*/
public static double[] minArrayIndexElement(double[] a) {
// find the index of the min
double[] ret = new double[2];
double min = a[0];
int imin = 0;
if (a.length == 1) {
ret[0] = min;
ret[1] = imin;
return ret;
}
for (int i = 1; i < a.length; i++) {
if (min > a[i]) {
min = a[i];
imin = i;
}
}
ret[0] = min;
ret[1] = imin;
return ret;
}
/**
* Array min.
*
* @param a the a
* @return the double
*/
public static double arrayMin(double[] a) {
double min = a[0];
if (a.length == 1) {
return min;
}
for (int i = 1; i < a.length; i++) {
if (min > a[i]) {
min = a[i];
}
}
return min;
}
/**
* Array min.
*
* @param a the a
* @return the double
*/
public static double array2dMin(double[][] a) {
double min = arrayMin(a[0]);
if (a.length == 1) {
return min;
}
for (int i = 1; i < a.length; i++) {
double rmin = arrayMin(a[i]);
if (min > rmin) {
min = rmin;
}
}
return min;
}
/**
* Sum array.
*
* @param a the a
* @return the int
*/
public static int sumArray(int[] a) {
return Arrays.stream(a).sum();
}
/**
* Prints the.
*
* @param a the a
*/
public static void print(double[] a) {
for (int i = 0; i < a.length; i++) {
System.out.print("" + a[i] + "\n");
}
System.out.println("");
}
/**
* Prints the.
*
* @param a the a
*/
public static void print(double[][] a) {
for (int i = 0; i < a.length; i++) {
System.out.print("" + a[i][0] + " " + a[i][1] + "\n");
}
}
/**
* Get size of array across all dimensions.
*
* @param object Array
* @return Size of array <tt>a</tt>
*/
public static int getArraySize(Object object) {
if (!object.getClass().isArray()) {
return 1;
}
int size = 0;
for (int i = 0; i < Array.getLength(object); i++) {
size += getArraySize(Array.get(object, i));
}
return size;
}
/**
* Fill 2D array with given value.
*
* <p>Assumes regular array.
*
* @param array array
* @param d value to fill with
*/
public static <T> void fill2Darray(double[][] array, double d) {
for (double[] row : array) {
Arrays.fill(row, d);
}
}
/**
* Fill 2D array with given value.
*
* <p>Assumes regular array.
*
* @param array array
* @param d value to fill with
*/
public static <T> void fill2Darray(int[][] array, int d) {
for (int[] row : array) {
Arrays.fill(row, d);
}
}
/**
* Find index of minimal element in Collection.
*
* @param a collection to search in
* @return index of minimal element
*/
public static <T extends Comparable<T>> int minListIndex(List<T> a) {
return a.indexOf(Collections.min(a));
}
/**
* Find maximum in 2D RealMatrix.
*
* @param input Matrix to process
* @return maximal value in input
*/
public static double getMax(RealMatrix input) {
double[][] data;
if (input instanceof Array2DRowRealMatrix) {
data = ((Array2DRowRealMatrix) input).getDataRef();
} else {
data = input.getData(); // optimize using visitors because this is copy
}
double[] maxs = new double[input.getRowDimension()];
for (int r = 0; r < input.getRowDimension(); r++) {
maxs[r] = StatUtils.max(data[r]);
}
return StatUtils.max(maxs);
}
/**
* Find minimum in 2D RealMatrix.
*
* @param input Matrix to process
* @return minimal value in input
*/
public static double getMin(RealMatrix input) {
double[][] data;
if (input instanceof Array2DRowRealMatrix) {
data = ((Array2DRowRealMatrix) input).getDataRef(); // only available for non cache-friendly
} else {
data = input.getData(); // TODO Optimise using visitors because this is copy
}
double[] maxs = new double[input.getRowDimension()];
for (int r = 0; r < input.getRowDimension(); r++) {
maxs[r] = StatUtils.min(data[r]);
}
return StatUtils.min(maxs);
}
/**
* Convert ImageProcessor pixel data to array of objects.
*
* @param input ImageProcessor
* @return array of objects converted from primitives
*/
public static Number[] castToNumber(ImageProcessor input) {
Object pixels = input.getPixels();
if (pixels == null) {
return null;
} else if (pixels instanceof byte[]) {
return ArrayUtils.toObject((byte[]) pixels);
} else if (pixels instanceof short[]) {
return ArrayUtils.toObject((short[]) pixels);
} else if (pixels instanceof int[]) {
return ArrayUtils.toObject((int[]) pixels);
} else if (pixels instanceof float[]) {
return ArrayUtils.toObject((float[]) pixels);
} else {
throw new IllegalArgumentException("Unknown bit depth");
}
}
/**
* Create FloatProcessor 2D from RealMatrix.
*
* @param rm input matrix
* @return FloatProcessor
*/
public static FloatProcessor realMatrix2ImageProcessor(RealMatrix rm) {
double[][] rawData = rm.transpose().getData();
return new FloatProcessor(double2dfloat(rawData));
}
/**
* Create RealMatrix 2D from image. Image is converted to Double.
*
* @param ip input image
* @return 2D matrix converted to Double
*/
public static RealMatrix imageProcessor2RealMatrix(ImageProcessor ip) {
if (ip == null) {
return null;
}
RealMatrix out;
float[][] image = ip.getFloatArray();
// no copy (it is done in float2double)
out = new Array2DRowRealMatrix(float2ddouble(image), false);
return out.transpose();
}
}