Plugin Architecture
LineageTracker can be extended using plugins. There are three different classes of plugin: Segmentation, Analysis and Tracking. This page briefly describes the three types and gives a simple example.
Compiling and Installing Plugins
LineageTracker will detect any suitable plugins which are present within the ImageJ plugins directory, or any plugins which are within a 'jar' file providing the word 'Tracker' is somewhere within the file name.
To compile a plugin, Java 1.6 is required and the ij.jar and LineageTracker.jar files need to be somewhere on the classpath to be visible to the compiler. For example:
javac -cp "/Users/mike/java/libraries/*" CountCells.java
If the jar files are in the library directory, the above command will compile the Count Cells plugin (see below).
Analysis Plugins
These plugins can analyse and export data, or draw onto the view window to highlight or label cells. The plugins must extend the 'AnalysisPlugin' class, which provides one overrideable method and 4 abstract methods:
package analysers;
import GUI.Image5DWithOverlay;
import ij.gui.Roi;
import java.util.List;
import java.util.Map;
import util.Cell;
import util.ExptData;
public abstract class AnalysisPlugin {
protected ExptData exp;
protected List<Cell> cells;
protected Image5DWithOverlay screen;
protected Map<Integer, List<Roi>> roiLists;
/**
* Called when the analysis plugins are loaded, prior to calling any of the analyse methods.
*
* @param exp
* @param cells
* @param screen
* @param roiLists
*/
public void setup(ExptData exp, List<Cell> cells, Image5DWithOverlay screen, Map<Integer, List<Roi>> roiLists) {
this.exp = exp;
this.cells = cells;
this.screen = screen;
this.roiLists = roiLists;
}
// Obtain information from the user, possibly via dialog box.
public abstract void setup();
// Called when the button is clicked in the control panel
public abstract void analyze(Cell currentCell);
// Called every time a cell is clicked on in the window
public abstract void cellClicked(Cell currentCell);
// Returns the name which appears in the menu, or null to disable the plugin
public abstract String getName();
}
A simple plugin to count the number of cells in each frame needs to import and extend the AnalysisPlugin class:
import analysers.AnalysisPlugin;
import ij.IJ;
import util.Cell;
/**
* @version March 21, 2011
* @author Mike Downey
*/
public class CountCells extends AnalysisPlugin {
int[] cellsInFrame;
/**
* Called when the 'Setup Analysis' button is clicked
*/
@Override
public void setup() {
IJ.showMessage("Displays numbers of cells in each frame");
}
@Override
public void analyze(Cell currentCell) {
countCells();
IJ.log("Frame, Cells");
for (int i = 0; i < cellsInFrame.length; i++) {
IJ.log(Integer.toString(i + 1) + " , " + cellsInFrame[i]);
}
}
private void countCells() {
cellsInFrame = new int[exp.getFrames()];
for (Cell c : cells) {
cellsInFrame[c.getFrame() - 1]++;
}
}
@Override
public void cellClicked(Cell currentCell) {
int frame = currentCell.getFrame();
countCells();
IJ.log("Frame " + frame + " contains " + cellsInFrame[frame - 1] + " cells");
}
@Override
public String getName() {
return "Count Cells";
}
}
A plugin to display the Cell ID alongside each cell in the frame. This demonstrates use of the Image5DWithOverlay class which handles the screen output:
import analysers.AnalysisPlugin;
import ij.gui.GenericDialog;
import java.awt.Color;
import util.Cell;
/**
* Plot the cell number alongside each cell
*
* @version 04-May-2011
* @author Mike Downey
*/
public class NumberCells extends AnalysisPlugin {
double fontSize=12;
@Override
public void setup() {
GenericDialog gd = new GenericDialog("Select Text Size");
gd.addNumericField("Size in Pixels", fontSize, 0);
gd.showDialog();
if(gd.wasCanceled())
return;
if(gd.wasOKed()){
fontSize = gd.getNextNumber();
}
}
@Override
public void analyze(Cell currentCell) {
for(Cell c: cells)
cellClicked(c);
}
/**
* Plots the cell number alongside the cell.
*
* @param currentCell
*/
@Override
public void cellClicked(Cell currentCell) {
if(screen!=null){
screen.plotNumber(currentCell.getFrame(), currentCell.getX(), currentCell.getY(),
currentCell.getCellID(), fontSize/10, Color.white, false);
}
}
@Override
public String getName() {
return("Draw Cell Numbers");
}
}
Details of the Cell & Image5DWithOverlay classes will be published soon.
Tracking Plugins
Tracking or lineage construction plugins need to extend the AbstractTracker class. This provides 2 abstract methods and several overrideable methods:
public abstract List<Cell> run() throws IOException;
public abstract String getName();
// Override this (to return null) in methods which do not use parameters.
public TrackingParameters getTrackingParameters() {
return tp;
}
// If desired, the overriding method can print the tracking parameters.
public String getDetails() {
return "";
}
/**
* Passed a File array which represents available tracking files. Returns a boolean
* stating whether it is possible to run the tracking algorithm. Any plugins
* which depend on loading a file instead of running tracking will need to
* examine the files to see if any are suitable.
*
* @param f
* @return whether it is possible to run/load the selected tracking
*/
public boolean trackable(File[] filesinDir) {
return true;
}
A simple tracking plugin is given below.
import util.Cell;
import util.GenericDataLoader;
import java.io.IOException;
import java.util.List;
import tracking.AbstractTracker;
import tracking.TrackedCell;
import tracking.TrackingParameters;
/**
* Simple-minded tracking.
* Locates the nearest cell in the next frame and if it hasn't already been
* assigned into a trajectory, add it to the current cell.
*
* Compile with, e.g.
* javac -cp /path/to/jars/ij.jar:/path/to/jars/LineageTracker_1.0.2.jar SimpleNearest.java
*
* @author Mike Downey
* @version 16 Feb 2011
*/
public class SimpleNearest extends AbstractTracker{
@Override
public List<Cell> run() throws IOException {
// Load in the segmented cell data
cellsToTrack = GenericDataLoader.loadData(expLoader);
int storeOld = TrackedCell.maximumCellSeparation;
TrackedCell.maximumCellSeparation=50;
for(Cell c : cellsToTrack){
int f = c.getFrame();
if(f<expLoader.getFrames()){
Cell nearest = TrackedCell.findNearestCell(f+1, c.getX(), c.getY(), cellsToTrack);
if(nearest!=null && nearest.getPreviousCell()==null)
c.setNextCell(nearest);
}
}
TrackedCell.maximumCellSeparation = storeOld;
return cellsToTrack;
}
@Override
// Method does not use tracking parameters so return null.
public TrackingParameters getTrackingParameters() {
return null;
}
@Override
public String getName() {
return "Simple Nearest Cell";
}
}
Segmentation Plugins
Any segmentation plugins must extend the abstract class ProcessingPlugin. The objective of the segmentation plugin is to receive images and return a segmentation mask (either binary or using pixel value to represent individual cells).
package segmentation;
import ij.ImagePlus;
import ij.process.ImageProcessor;
import java.awt.Frame;
import java.io.Serializable;
/**
* Plugin for the Segmentation software, to allow different segmentation methods to
* be implemented without having to modify the original program.
*
* An additional parameter is returned to specify whether the returned cell
* mask is 'colour coded' where pixel value = cell number (which allows areas to
* be touching), or whether it is simply a binary image.
*
* The methods are run in the following order:
* imageToSegment();
* runSegmentation();
* cellMask()
* colourCoded()
*
* @author Mike
* @version 10/02/2011
*/
public abstract class ProcessingPlugin implements Serializable, Cloneable{
/**
* Obtain the name of the segmentation method.
*
* @return null if plugin is to be ignored.
*/
public abstract String getName();
/**
* Sets the ImageProcessor which holds the cell image to segment
* @param img
*/
public abstract void imageToSegment(ImageProcessor img);
/**
* Obtain the cell mask produced by the segmentation
* @return
*/
public abstract ImagePlus cellMask();
/**
* Does the cell mask use intensity to determine cell number (true) or
* is it a binary image (false)
* @return
*/
public abstract boolean colourCoded();
/**
* Run the segmentation method and obtain the segmentation mask.
*/
public abstract void runSegmentation(int frameNo);
public abstract void editSettings(Frame parentWindow);
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
A simple plugin, to perform a Threshold segmentation, is given below:
package segmentation;
import ij.IJ;
import ij.ImagePlus;
import ij.process.ImageProcessor;
import java.awt.Frame;
/**
* Simple example of writing a Processing Plugin.
*
* @version 14-Jan-2011
* @author Mike Downey
*/
public class ThresholdSegmentation extends ProcessingPlugin{
ImageProcessor toSegment;
ImagePlus mask;
@Override
public void imageToSegment(ImageProcessor img) {
toSegment = img;
}
@Override
public ImagePlus cellMask() {
return mask;
}
@Override
public boolean colourCoded() {
return false;
}
@Override
public void runSegmentation(int frameNo) {
// Take the ImageProcessor and convert into a binary image.
ImagePlus imp = new ImagePlus("temp",toSegment);
IJ.setAutoThreshold(imp, "Li dark");
IJ.run(imp, "Convert to Mask", "");
mask = imp;
}
@Override
public String getName() {
return "Auto Threshold";
}
@Override
public void editSettings(Frame parentWindow) {
IJ.showMessage("No settings to edit.");
}
}