analysers.ExportValidated.java Source code

Java tutorial

Introduction

Here is the source code for analysers.ExportValidated.java

Source

package analysers;

import ij.IJ;
import ij.Prefs;
import ij.gui.YesNoCancelDialog;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileNameExtensionFilter;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

import util.Cell;
import GUI.DisplayTrackingWindow;

/** 
 * Copyright 2013 University of Warwick
 * 
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 * 
 *        http://www.apache.org/licenses/LICENSE-2.0
 * 
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 *
 * This is a plugin for exporting all lineage data to a JSON file.
 *
 * @author Peter Krusche
 */
public class ExportValidated extends AnalysisPlugin {

    /* (non-Javadoc)
     * @see analysers.AnalysisPlugin#setup()
     */
    @Override
    public void setup() {
        FilterValidatedDialog fvd = new FilterValidatedDialog();
        fvd.setCells(cells);
        fvd.setVisible(true);
    }

    /* (non-Javadoc)
     * @see analysers.AnalysisPlugin#analyze(util.Cell)
     */
    @Override
    public void analyze(Cell currentCell) {
        JFileChooser fc = new JFileChooser();
        fc.addChoosableFileFilter(new FileNameExtensionFilter("Comma Separated Values", "csv"));
        fc.addChoosableFileFilter(new FileNameExtensionFilter("JSON file", "json"));
        int rv = fc.showSaveDialog(null);
        if (rv == JFileChooser.APPROVE_OPTION) {
            int b = Prefs.getInt(".TrackApp.FilterValidatedDialog.exportMean", 1);
            if (b != 0) {
                IJ.log("Exporting mean intensity values.");
            } else {
                IJ.log("Exporting sum of intensity values.");
            }

            File ff = fc.getSelectedFile();
            String fn = fc.getSelectedFile().getAbsolutePath();

            if (fc.getFileFilter().getDescription().contains("JSON")) {
                if (!fn.toLowerCase().endsWith(".json")) {
                    fn = fn + ".json";
                    ff = new File(ff + ".json");
                }
                IJ.log("Writing a JSON file.");
            } else {
                if (!fn.toLowerCase().endsWith(".csv")) {
                    fn = fn + ".csv";
                    ff = new File(ff + ".csv");
                }
                IJ.log("Writing a CSV file.");
            }
            IJ.log("Writing to " + fn);

            if (fn.endsWith(".csv")) {
                writeCSV(ff);
            } else if (fn.endsWith(".json")) {
                writeJSON(ff);
            } else {
                IJ.log("Unknown output format for file name " + fn);
            }

        }
    }

    /* (non-Javadoc)
     * @see analysers.AnalysisPlugin#cellClicked(util.Cell)
     */
    @Override
    public void cellClicked(Cell currentCell) {
        Cell a = findFirstAncestor(currentCell);

        String question = "";
        boolean stat = false;
        if (currentCell.isValidated()) {
            question = "This cell is marked as validated. Do you really want to remove validation from  this lineage?  [click 'redraw masks' to display the result]";
        } else {
            stat = true;
            question = "Validate cell? [click 'redraw masks' to display the result]";
        }

        YesNoCancelDialog yncd = new YesNoCancelDialog(null, "Toggle Validation",
                question + "  " + "This cell: " + currentCell.getCellID() + ":X=" + currentCell.getX() + ",Y="
                        + currentCell.getY() + "   " + "Ancestor : " + a.getCellID() + ":X=" + a.getX() + ",Y="
                        + a.getY() + "   ");
        if (yncd.yesPressed()) {
            IJ.log("Changing " + a.toString());
            // if we have a display window make sure the ROIs get updated too.
            if (screen instanceof DisplayTrackingWindow) {
                DisplayTrackingWindow dtw = (DisplayTrackingWindow) screen;
                validateCell(a, stat);
                // redraw
                dtw.updateAllDisplay();
                IJ.log("Done!");
            } else {
                validateCell(a, stat);
            }
        }
    }

