View Javadoc
1   package com.github.celldynamics.quimp.plugin.protanalysis;
2   
3   import java.awt.Polygon;
4   import java.awt.geom.Point2D;
5   import java.io.PrintWriter;
6   import java.util.Iterator;
7   
8   import org.apache.commons.lang3.tuple.Pair;
9   import org.slf4j.Logger;
10  import org.slf4j.LoggerFactory;
11  
12  import com.github.celldynamics.quimp.CellStats;
13  import com.github.celldynamics.quimp.FrameStatistics;
14  import com.github.celldynamics.quimp.filesystem.IQuimpSerialize;
15  import com.github.celldynamics.quimp.plugin.protanalysis.Track.TrackType;
16  import com.github.celldynamics.quimp.plugin.qanalysis.STmap;
17  import com.github.celldynamics.quimp.utils.QuimPArrayUtils;
18  
19  import ij.measure.ResultsTable;
20  
21  /*
22   * !>
23   * @startuml doc-files/ProtStat_1_UML.png 
24   * title Class dependency and most important methods. IQuimpSerialize <|.. ProtStat
25   * ProtStat *-- "1" CellStatistics
26   * ProtStat *-- "1" ProtrusionStatistics
27   * class ProtStat {
28   * +cellStatistics : CellStatistics
29   * +protStatistics : ProtrusionStatistics
30   * +void writeCell()
31   * +void writeProtrusion()
32   * }
33   * class CellStatistics {
34   * -void writeCellHeader()
35   * -void writeCellRecord()
36   * }
37   * class ProtrusionStatistics {
38   * -void writeProtHeader()
39   * -void writeProtRecord()
40   * }
41   * @enduml
42   * !<
43   */
44  /**
45   * Compute statistics for one cell.
46   * 
47   * <br>
48   * <img src="doc-files/ProtStat_1_UML.png"/><br>
49   * 
50   * @author p.baniukiewicz
51   *
52   */
53  public class ProtStat implements IQuimpSerialize {
54  
55    /**
56     * The Constant LOGGER.
57     */
58    static final Logger LOGGER = LoggerFactory.getLogger(ProtStat.class.getName());
59    /**
60     * Frame window used for computation. Should be uneven.
61     */
62    private final int framewindow = 3;
63  
64    private CellStats cs;
65    private TrackCollection tc;
66    private MaximaFinder mf;
67    private STmap maps;
68    /**
69     * Number of frames.
70     */
71    private int frames;
72    /**
73     * Map resolution.
74     */
75    private int mapRes;
76  
77    /**
78     * Total number of protrusions.
79     */
80    private int protCount;
81  
82    /**
83     * Hold cell shape based statistics.
84     */
85    public CellStatistics cellStatistics;
86    /**
87     * Hold protrusion statistics.
88     */
89    public ProtrusionStatistics protStatistics;
90  
91    /**
92     * Construct the object using various data containers.
93     * 
94     * @param mf MaximaFinder
95     * @param tc TrackCollection
96     * @param cs CellStats
97     * @param maps STmap
98     */
99    public ProtStat(MaximaFinder mf, TrackCollection tc, CellStats cs, STmap maps) {
100     this.mf = mf;
101     this.tc = tc;
102     this.cs = cs;
103     this.maps = maps;
104     frames = maps.getT();
105     mapRes = maps.getRes();
106     protCount = mf.getMaximaNumber();
107 
108     cellStatistics = new CellStatistics();
109     protStatistics = new ProtrusionStatistics();
110 
111   }
112 
113   /**
114    * Collection of methods for compute cell shape based statistics.
115    * 
116    * @author p.baniukiewicz
117    *
118    */
119   class CellStatistics {
120 
121     /**
122      * The displacement.
123      */
124     // parameter vs. frames
125     double[] displacement;
126 
127     /**
128      * The distance.
129      */
130     double[] distance;
131 
132     /**
133      * The area.
134      */
135     double[] area;
136 
137     /**
138      * The circularity.
139      */
140     double[] circularity;
141     /**
142      * Mean of motility.
143      */
144     double[] motMean;
145     /**
146      * Variance of motility.
147      */
148     double[] motVar;
149     /**
150      * Mean of convexity.
151      */
152     double[] conMean;
153     /**
154      * Variance of convexity.
155      */
156     double[] conVar;
157     /**
158      * Number of protrusions in every frame.
159      */
160     double[] protcount;
161 
162     /**
163      * Compute all cell statistics.
164      */
165     public CellStatistics() {
166       getFromCellStats(); // copy some stats from CellStats
167       // calculate basic stats for motility and convexity
168       motMean = QuimPArrayUtils.getMeanR(maps.getMotMap());
169       conMean = QuimPArrayUtils.getMeanR(maps.getConvMap());
170       motVar = QuimPArrayUtils.getVarR(maps.getMotMap());
171       conVar = QuimPArrayUtils.getVarR(maps.getConvMap());
172       protcount = countProtrusions();
173     }
174 
175     /**
176      * Extract already calculated stats from CellStats.
177      * 
178      * <p>Fill internal fields of this class.
179      */
180     private void getFromCellStats() {
181       displacement = new double[frames];
182       distance = new double[frames];
183       area = new double[frames];
184       circularity = new double[frames];
185       int l = 0;
186       for (FrameStatistics frameStat : cs.framestat) {
187         displacement[l] = frameStat.displacement;
188         distance[l] = frameStat.dist;
189         area[l] = frameStat.area;
190         circularity[l] = frameStat.circularity;
191         l++;
192       }
193     }
194 
195     /**
196      * Count protrusions for every frame.
197      * 
198      * @return Number of protrusions found for every frame. TODO use framewindow
199      */
200     private double[] countProtrusions() {
201       Polygon maxima = mf.getMaxima();
202       int[] tmpframes = maxima.xpoints; // frame numbers from maxima
203       double[] hist = new double[frames]; // bin count
204       for (int f = 0; f < tmpframes.length; f++) {
205         hist[tmpframes[f]]++;
206       }
207       return hist;
208     }
209 
210     /**
211      * Add header to common file before next cell.
212      * 
213      * @param bf writer
214      * @param cellno Number of cell.
215      */
216     private void writeCellHeader(PrintWriter bf, int cellno) {
217       //!>
218       String ret = "#Cell:" + cellno;
219       LOGGER.trace(ret);
220       String h = "#Frame," + "Displacement," + "Distance," + "Area," + "Circularity," + "meanMot,"
221               + "varMot," + "meanConv," + "varConv," + "protCount";
222       //!<
223       LOGGER.trace(h);
224       bf.print(ret + '\n');
225       bf.print(h + '\n');
226       bf.flush();
227     }
228 
229     /**
230      * Write one line for given frame for current cell.
231      * 
232      * @param bf writer
233      * @param frameno frame
234      */
235     private void writeCellRecord(PrintWriter bf, int frameno) {
236       String ret = Integer.toString(frameno + 1) + ',';
237       ret = ret.concat(Double.toString(displacement[frameno])) + ',';
238       ret = ret.concat(Double.toString(distance[frameno])) + ',';
239       ret = ret.concat(Double.toString(area[frameno])) + ',';
240       ret = ret.concat(Double.toString(circularity[frameno])) + ',';
241       ret = ret.concat(Double.toString(motMean[frameno])) + ',';
242       ret = ret.concat(Double.toString(motVar[frameno])) + ',';
243       ret = ret.concat(Double.toString(conMean[frameno])) + ',';
244       ret = ret.concat(Double.toString(conVar[frameno])) + ',';
245       ret = ret.concat(Double.toString(protcount[frameno]));
246 
247       LOGGER.trace(ret);
248       bf.print(ret + '\n');
249       bf.flush();
250     }
251 
252     /**
253      * Add cell statistic to given ResultsTable.
254      * 
255      * @param rt table
256      * @see #writeCellRecord(PrintWriter, int)
257      * @see #writeCellHeader(PrintWriter, int)
258      */
259     public void addCellToCellTable(ResultsTable rt) {
260       // Those fields must be related to writeCellHeader and writeCellRecord
261       for (int i = 0; i < displacement.length; i++) {
262         rt.incrementCounter();
263         rt.addValue("frame", i + 1);
264         rt.addValue("displacement", displacement[i]);
265         rt.addValue("distance", distance[i]);
266         rt.addValue("area", area[i]);
267         rt.addValue("circularity", circularity[i]);
268         rt.addValue("motMean", motMean[i]);
269         rt.addValue("motVar", motVar[i]);
270         rt.addValue("conMean", conMean[i]);
271         rt.addValue("conVar", conVar[i]);
272         rt.addValue("protcount", protcount[i]);
273       }
274     }
275 
276   }
277 
278   /**
279    * Collection of methods for compute protrusion statistics.
280    * 
281    * @author p.baniukiewicz
282    *
283    */
284   class ProtrusionStatistics {
285     /**
286      * Indicate position of the tip on cell outline. Not normalised.
287      */
288     int[] tipPositionIndex;
289     /**
290      * Screen coordinate of the tip.
291      */
292     Point2D.Double[] tipCoordinate;
293     /**
294      * Tip frame.
295      */
296     int[] tipFrame;
297     /**
298      * First frame where tip appeared, within given criterion of motility drop.
299      */
300     int[] tipFirstFrame;
301     /**
302      * Last frame where tip appeared, within given criterion of motility drop.
303      */
304     int[] tipLastFrame;
305 
306     /**
307      * Compute protrusion stats.
308      */
309     public ProtrusionStatistics() {
310       getFromTrackStats();
311     }
312 
313     /**
314      * Extract statistic data from {@link TrackCollection}.
315      */
316     private void getFromTrackStats() {
317       if (tc.isInitialPointIncluded() == false) {
318         throw new IllegalArgumentException(
319                 "This method assumes that initial point must be included in Track");
320       }
321       tipPositionIndex = new int[protCount];
322       tipCoordinate = new Point2D.Double[protCount];
323       tipFrame = new int[protCount];
324       tipFirstFrame = new int[protCount];
325       tipLastFrame = new int[protCount];
326       Iterator<Pair<Track, Track>> it = tc.iterator(); // over backward,forward pairs
327       int l = 0;
328       while (it.hasNext()) {
329         Pair<Track, Track> p = it.next();
330         Track t1 = p.getLeft();
331         Track t2 = p.getRight();
332         tipPositionIndex[l] = t1.getOutline(0); // first point is maximum
333         tipCoordinate[l] = t1.getXY(0, maps.getxMap(), maps.getyMap());
334         tipFrame[l] = t1.getFrame(0);
335         if (t1.type == TrackType.BACKWARD && t2.type == TrackType.FORWARD) {
336           tipFirstFrame[l] = t1.getFrame(t1.size() - 1);
337           tipLastFrame[l] = t2.getFrame(t2.size() - 1);
338         } else if (t1.type == TrackType.FORWARD && t2.type == TrackType.BACKWARD) {
339           tipFirstFrame[l] = t2.getFrame(t2.size() - 1);
340           tipLastFrame[l] = t1.getFrame(t1.size() - 1);
341         }
342         l++;
343       }
344     }
345 
346     /**
347      * Add header to common file before next cell.
348      * 
349      * @param bf writer
350      * @param cellno Number of cell.
351      */
352     private void writeProtHeader(PrintWriter bf, int cellno) {
353       //!<
354       String ret = "#Cell:" + cellno;
355       LOGGER.trace(ret);
356       String h = "#Id," + "Position," + "x-xoordinate," + "y-coordinate," + "Frame," + "FirstFrame,"
357               + "LastFrame";
358       /**/
359       LOGGER.trace(h);
360       bf.print(ret + '\n');
361       bf.print(h + '\n');
362       bf.flush();
363     }
364 
365     /**
366      * Write one line for given frame for current cell.
367      * 
368      * @param bf writer
369      * @param id protrusion id
370      */
371     private void writeProtRecord(PrintWriter bf, int id) {
372       String ret = Integer.toString(id + 1) + ',';
373       ret = ret.concat(Integer.toString(tipPositionIndex[id])) + ',';
374       ret = ret.concat(Double.toString(tipCoordinate[id].x)) + ',';
375       ret = ret.concat(Double.toString(tipCoordinate[id].y)) + ',';
376       ret = ret.concat(Integer.toString(tipFrame[id])) + ',';
377       ret = ret.concat(Integer.toString(tipFirstFrame[id])) + ',';
378       ret = ret.concat(Integer.toString(tipLastFrame[id]));
379 
380       LOGGER.trace(ret);
381       bf.print(ret + '\n');
382       bf.flush();
383     }
384 
385   }
386 
387   /**
388    * Add stats for this cell to output.
389    * 
390    * @param bf place to write
391    * @param cellno Cell number
392    */
393   public void writeCell(PrintWriter bf, int cellno) {
394     LOGGER.debug("Writing cell stats at:" + bf.toString());
395     cellStatistics.writeCellHeader(bf, cellno);
396     for (int f = 0; f < frames; f++) {
397       cellStatistics.writeCellRecord(bf, f);
398     }
399   }
400 
401   /**
402    * Write stats for protrusions for this cell.
403    * 
404    * @param bf place to write
405    * @param cellno Cell number.
406    */
407   public void writeProtrusion(PrintWriter bf, int cellno) {
408     LOGGER.debug("Writing prot stats at:" + bf.toString());
409     protStatistics.writeProtHeader(bf, cellno);
410     for (int t = 0; t < tc.getBf().size(); t++) {
411       protStatistics.writeProtRecord(bf, t);
412     }
413   }
414 
415   /*
416    * (non-Javadoc)
417    * 
418    * @see com.github.celldynamics.quimp.filesystem.IQuimpSerialize#beforeSerialize()
419    */
420   @Override
421   public void beforeSerialize() {
422   }
423 
424   /*
425    * (non-Javadoc)
426    * 
427    * @see com.github.celldynamics.quimp.filesystem.IQuimpSerialize#afterSerialize()
428    */
429   @Override
430   public void afterSerialize() throws Exception {
431   }
432 }