View Javadoc
1   package com.github.celldynamics.quimp;
2   
3   import java.io.BufferedReader;
4   import java.io.File;
5   import java.io.FileReader;
6   import java.io.FileWriter;
7   import java.io.IOException;
8   import java.io.PrintWriter;
9   
10  import com.github.celldynamics.quimp.geom.ExtendedVector2d;
11  import com.github.celldynamics.quimp.plugin.ana.ChannelStat;
12  import com.github.celldynamics.quimp.utils.QuimpToolsCollection;
13  
14  import ij.IJ;
15  import ij.measure.ResultsTable;
16  
17  /**
18   * Hold statistic evaluated for one frame and one outline, both geometric and fluorescence.
19   * 
20   * @author p.baniukiewicz
21   * @see CellStatsEval
22   * @see ChannelStat
23   */
24  public class FrameStatistics {
25    /**
26     * Frame number.
27     */
28    public int frame;
29    /**
30     * Area of outline.
31     */
32    public double area;
33    /**
34     * Centroid of outline.
35     */
36    public ExtendedVector2d centroid;
37    /**
38     * Elongation of outline. An ellipse is fitted to the cell outline and the major/minor axis used
39     * to compute the elongation of the cell’s shape. Elongation = major axis∕minor axis. Note that a
40     * value of 1 does not necessarily represent a perfectly circular cell, only a circular fitted
41     * ellipse.
42     */
43    public double elongation;
44    /**
45     * Circularity of outline. A measure of circularity defined by the following equation:
46     * 4*PI*Area/(Perimeter*Perimeter). A value of 1 reveals the cell’s outline to be perfectly
47     * circular.
48     */
49    public double circularity;
50    /**
51     * Perimeter of outline. Length of the cell perimeter (segmented outline).
52     */
53    public double perimiter;
54    /**
55     * Displacement of outline. Distance the cell centroid has moved from its position in the first
56     * recorded frame.
57     */
58    public double displacement;
59    /**
60     * Cumulated distance that cell moved from the first recorded frame. (sum of distances between i-1
61     * and i frame)
62     */
63    public double dist;
64    /**
65     * Persistence of outline. Persistence in direction, calculated as Displacement∕Dist.Travelled
66     * (chemotaxis index). A value of 1 reveals that a cell has moved in a straight line. Decreasing
67     * values denote a cell moving increasingly erratically.
68     */
69    public double persistance;
70    /**
71     * Speed at which the centroid moved between the current and previous frame.
72     */
73    public double speed;
74    /**
75     * Unknown.
76     * 
77     * @deprecated Not used
78     */
79    public double persistanceToSource;
80    /**
81     * Dispersion of outline.
82     * 
83     * @deprecated Not used
84     */
85    public double dispersion;
86    /**
87     * Extension of outline.
88     * 
89     * @deprecated Not used
90     */
91    public double extension;
92    /**
93     * Fluorescence stats added by ANA module.
94     */
95    public ChannelStat[] channels;
96  
97    /**
98     * Default constructor, create empty container.
99     */
100   public FrameStatistics() {
101     centroid = new ExtendedVector2d();
102     channels = new ChannelStat[3];
103     channels[0] = new ChannelStat();
104     channels[1] = new ChannelStat();
105     channels[2] = new ChannelStat();
106   }
107 
108   /**
109    * Rescale scalable parameters (area, perimeter,etc) to SI units.
110    * 
111    * @param scale scale
112    * @param frameInterval frame interval
113    */
114   public void toScale(double scale, double frameInterval) {
115     area = QuimpToolsCollection.areaToScale(area, scale);
116     perimiter = QuimpToolsCollection.distanceToScale(perimiter, scale);
117     displacement = QuimpToolsCollection.distanceToScale(displacement, scale);
118     dist = QuimpToolsCollection.distanceToScale(dist, scale);
119     speed = QuimpToolsCollection.speedToScale(speed, scale, frameInterval); // over 1 frame
120   }
121 
122   /**
123    * Re-scale centroid to pixels (from SI).
124    * 
125    * @param scale scale
126    */
127   void centroidToPixels(double scale) {
128     centroid.setXY(centroid.getX() / scale, centroid.getY() / scale);
129   }
130 
131   /**
132    * Clear channel statistics.
133    * 
134    * @see ChannelStat
135    */
136   public void clearFluo() {
137     this.channels[0] = new ChannelStat();
138     this.channels[1] = new ChannelStat();
139     this.channels[2] = new ChannelStat();
140   }
141 
142   /**
143    * Write stat file in old format (stQP.csv).
144    * 
145    * @param s statistics to write
146    * @param outfile file name
147    * @param scale image scale
148    * @param frameInterval data frame interval
149    * @throws IOException on file error
150    */
151   public static void write(FrameStatistics[] s, File outfile, double scale, double frameInterval)
152           throws IOException {
153     PrintWriter pw = new PrintWriter(new FileWriter(outfile), true); // auto flush
154     IJ.log("Writing to file: " + outfile.getName());
155     pw.print("#p2\n#QuimP output - " + outfile.getAbsolutePath() + "\n");
156     pw.print("# Centroids are given in pixels.  Distance & speed & area measurements are scaled"
157             + " to micro meters\n");
158     pw.print("# Scale: " + scale + " micro meter per pixel | Frame interval: " + frameInterval
159             + " sec\n");
160     pw.print("# Frame,X-Centroid,Y-Centroid,Displacement,Dist. Traveled,"
161             + "Directionality,Speed,Perimeter,Elongation,Circularity,Area");
162 
163     for (int i = 0; i < s.length; i++) {
164       pw.print("\n" + s[i].frame + "," + IJ.d2s(s[i].centroid.getX(), 2) + ","
165               + IJ.d2s(s[i].centroid.getY(), 2) + "," + IJ.d2s(s[i].displacement) + ","
166               + IJ.d2s(s[i].dist) + "," + IJ.d2s(s[i].persistance) + "," + IJ.d2s(s[i].speed) + ","
167               + IJ.d2s(s[i].perimiter) + "," + IJ.d2s(s[i].elongation) + ","
168               + IJ.d2s(s[i].circularity, 3) + "," + IJ.d2s(s[i].area));
169     }
170     pw.print("\n#\n# Fluorescence measurements");
171     writeFluo(s, pw, 0);
172     writeFluo(s, pw, 1);
173     writeFluo(s, pw, 2);
174     pw.close();
175   }
176 
177   private static void writeFluo(FrameStatistics[] s, PrintWriter pw, int c) {
178     pw.print("\n#\n# Channel " + (c + 1)
179             + ";Frame, Total Fluo.,Mean Fluo.,Cortex Width, Cyto. Area,Total Cyto. Fluo.,"
180             + " Mean Cyto. Fluo.,"
181             + "Cortex Area,Total Cortex Fluo., Mean Cortex Fluo., %age Cortex Fluo.");
182     for (int i = 0; i < s.length; i++) {
183       pw.print("\n" + s[i].frame + "," + IJ.d2s(s[i].channels[c].totalFluor) + ","
184               + IJ.d2s(s[i].channels[c].meanFluor) + "," + IJ.d2s(s[i].channels[c].cortexWidth));
185       pw.print("," + IJ.d2s(s[i].channels[c].innerArea) + ","
186               + IJ.d2s(s[i].channels[c].totalInnerFluor) + ","
187               + IJ.d2s(s[i].channels[c].meanInnerFluor));
188       pw.print("," + IJ.d2s(s[i].channels[c].cortexArea) + ","
189               + IJ.d2s(s[i].channels[c].totalCorFluo) + "," + IJ.d2s(s[i].channels[c].meanCorFluo)
190               + "," + IJ.d2s(s[i].channels[c].percCortexFluo));
191     }
192   }
193 
194   /**
195    * Load statistics from old file format.
196    * 
197    * @param infile file to read
198    * @return Array of statistics for all object in frame
199    * @throws IOException on file error
200    */
201   public static FrameStatistics[] read(File infile) throws IOException {
202 
203     BufferedReader br = new BufferedReader(new FileReader(infile));
204     String thisLine;
205     int i = 0;
206     // count the number of frames in .scv file
207     while ((thisLine = br.readLine()) != null) {
208       if (thisLine.startsWith("# Fluorescence measurements")) {
209         break;
210       }
211       if (thisLine.startsWith("#")) {
212         continue;
213       }
214       // System.out.println(thisLine);
215       i++;
216     }
217     br.close();
218     FrameStatisticstistics.html#FrameStatistics">FrameStatistics[] stats = new FrameStatistics[i];
219 
220     i = 0;
221     String[] split;
222     br = new BufferedReader(new FileReader(infile)); // re-open and read
223     while ((thisLine = br.readLine()) != null) {
224       if (thisLine.startsWith("# Channel")) { // reached fluo stats
225         break;
226       }
227       if (thisLine.startsWith("#")) {
228         continue;
229       }
230       // System.out.println(thisLine);
231 
232       split = thisLine.split(",");
233 
234       stats[i] = new FrameStatistics();
235       stats[i].frame = (int) QuimpToolsCollection.s2d(split[0]);
236       stats[i].centroid.setXY(QuimpToolsCollection.s2d(split[1]),
237               QuimpToolsCollection.s2d(split[2]));
238       stats[i].displacement = QuimpToolsCollection.s2d(split[3]);
239       stats[i].dist = QuimpToolsCollection.s2d(split[4]);
240       stats[i].persistance = QuimpToolsCollection.s2d(split[5]);
241       stats[i].speed = QuimpToolsCollection.s2d(split[6]);
242       stats[i].perimiter = QuimpToolsCollection.s2d(split[7]);
243       stats[i].elongation = QuimpToolsCollection.s2d(split[8]);
244       stats[i].circularity = QuimpToolsCollection.s2d(split[9]);
245       stats[i].area = QuimpToolsCollection.s2d(split[10]);
246 
247       i++;
248     }
249 
250     readChannel(0, stats, br);
251     readChannel(1, stats, br);
252     readChannel(2, stats, br);
253 
254     br.close();
255     return stats;
256   }
257 
258   private static void readChannel(int c, FrameStatistics[] stats, BufferedReader br)
259           throws IOException {
260     String thisLine;
261     String[] split;
262     int i = 0;
263     while ((thisLine = br.readLine()) != null) {
264       if (thisLine.startsWith("# Channel")) {
265         break;
266       }
267       if (thisLine.startsWith("#")) {
268         continue;
269       }
270 
271       split = thisLine.split(",");
272       // split[0] == frame
273       stats[i].channels[c].totalFluor = QuimpToolsCollection.s2d(split[1]);
274       stats[i].channels[c].meanFluor = QuimpToolsCollection.s2d(split[2]);
275       stats[i].channels[c].cortexWidth = QuimpToolsCollection.s2d(split[3]);
276       stats[i].channels[c].innerArea = QuimpToolsCollection.s2d(split[4]);
277       stats[i].channels[c].totalInnerFluor = QuimpToolsCollection.s2d(split[5]);
278       stats[i].channels[c].meanInnerFluor = QuimpToolsCollection.s2d(split[6]);
279       stats[i].channels[c].cortexArea = QuimpToolsCollection.s2d(split[7]);
280       stats[i].channels[c].totalCorFluo = QuimpToolsCollection.s2d(split[8]);
281       stats[i].channels[c].meanCorFluo = QuimpToolsCollection.s2d(split[9]);
282       stats[i].channels[c].percCortexFluo = QuimpToolsCollection.s2d(split[10]);
283 
284       i++;
285     }
286   }
287 
288   /**
289    * Add channel statistic to given ResultsTable.
290    * 
291    * @param rt IJ result table
292    * @param channelno channel number for fluoro stats
293    * @see #addStatToResultTable(ResultsTable)
294    */
295   public void addFluoToResultTable(ResultsTable rt, int channelno) {
296     // Those fields must be related to writeFluo
297     ChannelStat cs = channels[channelno]; // reference to channel
298     rt.incrementCounter();
299     rt.addValue("frame", frame);
300     rt.addValue("TotalFluo", cs.totalFluor);
301     rt.addValue("MeanFluo", cs.meanFluor);
302     rt.addValue("Cortex Width", cs.cortexWidth);
303     rt.addValue("Cyto. Area", cs.innerArea);
304     rt.addValue("Total Cyto. Fluo.", cs.totalInnerFluor);
305     rt.addValue("Mean Cyto. Fluo.h", cs.meanInnerFluor);
306     rt.addValue("Cortex Area", cs.cortexArea);
307     rt.addValue("Total Cortex Fluo.", cs.totalCorFluo);
308     rt.addValue("Mean Cortex Fluo.", cs.meanCorFluo);
309     rt.addValue("%age Cortex Fluo.", cs.percCortexFluo);
310   }
311 
312   /**
313    * Add statistics for this frame to resutl table.
314    * 
315    * @param rt IJ result table
316    * @see #addFluoToResultTable(ResultsTable, int)
317    */
318   public void addStatToResultTable(ResultsTable rt) {
319     rt.incrementCounter();
320     rt.addValue("Frame", frame);
321     rt.addValue("Area", area);
322     rt.addValue("Centroid_x", centroid.getX());
323     rt.addValue("Centroid_y", centroid.getY());
324     rt.addValue("Circularity", circularity);
325     rt.addValue("Displacement", displacement);
326     rt.addValue("Distance", dist);
327     rt.addValue("Elongation", elongation);
328     rt.addValue("Perimeter", perimiter);
329     rt.addValue("Persistance", persistance);
330     rt.addValue("Speed", speed);
331 
332   }
333 
334 }