    /**
     * Invalidate a cell
     * 
     * @param a the cell
     * @param stat the status (true => valid)
     */
    public void validateCell(Cell a, boolean stat) {
        // track back to parent
        if (a == null) {
            return;
        }
        while (a.getPreviousCell() != null) {
            a = a.getPreviousCell();
        }
        while (a != null) {
            a.setValidated(stat);
            if (a.getDaughterCell() != null) {
                validateCell(a.getDaughterCell(), stat);
            }
            a = a.getNextCell();
        }
    }

    /* (non-Javadoc)
     * @see analysers.AnalysisPlugin#getName()
     */
    @Override
    public String getName() {
        return "Export all validated Cells";
    }

    /** 
     * Find the first ancestor of a cell in a lineage.
     * @param c The cell 
     * @return the first ancestor of c
     */
    public static Cell findFirstAncestor(Cell c) {
        Cell anc = c;

        while (anc.getPreviousCell() != null) {
            anc = anc.getPreviousCell();
        }
        return anc;
    }

    /**
     * Write the validated lineages as JSON files
      * 
     * @param f the file name
     */
    @SuppressWarnings("unchecked")
    private void writeJSON(File f) {
        HashMap<String, Integer> lineagesDone = new HashMap<String, Integer>();
        int nlin = 1;
        int linsWritten = 0;
        int cellsWritten = 0;
        JSONArray jsa = new JSONArray();

        FilterValidatedDialog fvd = new FilterValidatedDialog();
        fvd.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        fvd.setCells(cells);

        for (Cell c : cells) {
            if (c.isValidated()) {
                Cell a = findFirstAncestor(c);
                if (lineagesDone.get("" + a.getCellID()) == null) {
                    lineagesDone.put("" + a.getCellID(), new Integer(nlin++));
                    if (fvd.accepts(a)) {
                        JSONObject lino = makeLineageJSON(a, nlin);
                        jsa.add(lino);
                        cellsWritten += ((JSONArray) lino.get("cells")).size();
                        linsWritten++;
                    }
                }
            }
        }

        IJ.log("Done, " + linsWritten + " lineages, and " + cellsWritten + " cells were exported.");

        fvd.dispose();

        try {
            FileWriter fw = new FileWriter(f);
            jsa.writeJSONString(fw);
            fw.close();
        } catch (IOException e) {
            IJ.log("Failed to write file!");
            e.printStackTrace();
        }
    }

    /**
     * Write the validated lineages as CSV files
      * 
     * @param f the file name
     */

