View Javadoc
1   package com.github.celldynamics.quimp.plugin.qanalysis;
2   
3   import java.io.File;
4   import java.io.IOException;
5   import java.util.ArrayList;
6   import java.util.Iterator;
7   
8   import org.slf4j.Logger;
9   import org.slf4j.LoggerFactory;
10  
11  import com.github.celldynamics.quimp.OutlineHandler;
12  import com.github.celldynamics.quimp.QColor;
13  import com.github.celldynamics.quimp.QParams;
14  import com.github.celldynamics.quimp.QParamsQconf;
15  import com.github.celldynamics.quimp.QuimP;
16  import com.github.celldynamics.quimp.QuimpException;
17  import com.github.celldynamics.quimp.QuimpException.MessageSinkTypes;
18  import com.github.celldynamics.quimp.filesystem.FileExtensions;
19  import com.github.celldynamics.quimp.filesystem.QconfLoader;
20  import com.github.celldynamics.quimp.filesystem.converter.FormatConverter;
21  import com.github.celldynamics.quimp.plugin.AbstractPluginQconf;
22  import com.github.celldynamics.quimp.plugin.QuimpPluginException;
23  import com.github.celldynamics.quimp.plugin.ecmm.ECMM_Mapping;
24  import com.github.celldynamics.quimp.utils.graphics.svg.SVGplotter;
25  
26  import ij.IJ;
27  import ij.gui.GenericDialog;
28  import ij.gui.YesNoCancelDialog;
29  
30  /**
31   * Run Q analysis for ECMM data.
32   * 
33   * <p>In principle this object loads and process name_X.paQP file generated by ECMM. <i>X</i> in
34   * this
35   * case means number of cell outline. The same result can be achieved by loading QCONF file that
36   * contains all outlines for given case. This class is designed to process one outline in one time.
37   * Thus most of methods operate on current status private fields (such as <tt>qp</tt>, <tt>oh</tt>).
38   * The main analysis runner method is {@link #runPlugin()}, whereas support for both input formats
39   * is
40   * covered by {@link #runFromPaqp()} and {@link #runFromQconf()} (similarly to {@link ECMM_Mapping
41   * ECMM_Mapping})
42   * 
43   * @author rtyson
44   * @author p.baniukiewicz
45   */
46  public class Q_Analysis extends AbstractPluginQconf {
47  
48    private static String thisPluginName = "QuimP Analysis";
49  
50    /**
51     * The Constant LOGGER.
52     */
53    static final Logger LOGGER = LoggerFactory.getLogger(Q_Analysis.class.getName());
54  
55    private File fileToLoad = null; // file to load paQP/QCONF
56  
57    /**
58     * The gd.
59     */
60    GenericDialog gd;
61    private OutlineHandler oh; // keep loaded handler, can change during run
62    private STmap stMap; // object holding all maps evaluated for current OutlineHandler (oh)
63  
64    /**
65     * Main constructor and runner - class entry point.
66     * 
67     * <p>Left in this form for backward compatibility
68     */
69    public Q_Analysis() {
70      super(new Qp(), thisPluginName);
71    }
72  
73    /**
74     * Parameterised constructor for tests.
75     * 
76     * @param paramFile paQP or QCONF file to process. If <tt>null</tt> user is asked for this file
77     * @see com.github.celldynamics.quimp.plugin.ecmm.ECMM_Mapping#ECMM_Mapping(File)
78     */
79    public Q_Analysis(File paramFile) {
80      super(new Qp(paramFile), thisPluginName);
81      apiCall = true;
82    }
83  
84    /*
85     * (non-Javadoc)
86     * 
87     * @see com.github.celldynamics.quimp.plugin.AbstractPluginQconf#loadFile(java.lang.String)
88     */
89    @Override
90    protected void loadFile(String paramFile) throws QuimpException {
91      // we need to use different handling for multiple paQP files, so use own loader
92      Qphref="../../../../../../com/github/celldynamics/quimp/plugin/qanalysis/Qp.html#Qp">Qp opts = (Qp) options;
93  
94      if (options.paramFile == null || options.paramFile.isEmpty()) {
95        fileToLoad = null;
96      } else {
97        fileToLoad = new File(options.paramFile);
98      }
99      qconfLoader = new QconfLoader(fileToLoad); // load file
100     if (qconfLoader == null || qconfLoader.getQp() == null) {
101       return; // failed to load exit
102     }
103     if (qconfLoader.isFileLoaded() == QParams.QUIMP_11) { // old path
104       QParams qp;
105       runFromPaqp();
106       File[] otherPaFiles = qconfLoader.getQp().findParamFiles();
107       if (otherPaFiles.length > 0) { // and process them if they are
108         YesNoCancelDialog yncd = new YesNoCancelDialog(IJ.getInstance(), "Batch Process?",
109                 "\tBatch Process?\n\n" + "Process other " + FileExtensions.configFileExt
110                         + " files in the same folder with QAnalysis?"
111                         + "\n[The same parameters will be used]");
112         if (yncd.yesPressed()) {
113           ArrayList<String> runOn = new ArrayList<String>(otherPaFiles.length);
114           this.closeAllImages();
115 
116           // if user agreed iterate over found files
117           // (except that loaded explicitly by user)
118           for (int j = 0; j < otherPaFiles.length; j++) {
119             IJ.log("Running on " + otherPaFiles[j].getAbsolutePath());
120             qconfLoader = new QconfLoader(otherPaFiles[j]);
121             qp = qconfLoader.getQp();
122             opts.setup(qp);
123             oh = new OutlineHandler(qp); // prepare current OutlineHandler
124             if (!oh.readSuccess) {
125               LOGGER.error("OutlineHandlers could not be read!");
126               return;
127             }
128             runPlugin(); // run on current OutlineHandler
129             runOn.add(otherPaFiles[j].getName());
130             this.closeAllImages();
131           }
132           IJ.log("\n\nBatch - Successfully ran QAnalysis on:");
133           for (int i = 0; i < runOn.size(); i++) {
134             IJ.log(runOn.get(i));
135           }
136         } else {
137           return; // no batch processing
138         }
139       }
140     } else if (qconfLoader.isFileLoaded() == QParams.NEW_QUIMP) { // new path
141       // verification for components run
142       validate();
143       if (qconfLoader.isQPresent() && apiCall == false && errorSink == MessageSinkTypes.GUI) {
144         YesNoCancelDialog ync;
145         ync = new YesNoCancelDialog(IJ.getInstance(), "Overwrite",
146                 "You are about to override previous Q results. Is it ok?");
147         if (!ync.yesPressed()) { // if no or cancel
148           IJ.log("No changes done in input file.");
149           return; // end}
150         }
151       }
152       runFromQconf();
153       IJ.log("The new data file " + qconfLoader.getQp().getParamFile().toString()
154               + " has been updated by results of Q Analysis.");
155     } else {
156       throw new IllegalStateException("QconfLoader returned unknown version of QuimP or error: "
157               + qconfLoader.isFileLoaded());
158     }
159   }
160 
161   /*
162    * (non-Javadoc)
163    * 
164    * @see com.github.celldynamics.quimp.plugin.AbstractPluginQconf#executer()
165    */
166   @Override
167   protected void executer() throws QuimpException {
168     if (apiCall == true) { // if run from other constructor, override sink (after run() set it)
169       errorSink = MessageSinkTypes.CONSOLE;
170     }
171     super.executer();
172   }
173 
174   /*
175    * (non-Javadoc)
176    * 
177    * @see com.github.celldynamics.quimp.plugin.AbstractPluginBase#run(java.lang.String)
178    */
179   @Override
180   public void run(String arg) {
181     super.run(arg);
182   }
183 
184   /*
185    * (non-Javadoc)
186    * 
187    * @see com.github.celldynamics.quimp.plugin.AbstractPluginQconf#validate()
188    */
189   @Override
190   protected void validate() throws QuimpException {
191     super.validate();
192     qconfLoader.getEcmm();
193     qconfLoader.getANA();
194     qconfLoader.getStats();
195   }
196 
197   /**
198    * Main runner - do all calculations on current OutlineHandler object.
199    * 
200    * <p><b>Warning</b>
201    * 
202    * <p>Process current <tt>OutlineHandler oh</tt>; object and modify it filling some fields from
203    * Vertex class
204    * 
205    * @throws QuimpException on error wit saving maps
206    * 
207    */
208   private void runPlugin() throws QuimpException {
209     Qphref="../../../../../../com/github/celldynamics/quimp/plugin/qanalysis/Qp.html#Qp">Qp opts = (Qp) options;
210     if (oh.getSize() == 1) {
211       opts.singleImage = true;
212       // only one frame - re lable node indices
213       oh.getStoredOutline(1).resetAllCoords();
214     }
215 
216     opts.convexityToPixels();
217 
218     stMap = new STmap(oh, opts.mapRes, opts);
219     if (QuimP.newFileFormat.get() == false) {
220       stMap.saveMaps(STmap.ALLMAPS); // save maps only for old path
221     }
222 
223     SVGplotterutils/graphics/svg/SVGplotter.html#SVGplotter">SVGplotter svgPlotter = new SVGplotter(oh, opts.fps, opts.scale, opts.channel, opts.outFile);
224     svgPlotter.plotTrack(opts.trackColor, opts.increment);
225     // svgPlotter.plotTrackAnim();
226     svgPlotter.plotTrackER(opts.outlinePlot);
227 
228     opts.convexityToUnits(); // reset the covexity options to units (as they are static)
229   }
230 
231   private boolean showDialog() {
232     Qphref="../../../../../../com/github/celldynamics/quimp/plugin/qanalysis/Qp.html#Qp">Qp opts = (Qp) options;
233     gd = new GenericDialog("Q Analysis Options", IJ.getInstance());
234 
235     gd.setOKLabel("RUN");
236 
237     gd.addMessage("Pixel width: " + opts.scale + " \u00B5m\nFrame Interval: " + opts.frameInterval
238             + " sec");
239 
240     gd.addMessage("******* Cell track options (svg) *******");
241     gd.addNumericField("Frame_increment", opts.increment, 0);
242     gd.addChoice("Colour_Map", QColor.colourMaps, QColor.colourMaps[0]);
243 
244     gd.addMessage("***** Motility movie options (svg) *****");
245     gd.addChoice("Colour_using", opts.outlinePlots, opts.outlinePlots[0]);
246 
247     gd.addMessage("********** Convexity options **********");
248     gd.addNumericField("Sum_over (\u00B5m)", opts.sumCov, 2);
249     gd.addNumericField("Smooth_over (\u00B5m)", opts.avgCov, 2);
250 
251     gd.addMessage("************* Map options *************");
252     gd.addNumericField("Map_resolution", opts.mapRes, 0);
253 
254     // gd.addMessage("************* Head nodes **************");
255     // gd.addChoice("Heads", headActions, headActions[0]);
256 
257     gd.setResizable(false);
258     gd.showDialog();
259 
260     if (gd.wasCanceled()) {
261       return false;
262     }
263 
264     // Qp.scale = gd.getNextNumber();
265     // Qp.setFPS(gd.getNextNumber());
266     opts.increment = (int) gd.getNextNumber();
267     opts.trackColor = gd.getNextChoice();
268     opts.outlinePlot = gd.getNextChoice();
269     opts.sumCov = gd.getNextNumber();
270     opts.avgCov = gd.getNextNumber();
271     opts.mapRes = (int) gd.getNextNumber();
272     // Qp.headProcessing = gd.getNextChoice();
273 
274     return true;
275   }
276 
277   private void closeAllImages() {
278     int[] ids = ij.WindowManager.getIDList();
279     for (int i = 0; i < ids.length; i++) {
280       ij.WindowManager.getImage(ids[i]).close();
281     }
282   }
283 
284   /*
285    * (non-Javadoc)
286    * 
287    * @see com.github.celldynamics.quimp.plugin.IQuimpPlugin#about()
288    */
289   @Override
290   public String about() {
291     return "Q analysis plugin.\n" + "Authors: Piotr Baniukiewicz\n"
292             + "mail: p.baniukiewicz@warwick.ac.uk\n" + "Richard Tyson";
293   }
294 
295   /*
296    * (non-Javadoc)
297    * 
298    * @see com.github.celldynamics.quimp.plugin.AbstractPluginQconf#runFromQconf()
299    */
300   @Override
301   protected void runFromQconf() throws QuimpException {
302     // {@link #run()} updates also {@link DataContainer#ECMMState ECMMState} by modifying fields in
303     // Outlines that are accessed by reference here.
304     Qphref="../../../../../../com/github/celldynamics/quimp/plugin/qanalysis/Qp.html#Qp">Qp opts = (Qp) options;
305     int i = 0;
306     QParamsQconf/../../../../../com/github/celldynamics/quimp/QParamsQconf.html#QParamsQconf">QParamsQconf qp = (QParamsQconf) qconfLoader.getQp();
307     Iterator<OutlineHandler> oi = qp.getLoadedDataContainer().getEcmmState().oHs.iterator();
308     ArrayList<STmap> tmp = new ArrayList<>();
309     while (oi.hasNext()) {
310       qp.setActiveHandler(i++); // set current handler number.
311       opts.setup(qconfLoader.getQp()); // copy selected data from general QParams to local storage
312       oh = oi.next();
313       runPlugin();
314       tmp.add(new STmap(stMap)); // store generated map
315     }
316     qp.getLoadedDataContainer().QState = tmp.toArray(new STmap[0]);
317     try {
318       qp.writeParams();
319     } catch (IOException e) {
320       throw new QuimpPluginException(e);
321     } // save global container
322     // generate additional OLD files, disabled #263, enabled 228
323     if (QuimP.newFileFormat.get() == false) {
324       FormatConverter/converter/FormatConverter.html#FormatConverter">FormatConverter formatConverter = new FormatConverter(qconfLoader);
325       formatConverter.doConversion();
326     }
327 
328   }
329 
330   /*
331    * (non-Javadoc)
332    * 
333    * @see com.github.celldynamics.quimp.plugin.AbstractPluginQconf#runFromPaqp()
334    */
335   @Override
336   protected void runFromPaqp() throws QuimpException {
337     Qphref="../../../../../../com/github/celldynamics/quimp/plugin/qanalysis/Qp.html#Qp">Qp opts = (Qp) options;
338     opts.setup(qconfLoader.getQp()); // copy selected data from general QParams to local storage
339     oh = new OutlineHandler(qconfLoader.getQp()); // load data from file
340     if (!oh.readSuccess) {
341       throw new QuimpException("Could not read OutlineHandler");
342     }
343     runPlugin();
344   }
345 
346   /*
347    * (non-Javadoc)
348    * 
349    * @see com.github.celldynamics.quimp.plugin.AbstractPluginBase#showUi(boolean)
350    */
351   @Override
352   public void showUi(boolean val) throws Exception {
353     if (!showDialog()) {
354       return;
355     }
356     executer();
357     // in case user loaded the file
358     if (qconfLoader != null && qconfLoader.getQp() != null) {
359       options.paramFile = qconfLoader.getQp().getParamFile().getAbsolutePath();
360     }
361 
362   }
363 }