View Javadoc
1   package com.github.celldynamics.quimp.utils;
2   
3   import java.io.File;
4   import java.io.FileReader;
5   import java.io.FileWriter;
6   import java.io.IOException;
7   import java.io.LineNumberReader;
8   import java.io.PrintWriter;
9   import java.lang.reflect.Array;
10  import java.nio.BufferOverflowException;
11  import java.util.ArrayList;
12  import java.util.Arrays;
13  import java.util.Collections;
14  import java.util.HashSet;
15  import java.util.List;
16  import java.util.Set;
17  import java.util.StringTokenizer;
18  import java.util.stream.IntStream;
19  
20  import org.apache.commons.lang3.ArrayUtils;
21  import org.apache.commons.math3.linear.Array2DRowRealMatrix;
22  import org.apache.commons.math3.linear.RealMatrix;
23  import org.apache.commons.math3.stat.StatUtils;
24  
25  import ij.process.FloatProcessor;
26  import ij.process.ImageProcessor;
27  
28  /**
29   * Deliver simple methods operating on arrays.
30   * 
31   * @author p.baniukiewicz
32   *
33   */
34  public class QuimPArrayUtils {
35  
36    /**
37     * Convert 1d float array to double.
38     * 
39     * @param input array to convert
40     * @return Input array converted to double
41     * @deprecated Use ij.Tools.toDouble
42     */
43    public static double[] float2double(float[] input) {
44      final double[] result = new double[input.length];
45      IntStream.range(0, input.length).forEach(index -> result[index] = input[index]);
46      return result;
47    }
48  
49    /**
50     * Convert 1d double array to float.
51     * 
52     * @param input array to convert
53     * @return Input array converted to float
54     */
55    public static float[] double2float(double[] input) {
56      final float[] result = new float[input.length];
57      IntStream.range(0, input.length).forEach(index -> result[index] = (float) input[index]);
58      return result;
59    }
60  
61    /**
62     * Convert 1d double array to Double.
63     * 
64     * @param input array to convert
65     * @return Input array converted to Double
66     */
67    public static Double[] object2double(Number[] input) {
68      final Double[] result = new Double[input.length];
69      IntStream.range(0, input.length).forEach(index -> result[index] = input[index].doubleValue());
70      return result;
71    }
72  
73    /**
74     * Convert 2D float array to double.
75     * 
76     * @param input Array to convert
77     * @return converted one
78     */
79    public static double[][] float2ddouble(float[][] input) {
80      if (input == null) {
81        return null;
82      }
83      int rows = input.length;
84      double[][] out = new double[rows][];
85      IntStream.range(0, input.length).forEach(index -> out[index] = float2double(input[index]));
86      return out;
87    }
88  
89    /**
90     * Convert 2D double array to float.
91     * 
92     * @param input Array to convert
93     * @return converted one
94     */
95    public static float[][] double2dfloat(double[][] input) {
96      if (input == null) {
97        return null;
98      }
99      int rows = input.length;
100     float[][] out = new float[rows][];
101     IntStream.range(0, input.length).forEach(index -> out[index] = double2float(input[index]));
102     return out;
103   }
104 
105   /**
106    * Calculate mean of map for every row.
107    * 
108    * @param map map
109    * @return Mean of map for every row as list.
110    */
111   public static double[] getMeanR(double[][] map) {
112     double[] ret = new double[map.length];
113     IntStream.range(0, map.length)
114             .forEach(index -> ret[index] = Arrays.stream(map[index]).sum() / map[index].length);
115     return ret;
116   }
117 
118   /**
119    * Calculate mean of map for every column. Assumes regular array.
120    * 
121    * @param map map
122    * @return Mean of map for every row as list.
123    */
124   public static double[] getMeanC(double[][] map) {
125     double[] ret = new double[map[0].length];
126     for (int r = 0; r < map[0].length; r++) { // for every frame
127       double mean = 0;
128       for (int f = 0; f < map.length; f++) {
129         mean += map[f][r];
130       }
131       ret[r] = mean / map.length;
132     }
133     return ret;
134   }
135 
136   /**
137    * Calculate variance of map for every row.
138    * 
139    * @param map map
140    * @return Variance of map for every row as list.
141    */
142   public static double[] getVarR(double[][] map) {
143     double[] ret = new double[map.length];
144     double[] means = getMeanR(map);
145     for (int f = 0; f < map.length; f++) { // for every frame
146       double var = 0;
147       for (int r = 0; r < map[f].length; r++) {
148         var += Math.pow(means[f] - map[f][r], 2.0);
149       }
150       ret[f] = var / map[f].length;
151     }
152     return ret;
153   }
154 
155   /**
156    * Remove duplicated elements from input list.
157    * 
158    * @param in in
159    * @return New list without duplicates. Ordering may be different than in input list.
160    */
161   public static <T> List<T> removeDuplicates(List<T> in) {
162     Set<T> retNoDpl = new HashSet<>(); // no duplicates allowed
163     retNoDpl.addAll(in); // add if not present already
164     ArrayList<T> retP2i = new ArrayList<>();
165     retP2i.addAll(retNoDpl); // convert back to list
166     return retP2i;
167   }
168 
169   /**
170    * reverse the given array in place.
171    * 
172    * @param input input
173    */
174   public static void reverseIntArray(int[] input) {
175     // handling null, empty and one element array
176     if (input == null || input.length <= 1) {
177       return;
178     }
179 
180     for (int i = 0; i < input.length / 2; i++) {
181       int temp = input[i]; // swap numbers
182       input[i] = input[input.length - 1 - i];
183       input[input.length - 1 - i] = temp;
184     }
185   }
186 
187   /**
188    * Convert integer array to short.
189    * 
190    * @param input input
191    * @return Input array converted to short
192    */
193   public static short[] int2short(int[] input) {
194     short[] ret = new short[input.length];
195     for (int i = 0; i < input.length; i++) {
196       if (input[i] > Short.MAX_VALUE) {
197         throw new BufferOverflowException();
198       }
199       ret[i] = (short) input[i];
200     }
201     return ret;
202   }
203 
204   /**
205    * Create 2D array of doubles.
206    * 
207    * @param rows Number of rows
208    * @param cols Number of columns
209    * @return rows x cols array
210    */
211   public static double[][] initDouble2dArray(int rows, int cols) {
212     double[][] ret;
213     ret = new double[rows][];
214     for (int r = 0; r < rows; r++) {
215       ret[r] = new double[cols];
216     }
217     return ret;
218   }
219 
220   /**
221    * Create 2D array of integers.
222    * 
223    * @param rows Number of rows
224    * @param cols Number of columns
225    * @return rows x cols array
226    */
227   public static int[][] initInteger2dArray(int rows, int cols) {
228     int[][] ret;
229     ret = new int[rows][];
230     for (int r = 0; r < rows; r++) {
231       ret[r] = new int[cols];
232     }
233     return ret;
234   }
235 
236   /**
237    * Make deep copy of 2D array.
238    * 
239    * <p>If destination matrix is already initialized it must have correct size.
240    * 
241    * @param source source matrix
242    * @param dest destination matrix, if <tt>dest</tt> is <tt>null</tt> the matrix is initialized
243    *        in place.
244    * @return copy of source matrix
245    */
246   public static double[][] copy2darray(double[][] source, double[][] dest) {
247     double[][] ret;
248     int rows = source.length;
249     int cols = source[0].length;
250     if (dest == null) {
251       ret = initDouble2dArray(rows, cols);
252     } else {
253       ret = dest;
254     }
255     for (int r = 0; r < source.length; r++) {
256       System.arraycopy(source[r], 0, ret[r], 0, source[r].length);
257     }
258     return ret;
259   }
260 
261   /**
262    * Array to file.
263    * 
264    * <p>Can be imported e.g. in Matlab {@code image=importdata('/tmp/image.txt');}
265    *
266    * @param a the a
267    * @param delim the delim
268    * @param outFile the out file
269    * @throws IOException Signals that an I/O exception has occurred.
270    * @see #file2Array(String, File)
271    */
272   public static void arrayToFile(double[][] a, String delim, File outFile) throws IOException {
273     PrintWriter pw = new PrintWriter(new FileWriter(outFile), true); // auto flush
274     for (int i = 0; i < a.length; i++) {
275       if (i != 0) {
276         pw.write("\n");
277       }
278       pw.write(a[i][0] + "");
279       for (int j = 1; j < a[0].length; j++) {
280         pw.write(delim + a[i][j]);
281       }
282     }
283     pw.close();
284   }
285 
286   /**
287    * Save RelaMatrix 2D to file.
288    * 
289    * <p>Use the code in Matlab:
290    * {@code data = importdata('/tmp/testRealMatrix2D2File.txt')}
291    * 
292    * @param matrix matrix to save
293    * @param outFile output file
294    * @throws IOException on file error
295    */
296   public static void realMatrix2D2File(RealMatrix matrix, String outFile) throws IOException {
297     if (matrix instanceof Array2DRowRealMatrix) {
298       double[][] ref = ((Array2DRowRealMatrix) matrix).getDataRef();
299       arrayToFile(ref, ",", new File(outFile));
300     } else {
301       throw new IllegalArgumentException("Input matrix should be instance of Array2DRowRealMatrix");
302     }
303   }
304 
305   /**
306    * Load map file produced by arrayToFile.
307    * 
308    * <p>Array must have equal number of columns in every row.
309    * 
310    * @param delim Delimiter, should be the same used in arrayToFile
311    * @param inFile inFile
312    * @return loaded file as 2D array
313    * @throws IOException on file error
314    * @see #arrayToFile(double[][], String, File)
315    */
316   public static double[][] file2Array(String delim, File inFile) throws IOException {
317     LineNumberReader pw = new LineNumberReader(new FileReader(inFile));
318     int lines = getNumberOfLinesinFile(inFile); // get number of rows
319     double[][] ret = new double[lines][];
320     String line = pw.readLine();
321     while (line != null) {
322       StringTokenizer tk = new StringTokenizer(line, delim);
323       ret[pw.getLineNumber() - 1] = new double[tk.countTokens()];
324       int colno = 0;
325       while (tk.hasMoreTokens()) {
326         ret[pw.getLineNumber() - 1][colno++] = Double.valueOf(tk.nextToken());
327       }
328       line = pw.readLine();
329     }
330     pw.close();
331     return ret;
332   }
333 
334   /**
335    * Return number of lines in file.
336    * 
337    * @param file file
338    * @return Number of lines
339    * @throws IOException on file error
340    */
341   public static int getNumberOfLinesinFile(File file) throws IOException {
342     LineNumberReader lnr = new LineNumberReader(new FileReader(file));
343     lnr.skip(Long.MAX_VALUE);
344     int lines = lnr.getLineNumber() + 1;
345     lnr.close();
346     return lines;
347   }
348 
349   /**
350    * Find num peaks.
351    *
352    * @param data the data
353    * @param peakWidth the peak width
354    * @return the int
355    */
356   public static int findNumPeaks(double[] data, int peakWidth) {
357 
358     int[] peaks = new int[data.length];
359     int flag;
360 
361     // find local peak points
362     for (int i = peakWidth; i < data.length - peakWidth; i++) {
363       flag = 0;
364       peaks[i] = 0;
365       for (int j = -peakWidth; j <= peakWidth; j++) {
366         if (data[i + j] > data[i]) {
367           flag = 1;
368           break;
369         }
370       }
371       if (flag == 0) {
372         peaks[i] = 1;
373         // System.out.println("peak at " + i);
374       }
375     }
376 
377     // remove consecutive points (i.e. in flat areas)
378     int realPeaks = 0;
379     for (int i = 0; i < peaks.length; i++) {
380       if (peaks[i] == 1) {
381         realPeaks++;
382         if (peaks[i + 1] == 1) {
383           realPeaks--;
384         }
385       }
386     }
387 
388     return realPeaks;
389   }
390 
391   /**
392    * Array max.
393    *
394    * @param a the a
395    * @return the int
396    */
397   public static int arrayMax(int[] a) {
398     return Arrays.stream(a).max().getAsInt();
399   }
400 
401   /**
402    * Array max.
403    *
404    * @param a the a
405    * @return the double
406    */
407   public static double arrayMax(double[] a) {
408     double max = a[0];
409     if (a.length == 1) {
410       return max;
411     }
412 
413     for (int i = 1; i < a.length; i++) {
414       if (max < a[i]) {
415         max = a[i];
416       }
417     }
418     return max;
419   }
420 
421   /**
422    * Array max.
423    *
424    * @param a the a
425    * @return the double
426    */
427   public static double array2dMax(double[][] a) {
428     double max = arrayMax(a[0]);
429     if (a.length == 1) {
430       return max;
431     }
432     for (int i = 1; i < a.length; i++) {
433       double rmax = arrayMax(a[i]);
434       if (max < rmax) {
435         max = rmax;
436       }
437     }
438     return max;
439   }
440 
441   /**
442    * Find index of minimal element.
443    * 
444    * @param a array to search in
445    * @return index of min(a)
446    */
447   public static int minArrayIndex(double[] a) {
448     // find the index of the min
449     double min = a[0];
450     int imin = 0;
451     if (a.length == 1) {
452       return imin;
453     }
454 
455     for (int i = 1; i < a.length; i++) {
456       if (min > a[i]) {
457         min = a[i];
458         imin = i;
459       }
460     }
461     return imin;
462   }
463 
464   /**
465    * Find index of minimal element and the element itself.
466    * 
467    * @param a array to search in
468    * @return [min,index]
469    */
470   public static double[] minArrayIndexElement(double[] a) {
471     // find the index of the min
472     double[] ret = new double[2];
473     double min = a[0];
474     int imin = 0;
475     if (a.length == 1) {
476       ret[0] = min;
477       ret[1] = imin;
478       return ret;
479     }
480 
481     for (int i = 1; i < a.length; i++) {
482       if (min > a[i]) {
483         min = a[i];
484         imin = i;
485       }
486     }
487     ret[0] = min;
488     ret[1] = imin;
489     return ret;
490   }
491 
492   /**
493    * Array min.
494    *
495    * @param a the a
496    * @return the double
497    */
498   public static double arrayMin(double[] a) {
499     double min = a[0];
500     if (a.length == 1) {
501       return min;
502     }
503 
504     for (int i = 1; i < a.length; i++) {
505       if (min > a[i]) {
506         min = a[i];
507       }
508     }
509     return min;
510   }
511 
512   /**
513    * Array min.
514    *
515    * @param a the a
516    * @return the double
517    */
518   public static double array2dMin(double[][] a) {
519     double min = arrayMin(a[0]);
520     if (a.length == 1) {
521       return min;
522     }
523     for (int i = 1; i < a.length; i++) {
524       double rmin = arrayMin(a[i]);
525       if (min > rmin) {
526         min = rmin;
527       }
528     }
529     return min;
530   }
531 
532   /**
533    * Sum array.
534    *
535    * @param a the a
536    * @return the int
537    */
538   public static int sumArray(int[] a) {
539     return Arrays.stream(a).sum();
540   }
541 
542   /**
543    * Prints the.
544    *
545    * @param a the a
546    */
547   public static void print(double[] a) {
548 
549     for (int i = 0; i < a.length; i++) {
550       System.out.print("" + a[i] + "\n");
551     }
552     System.out.println("");
553   }
554 
555   /**
556    * Prints the.
557    *
558    * @param a the a
559    */
560   public static void print(double[][] a) {
561 
562     for (int i = 0; i < a.length; i++) {
563       System.out.print("" + a[i][0] + " " + a[i][1] + "\n");
564     }
565   }
566 
567   /**
568    * Get size of array across all dimensions.
569    * 
570    * @param object Array
571    * @return Size of array <tt>a</tt>
572    */
573   public static int getArraySize(Object object) {
574     if (!object.getClass().isArray()) {
575       return 1;
576     }
577     int size = 0;
578     for (int i = 0; i < Array.getLength(object); i++) {
579       size += getArraySize(Array.get(object, i));
580     }
581     return size;
582   }
583 
584   /**
585    * Fill 2D array with given value.
586    * 
587    * <p>Assumes regular array.
588    * 
589    * @param array array
590    * @param d value to fill with
591    */
592   public static <T> void fill2Darray(double[][] array, double d) {
593     for (double[] row : array) {
594       Arrays.fill(row, d);
595     }
596   }
597 
598   /**
599    * Fill 2D array with given value.
600    * 
601    * <p>Assumes regular array.
602    * 
603    * @param array array
604    * @param d value to fill with
605    */
606   public static <T> void fill2Darray(int[][] array, int d) {
607     for (int[] row : array) {
608       Arrays.fill(row, d);
609     }
610   }
611 
612   /**
613    * Find index of minimal element in Collection.
614    * 
615    * @param a collection to search in
616    * @return index of minimal element
617    */
618   public static <T extends Comparable<T>> int minListIndex(List<T> a) {
619     return a.indexOf(Collections.min(a));
620   }
621 
622   /**
623    * Find maximum in 2D RealMatrix.
624    * 
625    * @param input Matrix to process
626    * @return maximal value in input
627    */
628   public static double getMax(RealMatrix input) {
629     double[][] data;
630     if (input instanceof Array2DRowRealMatrix) {
631       data = ((Array2DRowRealMatrix) input).getDataRef();
632     } else {
633       data = input.getData(); // optimize using visitors because this is copy
634     }
635     double[] maxs = new double[input.getRowDimension()];
636     for (int r = 0; r < input.getRowDimension(); r++) {
637       maxs[r] = StatUtils.max(data[r]);
638     }
639     return StatUtils.max(maxs);
640   }
641 
642   /**
643    * Find minimum in 2D RealMatrix.
644    * 
645    * @param input Matrix to process
646    * @return minimal value in input
647    */
648   public static double getMin(RealMatrix input) {
649     double[][] data;
650     if (input instanceof Array2DRowRealMatrix) {
651       data = ((Array2DRowRealMatrix) input).getDataRef(); // only available for non cache-friendly
652     } else {
653       data = input.getData(); // TODO Optimise using visitors because this is copy
654     }
655     double[] maxs = new double[input.getRowDimension()];
656     for (int r = 0; r < input.getRowDimension(); r++) {
657       maxs[r] = StatUtils.min(data[r]);
658     }
659     return StatUtils.min(maxs);
660   }
661 
662   /**
663    * Convert ImageProcessor pixel data to array of objects.
664    * 
665    * @param input ImageProcessor
666    * @return array of objects converted from primitives
667    */
668   public static Number[] castToNumber(ImageProcessor input) {
669     Object pixels = input.getPixels();
670     if (pixels == null) {
671       return null;
672     } else if (pixels instanceof byte[]) {
673       return ArrayUtils.toObject((byte[]) pixels);
674     } else if (pixels instanceof short[]) {
675       return ArrayUtils.toObject((short[]) pixels);
676     } else if (pixels instanceof int[]) {
677       return ArrayUtils.toObject((int[]) pixels);
678     } else if (pixels instanceof float[]) {
679       return ArrayUtils.toObject((float[]) pixels);
680     } else {
681       throw new IllegalArgumentException("Unknown bit depth");
682     }
683   }
684 
685   /**
686    * Create FloatProcessor 2D from RealMatrix.
687    * 
688    * @param rm input matrix
689    * @return FloatProcessor
690    */
691   public static FloatProcessor realMatrix2ImageProcessor(RealMatrix rm) {
692     double[][] rawData = rm.transpose().getData();
693     return new FloatProcessor(double2dfloat(rawData));
694   }
695 
696   /**
697    * Create RealMatrix 2D from image. Image is converted to Double.
698    * 
699    * @param ip input image
700    * @return 2D matrix converted to Double
701    */
702   public static RealMatrix imageProcessor2RealMatrix(ImageProcessor ip) {
703     if (ip == null) {
704       return null;
705     }
706     RealMatrix out;
707     float[][] image = ip.getFloatArray();
708     // no copy (it is done in float2double)
709     out = new Array2DRowRealMatrix(float2ddouble(image), false);
710     return out.transpose();
711   }
712 }