Q_Analysis.java
package com.github.celldynamics.quimp.plugin.qanalysis;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.celldynamics.quimp.OutlineHandler;
import com.github.celldynamics.quimp.QColor;
import com.github.celldynamics.quimp.QParams;
import com.github.celldynamics.quimp.QParamsQconf;
import com.github.celldynamics.quimp.QuimP;
import com.github.celldynamics.quimp.QuimpException;
import com.github.celldynamics.quimp.QuimpException.MessageSinkTypes;
import com.github.celldynamics.quimp.filesystem.FileExtensions;
import com.github.celldynamics.quimp.filesystem.QconfLoader;
import com.github.celldynamics.quimp.filesystem.converter.FormatConverter;
import com.github.celldynamics.quimp.plugin.AbstractPluginQconf;
import com.github.celldynamics.quimp.plugin.QuimpPluginException;
import com.github.celldynamics.quimp.plugin.ecmm.ECMM_Mapping;
import com.github.celldynamics.quimp.utils.graphics.svg.SVGplotter;
import ij.IJ;
import ij.gui.GenericDialog;
import ij.gui.YesNoCancelDialog;
/**
* Run Q analysis for ECMM data.
*
* <p>In principle this object loads and process name_X.paQP file generated by ECMM. <i>X</i> in
* this
* case means number of cell outline. The same result can be achieved by loading QCONF file that
* contains all outlines for given case. This class is designed to process one outline in one time.
* Thus most of methods operate on current status private fields (such as <tt>qp</tt>, <tt>oh</tt>).
* The main analysis runner method is {@link #runPlugin()}, whereas support for both input formats
* is
* covered by {@link #runFromPaqp()} and {@link #runFromQconf()} (similarly to {@link ECMM_Mapping
* ECMM_Mapping})
*
* @author rtyson
* @author p.baniukiewicz
*/
public class Q_Analysis extends AbstractPluginQconf {
private static String thisPluginName = "QuimP Analysis";
/**
* The Constant LOGGER.
*/
static final Logger LOGGER = LoggerFactory.getLogger(Q_Analysis.class.getName());
private File fileToLoad = null; // file to load paQP/QCONF
/**
* The gd.
*/
GenericDialog gd;
private OutlineHandler oh; // keep loaded handler, can change during run
private STmap stMap; // object holding all maps evaluated for current OutlineHandler (oh)
/**
* Main constructor and runner - class entry point.
*
* <p>Left in this form for backward compatibility
*/
public Q_Analysis() {
super(new Qp(), thisPluginName);
}
/**
* Parameterised constructor for tests.
*
* @param paramFile paQP or QCONF file to process. If <tt>null</tt> user is asked for this file
* @throws QuimpException on error in {@link #loadFile(String)}
* @see com.github.celldynamics.quimp.plugin.ecmm.ECMM_Mapping#ECMM_Mapping(File)
*/
public Q_Analysis(File paramFile) throws QuimpException {
super(new Qp(paramFile), thisPluginName);
apiCall = true;
loadFile(paramFile.toString());
}
/*
* (non-Javadoc)
*
* @see com.github.celldynamics.quimp.plugin.AbstractPluginQconf#loadFile(java.lang.String)
*/
@Override
protected void loadFile(String paramFile) throws QuimpException {
// we need to use different handling for multiple paQP files, so use own loader
if (paramFile == null || paramFile.isEmpty()) {
fileToLoad = null;
} else {
fileToLoad = new File(paramFile);
}
qconfLoader = new QconfLoader(fileToLoad); // load file
if (qconfLoader != null && qconfLoader.getQp() != null) {
options.paramFile = qconfLoader.getQp().getParamFile().getAbsolutePath();
}
}
/*
* (non-Javadoc)
*
* @see com.github.celldynamics.quimp.plugin.AbstractPluginQconf#executer()
*/
@Override
public void executer() throws QuimpException {
Qp opts = (Qp) options;
if (apiCall == true) { // if run from other constructor, override sink (after run() set it)
errorSink = MessageSinkTypes.CONSOLE;
}
if (qconfLoader == null || qconfLoader.getQp() == null) {
// if we came here from Macro, file is not loaded (it is loaded before UI in showUI)
// paramFile should contain something
if (options.paramFile == null) {
throw new QuimpException("Option \"paramFile\" not specified in macro string");
}
loadFile(options.paramFile);
}
if (qconfLoader.isFileLoaded() == QParams.QUIMP_11) { // old path
QParams qp;
runFromPaqp();
File[] otherPaFiles = qconfLoader.getQp().findParamFiles();
if (otherPaFiles.length > 0) { // and process them if they are
YesNoCancelDialog yncd = new YesNoCancelDialog(IJ.getInstance(), "Batch Process?",
"\tBatch Process?\n\n" + "Process other " + FileExtensions.configFileExt
+ " files in the same folder with QAnalysis?"
+ "\n[The same parameters will be used]");
if (yncd.yesPressed()) {
ArrayList<String> runOn = new ArrayList<String>(otherPaFiles.length);
this.closeAllImages();
// if user agreed iterate over found files
// (except that loaded explicitly by user)
for (int j = 0; j < otherPaFiles.length; j++) {
IJ.log("Running on " + otherPaFiles[j].getAbsolutePath());
qconfLoader = new QconfLoader(otherPaFiles[j]);
qp = qconfLoader.getQp();
opts.setup(qp);
oh = new OutlineHandler(qp); // prepare current OutlineHandler
if (!oh.readSuccess) {
LOGGER.error("OutlineHandlers could not be read!");
return;
}
runPlugin(); // run on current OutlineHandler
runOn.add(otherPaFiles[j].getName());
this.closeAllImages();
}
IJ.log("\n\nBatch - Successfully ran QAnalysis on:");
for (int i = 0; i < runOn.size(); i++) {
IJ.log(runOn.get(i));
}
} else {
return; // no batch processing
}
}
} else if (qconfLoader.isFileLoaded() == QParams.NEW_QUIMP) { // new path
// verification for components run
validate();
if (qconfLoader.isQPresent() && apiCall == false && errorSink == MessageSinkTypes.GUI) {
YesNoCancelDialog ync;
ync = new YesNoCancelDialog(IJ.getInstance(), "Overwrite",
"You are about to override previous Q results. Is it ok?");
if (!ync.yesPressed()) { // if no or cancel
IJ.log("No changes done in input file.");
return; // end}
}
}
runFromQconf();
IJ.log("The new data file " + qconfLoader.getQp().getParamFile().toString()
+ " has been updated by results of Q Analysis.");
} else {
throw new IllegalStateException("QconfLoader returned unknown version of QuimP or error: "
+ qconfLoader.isFileLoaded());
}
}
/*
* (non-Javadoc)
*
* @see com.github.celldynamics.quimp.plugin.AbstractPluginBase#run(java.lang.String)
*/
@Override
public void run(String arg) {
super.run(arg);
}
/*
* (non-Javadoc)
*
* @see com.github.celldynamics.quimp.plugin.AbstractPluginQconf#validate()
*/
@Override
protected void validate() throws QuimpException {
super.validate();
qconfLoader.getEcmm();
qconfLoader.getANA();
qconfLoader.getStats();
}
/**
* Main runner - do all calculations on current OutlineHandler object.
*
* <p><b>Warning</b>
*
* <p>Process current <tt>OutlineHandler oh</tt>; object and modify it filling some fields from
* Vertex class
*
* @throws QuimpException on error wit saving maps
*
*/
private void runPlugin() throws QuimpException {
Qp opts = (Qp) options;
if (oh.getSize() == 1) {
opts.singleImage = true;
// only one frame - re lable node indices
oh.getStoredOutline(1).resetAllCoords();
}
opts.convexityToPixels();
stMap = new STmap(oh, opts.mapRes, opts);
if (QuimP.newFileFormat.get() == false) {
stMap.saveMaps(STmap.ALLMAPS); // save maps only for old path
}
SVGplotter svgPlotter = new SVGplotter(oh, opts.fps, opts.scale, opts.channel, opts.outFile);
svgPlotter.plotTrack(opts.trackColor, opts.increment);
// svgPlotter.plotTrackAnim();
svgPlotter.plotTrackER(opts.outlinePlot);
opts.convexityToUnits(); // reset the covexity options to units (as they are static)
}
private boolean showDialog() {
Qp opts = (Qp) options;
gd = new GenericDialog("Q Analysis Options", IJ.getInstance());
gd.setOKLabel("RUN");
gd.addMessage("Pixel width: " + opts.scale + " \u00B5m\nFrame Interval: " + opts.frameInterval
+ " sec");
gd.addMessage("******* Cell track options (svg) *******");
gd.addNumericField("Frame_increment", opts.increment, 0);
gd.addChoice("Colour_Map", QColor.colourMaps, QColor.colourMaps[0]);
gd.addMessage("***** Motility movie options (svg) *****");
gd.addChoice("Colour_using", opts.outlinePlots, opts.outlinePlots[0]);
gd.addMessage("********** Convexity options **********");
gd.addNumericField("Sum_over (\u00B5m)", opts.sumCov, 2);
gd.addNumericField("Smooth_over (\u00B5m)", opts.avgCov, 2);
gd.addMessage("************* Map options *************");
gd.addNumericField("Map_resolution", opts.mapRes, 0);
// gd.addMessage("************* Head nodes **************");
// gd.addChoice("Heads", headActions, headActions[0]);
gd.setResizable(false);
gd.showDialog();
if (gd.wasCanceled()) {
return false;
}
// Qp.scale = gd.getNextNumber();
// Qp.setFPS(gd.getNextNumber());
opts.increment = (int) gd.getNextNumber();
opts.trackColor = gd.getNextChoice();
opts.outlinePlot = gd.getNextChoice();
opts.sumCov = gd.getNextNumber();
opts.avgCov = gd.getNextNumber();
opts.mapRes = (int) gd.getNextNumber();
// Qp.headProcessing = gd.getNextChoice();
return true;
}
private void closeAllImages() {
int[] ids = ij.WindowManager.getIDList();
for (int i = 0; i < ids.length; i++) {
ij.WindowManager.getImage(ids[i]).close();
}
}
/*
* (non-Javadoc)
*
* @see com.github.celldynamics.quimp.plugin.IQuimpPlugin#about()
*/
@Override
public String about() {
return "Q analysis plugin.\n" + "Authors: Piotr Baniukiewicz\n"
+ "mail: p.baniukiewicz@warwick.ac.uk\n" + "Richard Tyson";
}
/*
* (non-Javadoc)
*
* @see com.github.celldynamics.quimp.plugin.AbstractPluginQconf#runFromQconf()
*/
@Override
protected void runFromQconf() throws QuimpException {
// {@link #run()} updates also {@link DataContainer#ECMMState ECMMState} by modifying fields in
// Outlines that are accessed by reference here.
Qp opts = (Qp) options;
int i = 0;
QParamsQconf qp = (QParamsQconf) qconfLoader.getQp();
Iterator<OutlineHandler> oi = qp.getLoadedDataContainer().getEcmmState().oHs.iterator();
ArrayList<STmap> tmp = new ArrayList<>();
while (oi.hasNext()) {
qp.setActiveHandler(i++); // set current handler number.
opts.setup(qconfLoader.getQp()); // copy selected data from general QParams to local storage
oh = oi.next();
runPlugin();
tmp.add(new STmap(stMap)); // store generated map
}
qp.getLoadedDataContainer().QState = tmp.toArray(new STmap[0]);
try {
qp.writeParams();
} catch (IOException e) {
throw new QuimpPluginException(e);
} // save global container
// generate additional OLD files, disabled #263, enabled 228
if (QuimP.newFileFormat.get() == false) {
FormatConverter formatConverter = new FormatConverter(qconfLoader);
formatConverter.doConversion();
}
}
/*
* (non-Javadoc)
*
* @see com.github.celldynamics.quimp.plugin.AbstractPluginQconf#runFromPaqp()
*/
@Override
protected void runFromPaqp() throws QuimpException {
Qp opts = (Qp) options;
opts.setup(qconfLoader.getQp()); // copy selected data from general QParams to local storage
oh = new OutlineHandler(qconfLoader.getQp()); // load data from file
if (!oh.readSuccess) {
throw new QuimpException("Could not read OutlineHandler");
}
runPlugin();
}
/*
* (non-Javadoc)
*
* @see com.github.celldynamics.quimp.plugin.AbstractPluginBase#showUi(boolean)
*/
@Override
public void showUi(boolean val) throws Exception {
// load file but do not execute - we need some information before showing UI
loadFile(options.paramFile);
// in case user loaded the file
if (qconfLoader == null || qconfLoader.getQp() == null) {
return; // cancelled (errors are by exceptions)
}
// show dialog
if (!showDialog()) {
return;
}
// execute comptations
executer();
}
}