View Javadoc
1   package com.github.celldynamics.quimp.geom;
2   
3   import java.awt.Point;
4   import java.util.ArrayList;
5   import java.util.Collections;
6   import java.util.List;
7   import java.util.function.Predicate;
8   
9   import com.github.celldynamics.quimp.utils.QuimPArrayUtils;
10  
11  /**
12   * Compute forward and backward tracking maps from origin and coordinates maps.
13   * 
14   * <p>Origin and coordinates maps are stored in both <i>QCONF</i> and <i>paQP</i> data files. Allow
15   * to
16   * track given outline point forward in backward using these maps.
17   * 
18   * @author p.baniukiewicz
19   *
20   */
21  public class MapTracker {
22  
23    /**
24     * Denote bad index.
25     */
26    public static final int BAD_INDEX = -1;
27    /**
28     * Decides whether include starting point in tracking. By default Matlab procedures do not
29     * include it. Therefore, trackXX(int, int, int) returns first tracked point AFTER initial one.
30     */
31    public boolean includeFirst = false;
32  
33    /**
34     * The forward map.
35     */
36    int[][] forwardMap;
37  
38    /**
39     * The backward map.
40     */
41    int[][] backwardMap;
42  
43    /**
44     * Number of rows in <i>Map</i> - equals to number of frames.
45     */
46    int rowsFrames;
47    /**
48     * Number of columns in <i>Map</i> - equals to number of outline points set by resolution in Q
49     * Analysis.
50     */
51    int colsIndexes;
52  
53    /**
54     * Construct tracking maps.
55     * 
56     * <p>Prepare tracking maps that are not stored by default in
57     * {@link com.github.celldynamics.quimp.filesystem.DataContainer}. This code is based on Matlab
58     * routine buildTrackMaps.m.
59     * 
60     * <p><b>Note</b>
61     * 
62     * <p>All frames are numbered from 0 as well as outline indexes. Nonexisting indexes are marked as
63     * -1.
64     * 
65     * @param originMap originMap stored in
66     *        {@link com.github.celldynamics.quimp.filesystem.DataContainer}
67     * @param coordMap coordMap stored in
68     *        {@link com.github.celldynamics.quimp.filesystem.DataContainer}
69     * @see com.github.celldynamics.quimp.filesystem.DataContainer
70     * @see com.github.celldynamics.quimp.plugin.qanalysis.STmap
71     */
72    public MapTracker(double[][] originMap, double[][] coordMap) {
73  
74      forwardMap = QuimPArrayUtils.initInteger2dArray(originMap.length, originMap[0].length);
75      backwardMap = QuimPArrayUtils.initInteger2dArray(forwardMap.length, forwardMap[0].length);
76  
77      QuimPArrayUtils.fill2Darray(forwardMap, -1);
78      QuimPArrayUtils.fill2Darray(backwardMap, -1);
79  
80      rowsFrames = forwardMap.length;
81      colsIndexes = forwardMap[0].length;
82      double[] minV = new double[3];
83      double[] minI = new double[3];
84      // backward map
85      for (int i = 1; i < rowsFrames; i++) {
86        for (int j = 0; j < colsIndexes; j++) {
87          double p = originMap[i][j];
88          double[] diffA = rowDiff(p, coordMap[i - 1]);
89          double[] diffB = rowDiff(p, rowAdd(-1, coordMap[i - 1]));
90          double[] diffC = rowDiff(p, rowAdd(+1, coordMap[i - 1]));
91  
92          double[] minDiffA = QuimPArrayUtils.minArrayIndexElement(diffA);
93          double[] minDiffB = QuimPArrayUtils.minArrayIndexElement(diffB);
94          double[] minDiffC = QuimPArrayUtils.minArrayIndexElement(diffC);
95  
96          // copy min values to array
97          minV[0] = minDiffA[0];
98          minV[1] = minDiffB[0];
99          minV[2] = minDiffC[0];
100         minI[0] = minDiffA[1];
101         minI[1] = minDiffB[1];
102         minI[2] = minDiffC[1];
103         double[] minMinV = QuimPArrayUtils.minArrayIndexElement(minV);
104         backwardMap[i][j] = (int) minI[(int) minMinV[1]]; // copy index of smallest among A,B,C
105       }
106     }
107 
108     // forward map
109     for (int i = 0; i < rowsFrames - 1; i++) {
110       for (int j = 0; j < colsIndexes; j++) {
111         double p = coordMap[i][j];
112         double[] diffA = rowDiff(p, originMap[i + 1]);
113         double[] diffB = rowDiff(p, rowAdd(-1, originMap[i + 1]));
114         double[] diffC = rowDiff(p, rowAdd(+1, originMap[i + 1]));
115 
116         double[] minDiffA = QuimPArrayUtils.minArrayIndexElement(diffA);
117         double[] minDiffB = QuimPArrayUtils.minArrayIndexElement(diffB);
118         double[] minDiffC = QuimPArrayUtils.minArrayIndexElement(diffC);
119 
120         // copy min values to array
121         minV[0] = minDiffA[0];
122         minV[1] = minDiffB[0];
123         minV[2] = minDiffC[0];
124         minI[0] = minDiffA[1];
125         minI[1] = minDiffB[1];
126         minI[2] = minDiffC[1];
127         double[] minMinV = QuimPArrayUtils.minArrayIndexElement(minV);
128         forwardMap[i][j] = (int) minI[(int) minMinV[1]]; // copy index of smallest among A,B,C
129       }
130     }
131   }
132 
133   /**
134    * Compute <tt>p</tt>-<tt>row</tt>.
135    * 
136    * <p>Based on Matlab routine buildTrackMaps.m
137    * 
138    * @param p single value that row will be subtracted from
139    * @param row row to subtract from p
140    * @return Vector of <tt>p</tt>-<tt>row</tt> as a copy.
141    */
142   private double[] rowDiff(double p, double[] row) {
143     double[] cpy = new double[row.length];
144     for (int i = 0; i < cpy.length; i++) {
145       cpy[i] = Math.abs(p - row[i]);
146     }
147     return cpy;
148   }
149 
150   /**
151    * Add value <tt>val</tt> to vector <tt>row</tt>
152    * 
153    * <p>Based on Matlab routine buildTrackMaps.m
154    * 
155    * @param val Value to add
156    * @param row Input matrix
157    * @return <tt>row</tt>+<tt>val</tt> as a copy.
158    */
159   private double[] rowAdd(double val, double[] row) {
160     double[] cpy = new double[row.length];
161     System.arraycopy(row, 0, cpy, 0, row.length);
162     for (int i = 0; i < cpy.length; i++) {
163       cpy[i] += val;
164     }
165     return cpy;
166   }
167 
168   /**
169    * Get position of <tt>membraneIndex</tt> on frame <tt>currentFrame+1</tt>.
170    * 
171    * @param frame frame number, counted from 0
172    * @param membraneIndex index of point on membrane on frame <tt>currentFrame</tt>
173    * @return corresponding index on next frame. Returns -1 when there is neither next frame nor
174    *         index.
175    */
176   public int getNext(int frame, int membraneIndex) {
177     if (frame >= rowsFrames || membraneIndex >= colsIndexes || frame < 0 || membraneIndex < 0) {
178       return BAD_INDEX;
179     }
180     return forwardMap[frame][membraneIndex]; // membrane index on frame
181   }
182 
183   /**
184    * Get position of <tt>membraneIndex</tt> on frame <tt>currentFrame-1</tt>.
185    * 
186    * @param currentFrame frame number, counted from 0
187    * @param membraneIndex index of point on membrane on frame <tt>currentFrame</tt>
188    * @return corresponding index on previous frame. Returns -1 when there is neither next frame
189    *         nor index.
190    */
191   public int getPrev(int currentFrame, int membraneIndex) {
192     if (currentFrame >= rowsFrames || membraneIndex >= colsIndexes || currentFrame < 0
193             || membraneIndex < 0) {
194       return BAD_INDEX;
195     }
196     return backwardMap[currentFrame][membraneIndex];
197   }
198 
199   /**
200    * Track given point forward.
201    * 
202    * @param currentFrame Starting frame (not included in results - depends on
203    *        <tt>includeFirst</tt> flag)
204    * @param membraneIndex Tracked membrane index
205    * @param timeSpan Number of frames to track
206    * @return Indexes of point <tt>membraneIndex</tt> in frames <tt>currentFrame+1</tt> to
207    *         <tt>currentFrame+timeSpan</tt>
208    * @deprecated Use trackForwardValid(int, int, int) instead
209    */
210   @Deprecated
211   public int[] trackForward(int currentFrame, int membraneIndex, int timeSpan) {
212     if (includeFirst) {
213       timeSpan++;
214     }
215     int[] ret = new int[timeSpan];
216     if (includeFirst) {
217       ret[0] = membraneIndex;
218     } else {
219       ret[0] = getNext(currentFrame, membraneIndex);
220     }
221     for (int t = 1; t < timeSpan; t++) {
222       ret[t] = getNext(currentFrame + t, (int) ret[t - 1]);
223     }
224     return ret;
225   }
226 
227   /**
228    * Track given point forward.
229    * 
230    * @param currentFrame Starting frame (not included in results - depends on
231    *        <tt>includeFirst</tt> flag)
232    * @param membraneIndex Tracked membrane index
233    * @param timeSpan Number of frames to track
234    * @return Indexes of point <tt>membraneIndex</tt> in frames <tt>currentFrame+1</tt> to
235    *         <tt>currentFrame+timeSpan</tt>. Only correct. Do no return negative indexes but may
236    *         return empty array. Keep order [frame,index]
237    */
238   public List<Point> trackForwardValid(int currentFrame, int membraneIndex, int timeSpan) {
239     if (includeFirst) {
240       timeSpan++;
241     }
242     ArrayList<Point> ret = new ArrayList<>();
243     if (includeFirst) {
244       ret.add(new Point(currentFrame, membraneIndex));
245     } else {
246       ret.add(new Point(currentFrame + 1, getNext(currentFrame, membraneIndex)));
247     }
248     for (int t = 1; t < timeSpan; t++) {
249       ret.add(new Point(ret.get(t - 1).x + 1, getNext(currentFrame + t, (int) ret.get(t - 1).y)));
250     }
251 
252     ret.removeIf(new PredicateBadIndex());
253 
254     return ret;
255   }
256 
257   /**
258    * Track given point backward.
259    * 
260    * @param currentFrame Starting frame (not included in results - depends on
261    *        <tt>includeFirst</tt> flag)
262    * @param membraneIndex Tracked membrane index
263    * @param timeSpan Number of frames to track
264    * @return Indexes of point <tt>membraneIndex</tt> in frames <tt>currentFrame-1</tt> to
265    *         <tt>currentFrame-timeSpan</tt>
266    * @deprecated Use trackBackwardValid(int, int, int) instead
267    */
268   @Deprecated
269   public int[] trackBackward(int currentFrame, int membraneIndex, int timeSpan) {
270     if (includeFirst) {
271       timeSpan++;
272     }
273     int[] ret = new int[timeSpan];
274     if (includeFirst) {
275       ret[timeSpan - 1] = membraneIndex;
276     } else {
277       ret[timeSpan - 1] = getPrev(currentFrame, membraneIndex);
278     }
279     for (int t = timeSpan - 2; t >= 0; t--) {
280       ret[t] = getPrev(currentFrame - (timeSpan - t - 1), (int) ret[t + 1]);
281     }
282     return ret;
283   }
284 
285   /**
286    * Track given point backward.
287    * 
288    * @param currentFrame Starting frame (not included in results - depends on
289    *        <tt>includeFirst</tt> flag)
290    * @param membraneIndex Tracked membrane index
291    * @param timeSpan Number of frames to track
292    * @return Indexes of point <tt>membraneIndex</tt> in frames <tt>currentFrame-1</tt> to
293    *         <tt>currentFrame-timeSpan</tt> Only correct. Do no return negative indexes but may
294    *         return empty array. Keep order [frame,index]
295    */
296   public List<Point> trackBackwardValid(int currentFrame, int membraneIndex, int timeSpan) {
297     if (includeFirst) {
298       timeSpan++;
299     }
300     ArrayList<Point> ret = new ArrayList<>();
301     if (includeFirst) {
302       ret.add(new Point(currentFrame, membraneIndex));
303     } else {
304       ret.add(new Point(currentFrame - 1, getPrev(currentFrame, membraneIndex)));
305     }
306     for (int t = 1; t < timeSpan; t++) {
307       ret.add(new Point(ret.get(t - 1).x - 1, getPrev(currentFrame - t, (int) ret.get(t - 1).y)));
308     }
309 
310     Collections.reverse(ret);
311     ret.removeIf(new PredicateBadIndex());
312 
313     return ret;
314   }
315 
316   /**
317    * Helper that generates range of frames for given input parameters.
318    * 
319    * <p>These are frames that {@link #trackForward(int, int, int)} returns indexes for. Input
320    * parameters must be the same as for {@link #trackForward(int, int, int)}.
321    * 
322    * @param currentFrame Starting frame (not included in results)
323    * @param timeSpan timeSpan Number of frames to track
324    * @return Array of frame numbers
325    * @deprecated Use {@link #trackForwardValid(int, int, int)} instead
326    */
327   @Deprecated
328   public int[] getForwardFrames(int currentFrame, int timeSpan) {
329     int f;
330     if (includeFirst) {
331       timeSpan++;
332     }
333     int[] ret = new int[timeSpan];
334     if (includeFirst) {
335       f = currentFrame;
336     } else {
337       f = currentFrame + 1;
338     }
339     int l = 0;
340     do {
341       ret[l++] = f++;
342     } while (l < timeSpan);
343     return ret;
344   }
345 
346   /**
347    * Helper that generates range of frames for given input parameters.
348    * 
349    * <p>These are frames that {@link #trackBackward(int, int, int)} returns indexes for. Input
350    * parameters must be the same as for {@link #trackBackward(int, int, int)}.
351    * 
352    * @param currentFrame Starting frame (not included in results)
353    * @param timeSpan timeSpan Number of frames to track
354    * @return Array of frame numbers
355    * @deprecated Use {@link #trackBackwardValid(int, int, int)} instead
356    */
357   @Deprecated
358   public int[] getBackwardFrames(int currentFrame, int timeSpan) {
359     int f;
360     if (includeFirst) {
361       timeSpan++;
362     }
363     int[] ret = new int[timeSpan];
364     if (includeFirst) {
365       f = currentFrame;
366     } else {
367       f = currentFrame - 1;
368     }
369     int l = timeSpan - 1;
370     do {
371       ret[l--] = f--;
372     } while (l >= 0);
373     return ret;
374   }
375 
376   /**
377    * Predicate class for detection bad indexes in tracking methods.
378    * 
379    * @author p.baniukiewicz
380    *
381    */
382   class PredicateBadIndex implements Predicate<Point> {
383 
384     @Override
385     public boolean test(Point t) {
386       if (t.y == MapTracker.BAD_INDEX) {
387         return true;
388       } else {
389         return false;
390       }
391     }
392   }
393 }