    private void writeCSV(File f) {
        HashMap<String, Integer> lineagesDone = new HashMap<String, Integer>();
        int nlin = 1;
        int linsWritten = 0;
        int cellsWritten = 0;
        String sep = ", ";
        double dt = exp.getFrameInterval();

        try {
            FileWriter fw = new FileWriter(f);
            BufferedWriter bw = new BufferedWriter(fw);

            int maxframe = 1;
            for (Cell c : cells) {
                if (c.getFrame() > maxframe) {
                    maxframe = c.getFrame();
                }
            }

            // 1. write time axis
            bw.write("Lineage" + sep + "Cell" + sep + "First Frame" + sep + "Last Frame" + sep + "FirstX" + sep
                    + "FirstY" + sep + "Area" + sep + "Start Time" + sep + "End Time" + sep);
            bw.write("Time");
            for (int fr = 1; fr <= maxframe; ++fr) {
                bw.write(sep + ((fr - 1) * dt));
            }
            bw.write("\n");

            FilterValidatedDialog fvd = new FilterValidatedDialog();
            fvd.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
            fvd.setCells(cells);

            boolean doExportPositions = Prefs.get("TrackApp.FilterValidatedDialog.exportPositions", false);

            for (Cell c : cells) {
                if (c.isValidated()) {
                    Cell a = findFirstAncestor(c);
                    if (lineagesDone.get("" + a.getCellID()) == null) {
                        lineagesDone.put("" + a.getCellID(), new Integer(nlin++));
                        if (fvd.accepts(a)) {
                            JSONObject lino = makeLineageJSON(a, nlin);

                            JSONArray cells = (JSONArray) lino.get("cells");
                            for (@SuppressWarnings("rawtypes")
                            Iterator cit = cells.iterator(); cit.hasNext();) {
                                JSONObject co = (JSONObject) cit.next();
                                String name = co.get("name").toString();
                                String firstx = co.get("first_x").toString();
                                String firsty = co.get("first_y").toString();
                                String carea = co.get("area").toString();

                                int start = ((Integer) co.get("start_index")).intValue();
                                String prefix = "";
                                for (int i = 1; i < start; ++i) {
                                    prefix += sep + "0";
                                }

                                JSONArray ad = (JSONArray) co.get("data");
                                int i = 1;
                                for (@SuppressWarnings("rawtypes")
                                Iterator itad = ad.iterator(); itad.hasNext();) {
                                    JSONArray da = (JSONArray) itad.next();
                                    int end = start + da.size() - 1;
                                    bw.write("" + nlin);
                                    bw.write(sep + name);
                                    bw.write(sep + start);
                                    bw.write(sep + end);
                                    bw.write(sep + firstx);
                                    bw.write(sep + firsty);
                                    bw.write(sep + carea);
                                    bw.write(sep + ((start - 1) * dt));
                                    bw.write(sep + ((end - 1) * dt));
                                    bw.write(sep + "Channel " + i + prefix);

                                    i++;
                                    for (@SuppressWarnings("rawtypes")
                                    Iterator itda = da.iterator(); itda.hasNext();) {
                                        bw.write(sep + itda.next().toString());
                                    }
                                    for (int k = end + 1; k <= maxframe; ++k) {
                                        bw.write(sep + "0");
                                    }
                                    bw.write("\n");
                                }
                                if (doExportPositions) {
                                    ad = (JSONArray) co.get("frame_position");
                                    i = 1;
                                    for (@SuppressWarnings("rawtypes")
                                    Iterator itad = ad.iterator(); itad.hasNext();) {
                                        JSONArray da = (JSONArray) itad.next();
                                        int end = start + da.size() - 1;
                                        bw.write("" + nlin);
                                        bw.write(sep + name);
                                        bw.write(sep + start);
                                        bw.write(sep + end);
                                        bw.write(sep + firstx);
                                        bw.write(sep + firsty);
                                        bw.write(sep + carea);
                                        bw.write(sep + ((start - 1) * dt));
                                        bw.write(sep + ((end - 1) * dt));
                                        bw.write(sep + "Position " + i + prefix);

                                        i++;
                                        for (@SuppressWarnings("rawtypes")
                                        Iterator itda = da.iterator(); itda.hasNext();) {
                                            bw.write(sep + itda.next().toString());
                                        }
                                        for (int k = end + 1; k <= maxframe; ++k) {
                                            bw.write(sep + "0");
                                        }
                                        bw.write("\n");
                                    }

                                    /** write areas too */
                                    JSONArray da = (JSONArray) co.get("frame_area");
                                    int end = start + da.size() - 1;
                                    bw.write("" + nlin);
                                    bw.write(sep + name);
                                    bw.write(sep + start);
                                    bw.write(sep + end);
                                    bw.write(sep + firstx);
                                    bw.write(sep + firsty);
                                    bw.write(sep + carea);
                                    bw.write(sep + ((start - 1) * dt));
                                    bw.write(sep + ((end - 1) * dt));
                                    bw.write(sep + "Area" + prefix);

                                    i++;
                                    for (@SuppressWarnings("rawtypes")
                                    Iterator itda = da.iterator(); itda.hasNext();) {
                                        bw.write(sep + itda.next().toString());
                                    }
                                    for (int k = end + 1; k <= maxframe; ++k) {
                                        bw.write(sep + "0");
                                    }
                                    bw.write("\n");
                                }

                                ++cellsWritten;
                            }
                            ++linsWritten;
                        }
                    }
                }
            }
            fvd.dispose();
            bw.close();
            fw.close();
            IJ.log("Done, " + linsWritten + " lineages, and " + cellsWritten + " cells were exported.");
        } catch (IOException e) {
            IJ.log("Failed to write file!");
            e.printStackTrace();
        }
    }

    /**
     * Make JSON Object for a lineage
     * @param a the ancestral cell of the lineage
     * @param nlin the lineage number
     * @return
     */
    @SuppressWarnings("unchecked")
    private JSONObject makeLineageJSON(Cell a, int nlin) {
        double dt = exp.getFrameInterval();
        JSONArray cs = new JSONArray();
        JSONObject l = new JSONObject();

        l.put("dt", dt);
        // also remember the intensity value type
        l.put("mean_intensities", Prefs.getInt(".TrackApp.FilterValidatedDialog.exportMean", 1));

        ArrayList<Cell> cells = new ArrayList<Cell>();
        HashMap<Integer, String> cellNames = new HashMap<Integer, String>();
        JSONArray divns = new JSONArray();
        cells.add(a);
        getAllDaughters(a, cells, cellNames, null, divns);

        l.put("divisions", divns);

        int cellid = 1;
        HashMap<String, Integer> cellsToCellsInLin = new HashMap<String, Integer>();

        int minframe = -1;
        int maxframe = -1;

        for (Iterator<Cell> i = cells.iterator(); i.hasNext();) {
            Cell cell = (Cell) i.next();

            if (minframe < 0 || cell.getFrame() < minframe) {
                minframe = cell.getFrame();
            }
            int clf = findLastFrame(cell);
            if (maxframe < 0 || clf > maxframe) {
                maxframe = clf;
            }

            cellsToCellsInLin.put("" + cell.getCellID(), new Integer(cellid));
            ++cellid;
        }

        // make lineage time axis (minframe -> maxframe)
        JSONArray lintime = new JSONArray();
        for (int f = minframe; f <= maxframe; ++f) {
            lintime.add(new Double((f - 1) * dt));
        }
        l.put("time", lintime);

        cellid = 1;
        for (Iterator<Cell> i = cells.iterator(); i.hasNext();) {
            Cell cell = (Cell) i.next();
            JSONObject co = makeCellJSON(cell);
            co.put("lineage", new Integer(nlin));

            int parent = 0;
            // parent is first cell back for which we have an id, or 0
            Cell pcell = cell.getPreviousCell();
            Cell ppcell = pcell;
            int f = 0;
            while (pcell != null) {
                int id = pcell.getCellID();
                Integer pi = cellsToCellsInLin.get("" + id);
                if (pi != null) {
                    parent = pi.intValue();
                    break;
                }
                ppcell = pcell;
                pcell = pcell.getPreviousCell();
                if (f++ > maxframe) {
                    IJ.log("Failed to find parent for cell " + cell.getCellID());
                    break;
                }
            }
            if (cellid != 1 && parent == 0) {
                IJ.log("Error: parent is null within lineage!" + ppcell.toString());
            }

            co.put("parent", new Integer(parent));
            co.put("parent_in_lineage", new Integer(parent));

            co.put("lineage_id", new Integer(cellid));
            co.put("name", cellNames.get(cell.getCellID()));

            cs.add(co);
            ++cellid;
        }

        JSONObject o = new JSONObject();
        o.put("lin", l);
        o.put("cells", cs);

        return o;
    }

    /**
     * Make the time axis for a given cell
     * @param c
     * @return a JSON array containing all time values
     */
    @SuppressWarnings("unchecked")
    private JSONArray makeCellTimeAxis(Cell c) {
        JSONArray a = new JSONArray();

        while (c != null) {
            double t = (c.getFrame() - 1) * exp.getFrameInterval();
            a.add(t);
            c = c.getNextCell();
        }
        return a;
    }

    /**
     * Collect frame positions for a cell into a 2D-array
     * @param c
     * @return 2-D array with x and y positions
     */
    @SuppressWarnings("unchecked")
    private JSONArray makeCellPositions(Cell c) {
        JSONArray[] pos = new JSONArray[2];

        pos[0] = new JSONArray();
        pos[1] = new JSONArray();

        while (c != null) {
            double xx = c.getX();
            double yy = c.getY();

            pos[0].add(new Double(xx));
            pos[1].add(new Double(yy));

            c = c.getNextCell();
        }

        JSONArray a = new JSONArray();
        for (int i = 0; i < pos.length; i++) {
            a.add(pos[i]);
        }
        return a;
    }

    /**
     * Collect areas for cell in each frame
     * @param c
     * @return 1-D array with areas 
     */
    @SuppressWarnings("unchecked")
    private JSONArray makeCellAreas(Cell c) {
        JSONArray a = new JSONArray();

        while (c != null) {
            double ar = c.getArea();

            a.add(new Double(ar));

            c = c.getNextCell();
        }
        return a;
    }

    /**
     * Make the data array for a given cell
     * @param c
     * @return a JSON array containing all time values
     */
    @SuppressWarnings("unchecked")
    private JSONArray makeCellData(Cell c) {
        boolean meanInt = true;

        if (screen instanceof DisplayTrackingWindow) {
            int b = Prefs.getInt(".TrackApp.FilterValidatedDialog.exportMean", 1);
            meanInt = b != 0;
        }

        JSONArray[] q = new JSONArray[c.getIntensity().length];

        for (int i = 0; i < q.length; i++) {
            q[i] = new JSONArray();
        }

        while (c != null) {
            double[] ints = c.getIntensity();
            double ar = c.getArea();
            int lm = Math.min(q.length, ints.length);
            if (lm != ints.length) {
                IJ.log("Warning : number of channels varies, inconsistent cell dataset.");
            }
            for (int i = 0; i < ints.length; i++) {
                if (meanInt) {
                    q[i].add(new Double(ints[i] / ar));
                } else {
                    q[i].add(new Double(ints[i]));
                }
            }
            c = c.getNextCell();
        }
        JSONArray a = new JSONArray();
        for (int i = 0; i < q.length; i++) {
            a.add(q[i]);
        }
        return a;
    }

    /**
     * Make JSON object for a given cell
     * @param c the first cell
     * @return the JSON object for this cell
     */
    @SuppressWarnings("unchecked")
    private JSONObject makeCellJSON(Cell c) {
        JSONObject o = new JSONObject();
        o.put("dt", new Double(exp.getFrameInterval()));
        o.put("time", makeCellTimeAxis(c));
        o.put("start_index", c.getFrame());
        o.put("nseries", new Integer(c.getIntensity().length));
        o.put("data", makeCellData(c));
        o.put("frame_position", makeCellPositions(c));
        o.put("frame_area", makeCellAreas(c));
        o.put("description", c.toString());
        o.put("first_x", c.getX());
        o.put("first_y", c.getY());
        o.put("area", c.getArea());
        return o;
    }

    /**
     * Make a list of all daughter cells.
     * 
     * Also fix the lineage links.
     * 
     * @param a The ancestral cell
     * @param l a list of all daughter cells
     * @param names a map of cell names
     * @param pname the name prefix
     * @param divns JSONArray of division frames
     */
    @SuppressWarnings("unchecked")
    public static void getAllDaughters(Cell a, List<Cell> l, Map<Integer, String> names, String pname,
            JSONArray divns) {
        int k = 1;
        if (pname == null) {
            pname = "Cell 1";
        }
        names.put(new Integer(a.getCellID()), pname);
        Cell p = null;
        while (a != null) {
            // fix links
            if (p != null) {
                a.setPreviousCell(p);
                p.setNextCell(a);
            }
            Cell d = a.getDaughterCell();
            if (d != null) {
                // fix, just in case.
                d.setPreviousCell(a);
                divns.add(new Integer(a.getFrame() + 1));
                l.add(d);
                getAllDaughters(d, l, names, pname + "-" + k, divns);
                k++;
            }
            p = a;
            a = a.getNextCell();
        }
    }

    /**
     * Find the last frame for a cell
     * @param c
     * @return the last frame for this cell
     */
    public static int findLastFrame(Cell c) {
        int f = 1;
        while (c != null) {
            f = c.getFrame();
            c = c.getNextCell();
        }
        return f;
    }

}