Supplementary Software 2: BudJanalysis software

/*BudJ_

- Max cell radius (MaxCellRad), Max window for cell edge (MaxWinEdge)

and Min gray change at cell edge (PixelValRelMin) must be carefully

determined by user from the image stack before using BudJ

*/

import ij.*;

import ij.plugin.filter.*;

import ij.process.*;

import ij.gui.*;

import java.awt.event.*;

import java.awt.*;

import ij.measure.ResultsTable;

import ij.measure.Calibration;

import ij.measure.Measurements;

import ij.text.*;

import javax.swing.*;

public class BudJ_ implements PlugInFilter, MouseListener, KeyListener, ImageListener {

ImagePlus Img;

ImageCanvas Canvas;

ImageWindow ImgWin;

Overlay CurrentOverlay;

CellBJ CurrentCell;

String ImgName;

double ScaleFactor, TimeInt;

String ScaleUnit, TimeUnit;

int ImgWidth;

int ImgHeight;

int ChCnt;

int SliceCnt;

int TimeCnt;

int MaxCellRad; //To be set by user

int MaxWinEdge; //To be set by user

int EdgeRelMin; //To be set by user

double ClusterThresholdSD; //To be set by user

int FociRelMin; //To be set by user

int FociSizeMin; //To be set by user

int FociSizeMax; //To be set by user

int x;

int y;

int FirstX;

int FirstY;

int ClickTimeID, FirstClickTimeID, TimeID, MaxTimeID;

int ChID, BFChID, FL1ChID, FL2ChID, SliceID;

String[] FL1Option, FL2Option, TimeDirectionOption;

int FL1OpID, FL2OpID, TimeDirectionOpID;

ImageStatistics Stats;

double Bkg;

int[] CellCtr;

int CellGroup;

Color CurrentColor;

boolean NewCell;

boolean[] XBox;

ResultsTable CellData;

Panel TextPanel1, TextPanel2, ButtonPanel;

Label MessageLabel;

Button button1, button2, button3, button4, button5, button6, button7,button8;

Font ButtonFont, PressedButtonFont;

public int setup(String arg, ImagePlus Img) {

this.Img = Img;

return DOES_ALL+NO_CHANGES;

}

public void run(ImageProcessor ip) {

// Check whether BudJ is already running on image

String ImgName = Img.getTitle();

if (ImgName.length() > 7) {

String ImgTitleF7 = Img.getTitle().substring(0, 7);

if (ImgTitleF7.equals("BudJ on")) {

IJ.error("BudJ already running on this image!");

return;

}

}

Img.setTitle("BudJ on "+Img.getTitle());

// Get image scale

Calibration oc = Img.getCalibration().copy();

ScaleFactor = (float) oc.getX(1);

ScaleUnit=oc.getUnit();

if (ScaleFactor==0 | ScaleFactor==1 | ScaleUnit.equals("")) {

ScaleFactor=1;

ScaleUnit="pixel";

MaxCellRad=80;

MaxWinEdge=20;

} else {

MaxCellRad=4;

MaxWinEdge=1;

}

TimeInt = oc.frameInterval;

TimeUnit = oc.getTimeUnit();

if (TimeInt>30 & TimeUnit.equals("sec")) {

TimeInt=Math.round(TimeInt/60);

TimeUnit="min";

}

// Get image dimensions

int[] ImgDim = new int[5];

ImgDim = Img.getDimensions();

ImgWidth = ImgDim[0];

ImgHeight = ImgDim[1];

ChCnt = ImgDim[2];

SliceCnt = ImgDim[3];

TimeCnt = ImgDim[4];

// Guess BF channel

Bkg=0;

BFChID = 1;

for (int i = 1; i <= ChCnt; i++) {

Img.setPosition(i,1,1);

Img.setRoi(0,0,ImgWidth-1,ImgHeight-1);

Stats = Img.getStatistics();

if (Stats.dmode > Bkg) {

Bkg = Stats.dmode;

BFChID = i;

}

}

Img.killRoi();

for (int i = 1; i <= ChCnt; i++) {

if (i != BFChID) {

FL1ChID = i;

break;

}

}

for (int i = 1; i <= ChCnt; i++) {

if (i != BFChID & i != FL1ChID) {

FL2ChID = i;

break;

}

}

// Set other default option values

FL1OpID = 0;

FL2OpID = 0;

EdgeRelMin = 30;

ClusterThresholdSD = 2;

FociRelMin = 20;

FociSizeMin = 2;

FociSizeMax = 25;

MaxTimeID = TimeCnt;

TimeDirectionOpID = 0;

// Set user options

SetOptions();

// Panels for messages and buttons in image window

TextPanel1 = new Panel();

TextPanel1.add(new Label("Click cell to get data (hold SHIFT key to toggle groups 1 and 2)"));

MessageLabel=new Label("");

MessageLabel.setForeground(Color.RED);

Font MessageFont=new Font("SansSerif",Font.BOLD,16);

MessageLabel.setFont(MessageFont);

TextPanel1.add(MessageLabel);

TextPanel2 = new Panel();

TextPanel2.add(new Label("Use cursor keys to scroll through time"));

ButtonPanel = new Panel();

ButtonFont=new Font("SansSerif",Font.BOLD,12);

PressedButtonFont=new Font("SansSerif",Font.BOLD,18);

button1 = new Button(" New Cell ");

button1.setFont(ButtonFont);

button1.addMouseListener(this);

ButtonPanel.add(button1);

button2 = new Button(" 1 ");

button2.setForeground(Color.RED);

button2.setFont(PressedButtonFont);

button2.addMouseListener(this);

ButtonPanel.add(button2);

button3 = new Button(" 2 ");

button3.setForeground(Color.GREEN);

button3.setFont(ButtonFont);

button3.addMouseListener(this);

ButtonPanel.add(button3);

button4 = new Button(" 3 ");

button4.setForeground(Color.BLUE);

button4.setFont(ButtonFont);

button4.addMouseListener(this);

ButtonPanel.add(button4);

button5 = new Button(" 4 ");

button5.setForeground(Color.MAGENTA);

button5.setFont(ButtonFont);

button5.addMouseListener(this);

ButtonPanel.add(button5);

button6 = new Button(" Reset ");

button6.setFont(ButtonFont);

button6.addMouseListener(this);

ButtonPanel.add(button6);

button7 = new Button(" Set Data ");

button7.setFont(ButtonFont);

button7.addMouseListener(this);

ButtonPanel.add(button7);

button8 = new Button(" Exit ");

button8.setFont(ButtonFont);

button8.addMouseListener(this);

ButtonPanel.add(button8);

ImgWin = Img.getWindow();

ImgWin.add(TextPanel1);

ImgWin.add(TextPanel2);

ImgWin.add(ButtonPanel);

// ImgWin.setLocation(10,10);

ImgWin.pack();

ImgWin.setVisible(true);

// Enable Listeners

Canvas = ImgWin.getCanvas();

ImgWin.removeKeyListener(IJ.getInstance());

Canvas.removeKeyListener(IJ.getInstance());

ImgWin.addKeyListener(this);

Canvas.addKeyListener(this);

Canvas.addMouseListener(this);

Img.addImageListener(this);

Canvas.requestFocus();

}

protected void SetOptions() {

// Initialize FL options

FL1Option = new String[3];

FL1Option[0] = "Expression level";

FL1Option[1] = "Cluster index";

FL1Option[2] = "Foci data";

FL2Option = new String[3];

FL2Option[0] = "Expression level";

FL2Option[1] = "Cluster index";

FL2Option[2] = "Foci data";

TimeDirectionOption = new String[2];

TimeDirectionOption[0] = "Forward";

TimeDirectionOption[1] = "Reverse";

// Show initial dialog box

GenericDialog BudJDialog=new GenericDialog("BudJ dialog box");

Font HeadingFont=new Font("SansSerif",Font.BOLD,12);

BudJDialog.addMessage("BF channel parameters:",HeadingFont);

BudJDialog.addNumericField("BF channel",BFChID,0,2,"");

BudJDialog.addNumericField("Scale factor",ScaleFactor,4,6,ScaleUnit+"/pixel");

BudJDialog.addNumericField("Max cell radius",MaxCellRad,0,2,ScaleUnit);

BudJDialog.addNumericField("Max window for cell edge",MaxWinEdge,0,2,ScaleUnit);

BudJDialog.addNumericField(" Min gray change at cell edge",EdgeRelMin,0,2,"%");

if (ChCnt>1) {

BudJDialog.addMessage("Fluorescence channel 1 parameters:",HeadingFont);

BudJDialog.addNumericField("FL1 channel (0 to disable)",FL1ChID,0,2,"");

BudJDialog.addChoice("FL1 data",FL1Option,FL1Option[FL1OpID]);

if (ChCnt>2) {

BudJDialog.addMessage("Fluorescence channel 2 parameters:",HeadingFont);

BudJDialog.addNumericField("FL2 channel (0 to disable)",FL2ChID,0,2,"");

BudJDialog.addChoice("FL2 data",FL2Option,FL2Option[FL2OpID]);

}

BudJDialog.addMessage("Cluster and foci parameters:",HeadingFont);

BudJDialog.addNumericField("Cluster threshold",ClusterThresholdSD ,1,2,"sdev units");

BudJDialog.addNumericField("Min FL change at foci",FociRelMin,0,2,"%");

BudJDialog.addNumericField("Min foci size",FociSizeMin,0,2,"pixels");

BudJDialog.addNumericField("Max foci size",FociSizeMax,0,2,"pixels");

}

if (TimeCnt>1) {

BudJDialog.addMessage("Time lapse parameters:",HeadingFont);

BudJDialog.addNumericField("Time points to analyze",MaxTimeID,0,2,"");

BudJDialog.addStringField("Time unit", TimeUnit, 3);

BudJDialog.addNumericField("Time interval",TimeInt,0,2,"time units");

BudJDialog.addChoice("Time direction",TimeDirectionOption,TimeDirectionOption[TimeDirectionOpID]);

}

BudJDialog.hideCancelButton();

BudJDialog.showDialog();

// Get parameters set by user

BFChID = (int) BudJDialog.getNextNumber();

ScaleFactor = BudJDialog.getNextNumber();

MaxCellRad = (int) BudJDialog.getNextNumber();

MaxWinEdge = (int) BudJDialog.getNextNumber();

EdgeRelMin = (int) BudJDialog.getNextNumber();

if (ChCnt>1) {

FL1ChID = (int) BudJDialog.getNextNumber();

FL1OpID = BudJDialog.getNextChoiceIndex();

if (ChCnt>2) {

FL2ChID = (int) BudJDialog.getNextNumber();

FL2OpID = BudJDialog.getNextChoiceIndex();

} else {

FL2ChID = 0;

FL2OpID = 0;

}

ClusterThresholdSD = BudJDialog.getNextNumber();

FociRelMin = (int) BudJDialog.getNextNumber();

FociSizeMin = (int) BudJDialog.getNextNumber();

FociSizeMax = (int) BudJDialog.getNextNumber();

} else {

FL1ChID = 0;

FL1OpID = 0;

}

if (TimeCnt>1) {

MaxTimeID = (int) BudJDialog.getNextNumber();

TimeUnit = BudJDialog.getNextString();

TimeInt = BudJDialog.getNextNumber();

TimeDirectionOpID = BudJDialog.getNextChoiceIndex();

} else {

MaxTimeID = TimeCnt;

TimeDirectionOpID = 0;

}

// Initialize other parameters

FirstClickTimeID=1;

SliceID = 1;

CellCtr = new int[5];

for (int i=1; i<=4; i++) CellCtr[i] = 0;

NewCell = true;

CellGroup=1;

CurrentColor = Color.red;

CurrentOverlay = new Overlay();

Img.setOverlay(CurrentOverlay);

Img.setPosition(BFChID,SliceID,FirstClickTimeID);

// Initialize CellBJ object

CurrentCell=new CellBJ();

CurrentCell.Img=Img;

CurrentCell.Canvas=Canvas;

CurrentCell.MaxCellRad=MaxCellRad;

CurrentCell.MaxWinEdge=MaxWinEdge;

CurrentCell.EdgeRelMin=EdgeRelMin;

CurrentCell.ClusterThresholdSD = ClusterThresholdSD;

CurrentCell.FociRelMin = FociRelMin;

CurrentCell.FociSizeMin = FociSizeMin;

CurrentCell.FociSizeMax = FociSizeMax;

CurrentCell.ScaleFactor=ScaleFactor;

CurrentCell.ScaleUnit=ScaleUnit;

CurrentCell.ImgWidth=ImgWidth;

CurrentCell.ImgHeight=ImgHeight;

CurrentCell.ChCnt=ChCnt;

CurrentCell.BFChID=BFChID;

CurrentCell.FL1ChID=FL1ChID;

CurrentCell.FL2ChID=FL2ChID;

CurrentCell.FL1OpID=FL1OpID;

CurrentCell.FL2OpID=FL2OpID;

// Set data measurements

XBox = new boolean[30];

for (int i = 0; i < 30; i++) XBox[i]=true;

SetData();

}

protected void SetData() {

// Show data dialog box

GenericDialog DataDialog=new GenericDialog("Data dialog box");

Font HeadingFont=new Font("SansSerif",Font.BOLD,12);

DataDialog.addCheckbox("cell group",XBox[0]);

DataDialog.addCheckbox("cell #",XBox[1]);

DataDialog.addCheckbox("time frame #",XBox[2]);

DataDialog.addCheckbox("time ("+TimeUnit+")",XBox[3]);

DataDialog.addCheckbox("centroid X",XBox[4]);

DataDialog.addCheckbox("centroid Y",XBox[5]);

DataDialog.addCheckbox("major radius",XBox[6]);

DataDialog.addCheckbox("minor radius",XBox[7]);

DataDialog.addCheckbox("ellipse angle",XBox[8]);

DataDialog.addCheckbox("edge index",XBox[9]);

DataDialog.addCheckbox("cell volume",XBox[10]);

if (FL1ChID>0) {

switch (FL1OpID) {

case 0:

DataDialog.addCheckbox("FL1 FL stats",XBox[11]);

break;

case 1:

DataDialog.addCheckbox("FL1 cluster index",XBox[12]);

DataDialog.addCheckbox("FL1 cluster FL stats",XBox[13]);

break;

case 2:

DataDialog.addCheckbox("FL1 foci",XBox[14]);

DataDialog.addCheckbox("FL1 doublets",XBox[15]);

DataDialog.addCheckbox("FL1 foci size",XBox[16]);

DataDialog.addCheckbox("FL1 foci FL stats",XBox[17]);

break;

}

}

if (FL2ChID>0) {

switch (FL2OpID) {

case 0:

DataDialog.addCheckbox("FL2 FL stats",XBox[21]);

break;

case 1:

DataDialog.addCheckbox("FL2 cluster index",XBox[22]);

DataDialog.addCheckbox("FL2 cluster FL stats",XBox[23]);

break;

case 2:

DataDialog.addCheckbox("FL2 foci",XBox[24]);

DataDialog.addCheckbox("FL2 doublets",XBox[25]);

DataDialog.addCheckbox("FL2 foci size",XBox[26]);

DataDialog.addCheckbox("FL2 foci FL stats",XBox[27]);

break;

}

}

DataDialog.hideCancelButton();

DataDialog.showDialog();

for (int i = 0; i <= 10; i++) XBox[i]=DataDialog.getNextBoolean();

if (FL1ChID>0) {

switch (FL1OpID) {

case 0:

XBox[11]=DataDialog.getNextBoolean();

break;

case 1:

for (int i = 12; i <=13; i++) XBox[i]=DataDialog.getNextBoolean();

break;

case 2:

for (int i = 14; i <=17; i++) XBox[i]=DataDialog.getNextBoolean();

break;

}

}

if (FL2ChID>0) {

switch (FL2OpID) {

case 0:

XBox[21]=DataDialog.getNextBoolean();

break;

case 1:

for (int i = 22; i <=23; i++) XBox[i]=DataDialog.getNextBoolean();

break;

case 2:

for (int i = 24; i <=27; i++) XBox[i]=DataDialog.getNextBoolean();

break;

}

}

// Prepare results table

CellData = new ResultsTable();

CellData.reset();

CellData.setPrecision(2);

}

// To implement the interface we have to implement the five methods it declares. We only want to react on clicks so we can leave the others empty.

// We get the coordinates of the point of the mouse click from the event object that is passed to the method.

// The image could be scaled in the window so we use the offScreenX() and offScreenY() method of ImageCanvas to receive the true coordinates.

public void mouseClicked(MouseEvent e) {

MessageLabel.setText("");

ImgWin.pack();

Object b = e.getSource();

if (b==button1) {

NewCell=true;

Img.setPosition(BFChID,SliceID,FirstClickTimeID);

Canvas.requestFocus();

return;

}

if (b==button2) {

NewCell=true;

Img.setPosition(BFChID,SliceID,FirstClickTimeID);

CellGroup=1;

CurrentColor = Color.red;

button2.setFont(PressedButtonFont);

button3.setFont(ButtonFont);

button4.setFont(ButtonFont);

button5.setFont(ButtonFont);

ImgWin.pack();

Canvas.requestFocus();

return;

}

if (b==button3) {

NewCell=true;

Img.setPosition(BFChID,SliceID,FirstClickTimeID);

CellGroup=2;

CurrentColor = Color.green;

button2.setFont(ButtonFont);

button3.setFont(PressedButtonFont);

button4.setFont(ButtonFont);

button5.setFont(ButtonFont);

ImgWin.pack();

Canvas.requestFocus();

return;

}

if (b==button4) {

NewCell=true;

Img.setPosition(BFChID,SliceID,FirstClickTimeID);

CellGroup=3;

CurrentColor = Color.blue;

button2.setFont(ButtonFont);

button3.setFont(ButtonFont);

button4.setFont(PressedButtonFont);

button5.setFont(ButtonFont);

ImgWin.pack();

Canvas.requestFocus();

return;

}

if (b==button5) {

NewCell=true;

Img.setPosition(BFChID,SliceID,FirstClickTimeID);

CellGroup=4;

CurrentColor = Color.magenta;

button2.setFont(ButtonFont);

button3.setFont(ButtonFont);

button4.setFont(ButtonFont);

button5.setFont(PressedButtonFont);

ImgWin.pack();

Canvas.requestFocus();

return;

}

if (b==button6) {

if (IJ.showMessageWithCancel("BudJ","Click OK to re-initiate BudJ on this image")) {

SetOptions();

}

Canvas.requestFocus();

return;

}

if (b==button7) {

if (IJ.showMessageWithCancel("BudJ","Click OK to set measurement data on this image")) {

SetData();

}

Canvas.requestFocus();

return;

}

if (b==button8) {

if (IJ.showMessageWithCancel("BudJ","Click OK to exit BudJ")) {

// Disable Listeners, close windows and exit

Canvas.removeMouseListener(this);

Canvas.removeKeyListener(this);

ImgWin.removeKeyListener(this);

Img.close();

}

Canvas.requestFocus();

return;

}

if (e.isShiftDown()) {

NewCell=true;

if (CellGroup==1) {

CellGroup=2;

CurrentColor = Color.green;

button1.setFont(ButtonFont);

button2.setFont(PressedButtonFont);

button3.setFont(ButtonFont);

button4.setFont(ButtonFont);

ImgWin.pack();

} else {

CellGroup=1;

CurrentColor = Color.red;

button1.setFont(PressedButtonFont);

button2.setFont(ButtonFont);

button3.setFont(ButtonFont);

button4.setFont(ButtonFont);

ImgWin.pack();

}

}

Canvas.requestFocus();

// Do not go on if zoom tool

if (Toolbar.getToolId()==11) return;

x = e.getX();

y = e.getY();

FirstX = Canvas.offScreenX(x);

FirstY = Canvas.offScreenY(y);

ClickTimeID = Img.getFrame();

// Obtain centroid coordinates of first TimeID

Img.setPosition(BFChID,SliceID,ClickTimeID);

CurrentCell.FirstX=FirstX;

CurrentCell.FirstY=FirstY;

CurrentCell.TimeID=ClickTimeID;

CurrentCell.GetData=false;

CurrentCell.GetCellData();

if (CurrentCell.CellFound) {

FirstX = (int) (CurrentCell.XCentroid/ScaleFactor);

FirstY = (int) (CurrentCell.YCentroid/ScaleFactor);

} else {

MessageLabel.setText("Cell not found!");

ImgWin.pack();

return;

}

TimeID=ClickTimeID;

boolean TimeOK=true;

while (TimeOK) {

// Initialize variable parameters of CellBJ object

CurrentCell.FirstX=FirstX;

CurrentCell.FirstY=FirstY;

CurrentCell.TimeID=TimeID;

CurrentCell.GetData=true;

CurrentCell.GetCellData();

if (CurrentCell.CellFound) {

// Plot ROI and trail

if (NewCell) {

CellCtr[CellGroup] = CellCtr[CellGroup] + 1;

NewCell = false;

FirstClickTimeID = ClickTimeID;

CurrentCell.EllipseRoi.setStrokeColor(CurrentColor);

CurrentOverlay.add (CurrentCell.EllipseRoi);

String CellCtrStr=Integer.toString(CellCtr[CellGroup]);

Font RoiFont=new Font("SansSerif",Font.BOLD,16);

TextRoi RoiLabel=new TextRoi((int) (CurrentCell.XCentroid/ScaleFactor),

(int)(CurrentCell.YCentroid/ScaleFactor),CellCtrStr,RoiFont);

RoiLabel.setStrokeColor(CurrentColor);

CurrentOverlay.add(RoiLabel);

} else {

Line CurrentLine=new Line(FirstX,FirstY,(int) (CurrentCell.XCentroid/ScaleFactor),

(int) (CurrentCell.YCentroid/ScaleFactor));;

CurrentLine.setStrokeColor(CurrentColor);

CurrentOverlay.add(CurrentLine);

}

Img.updateAndDraw();

FirstX = (int) (CurrentCell.XCentroid/ScaleFactor);

FirstY = (int) (CurrentCell.YCentroid/ScaleFactor);

// Add data to table

CellData.incrementCounter();

if (XBox[0]) CellData.addValue("Group",CellGroup);

if (XBox[1]) CellData.addValue("Cell",CellCtr[CellGroup]);

if (XBox[2]) CellData.addValue("TimeID",TimeID);

if (XBox[3]) CellData.addValue("time ("+TimeUnit+")",TimeInt*(TimeID-1));

if (XBox[4]) CellData.addValue("x",CurrentCell.XCentroid);

if (XBox[5]) CellData.addValue("y",CurrentCell.YCentroid);

if (XBox[6]) CellData.addValue("major R",CurrentCell.Major);

if (XBox[7]) CellData.addValue("minor r",CurrentCell.Minor);

if (XBox[8]) CellData.addValue("angle",CurrentCell.Angle);

if (XBox[9]) CellData.addValue("edge",CurrentCell.Edge);

if (XBox[10]) CellData.addValue("volume",CurrentCell.Volume);

if (FL1ChID>0) {

switch (FL1OpID) {

case 0:

if (XBox[11]) {

CellData.addValue("FL1 FL mean",CurrentCell.FL1Mean);

CellData.addValue("FL1 FL sdev",CurrentCell.FL1SDev);

}

break;

case 1:

if (XBox[12]) CellData.addValue("FL1 cluster index",CurrentCell.FL1Cluster);

if (XBox[13]) {

CellData.addValue("FL1 cluster FL mean",CurrentCell.FL1ClusterMean);

CellData.addValue("FL1 cluster FL sdev",CurrentCell.FL1ClusterSDev);

}

break;

case 2:

if (XBox[14]) CellData.addValue("FL1 foci",CurrentCell.FL1Foci);

if (XBox[15]) CellData.addValue("FL1 doublets",CurrentCell.FL1FociDoublets);

if (XBox[16]) {

CellData.addValue("FL1 foci size mean",CurrentCell.FL1FociSizeMean);

CellData.addValue("FL1 foci size sdev",CurrentCell.FL1FociSizeSDev);

}

if (XBox[17]) {

CellData.addValue("FL1 foci FL mean",CurrentCell.FL1FociMean);

CellData.addValue("FL1 foci FL sdev",CurrentCell.FL1FociSDev);

}

break;

}

}

if (FL2ChID>0) {

switch (FL2OpID) {

case 0:

if (XBox[21]) {

CellData.addValue("FL2 FL mean",CurrentCell.FL2Mean);

CellData.addValue("FL2 FL sdev",CurrentCell.FL2SDev);

}

break;

case 1:

if (XBox[22]) CellData.addValue("FL2 cluster index",CurrentCell.FL2Cluster);

if (XBox[23]) {

CellData.addValue("FL2 cluster FL mean",CurrentCell.FL2ClusterMean);

CellData.addValue("FL2 cluster FL sdev",CurrentCell.FL2ClusterSDev);

}

break;

case 2:

if (XBox[24]) CellData.addValue("FL2 foci",CurrentCell.FL2Foci);

if (XBox[25]) CellData.addValue("FL2 doublets",CurrentCell.FL2FociDoublets);

if (XBox[26]) {

CellData.addValue("FL2 foci size mean",CurrentCell.FL2FociSizeMean);

CellData.addValue("FL2 foci size sdev",CurrentCell.FL2FociSizeSDev);

}

if (XBox[27]) {

CellData.addValue("FL2 foci FL mean",CurrentCell.FL2FociMean);

CellData.addValue("FL2 foci FL sdev",CurrentCell.FL2FociSDev);

}

break;

}

}

CellData.show(Img.getTitle()+" Results");

} else {

if (NewCell) MessageLabel.setText("Cell not found!");

else MessageLabel.setText("Cell lost at frame "+TimeID);

ImgWin.pack();

Canvas.requestFocus();

return;

}

if (TimeDirectionOpID==0) {

TimeID++;

if (TimeID >= FirstClickTimeID+MaxTimeID | TimeID > TimeCnt) TimeOK = false;

} else {

TimeID = TimeID - 1;

if (TimeID <= FirstClickTimeID-MaxTimeID | TimeID < 1) TimeOK = false;

}

}

NewCell = true;

Img.setPosition(BFChID,SliceID,FirstClickTimeID);

Canvas.requestFocus();

}

public void mousePressed(MouseEvent e) {}

public void mouseReleased(MouseEvent e) {}

public void mouseEntered(MouseEvent e) {}

public void mouseExited(MouseEvent e) {}

public void keyPressed(KeyEvent e) {

TimeID = Img.getFrame();

ChID = Img.getChannel();

int keyCode = e.getKeyCode();

switch (keyCode) {

case 37:

if (TimeID > 0) TimeID = TimeID - 1;

Img.setPosition(ChID,SliceID,TimeID);

break;

case 39:

if (TimeID < TimeCnt) TimeID = TimeID + 1;

Img.setPosition(ChID,SliceID,TimeID);

break;

}

}

public void keyReleased(KeyEvent e) {}

public void keyTyped(KeyEvent e) {}

public void imageClosed(ImagePlus Img) {

Canvas.removeMouseListener(this);

Canvas.removeKeyListener(this);

ImgWin.removeKeyListener(this);

Img.removeImageListener(this);

}

public void imageUpdated(ImagePlus Img) {}

public void imageOpened(ImagePlus Img) {}

}

//CellBJ class

import ij.*;

import ij.plugin.filter.*;

import ij.process.*;

import ij.gui.*;

import java.awt.event.*;

import java.awt.*;

import ij.measure.ResultsTable;

import ij.measure.Calibration;

import ij.measure.Measurements;

import ij.text.TextWindow;

public class CellBJ {

// Required parameters

public ImagePlus Img;

public ImageCanvas Canvas;

public int MaxCellRad;

public int MaxWinEdge;

public int EdgeRelMin;

public double ClusterThresholdSD;

public int FociRelMin;

public int FociSizeMin;

public int FociSizeMax;

public int FirstX, FirstY;

public int ImgWidth, ImgHeight;

public double ScaleFactor;

public String ScaleUnit;

public int TimeID, BFChID;

public int FL1ChID, FL2ChID;

public int FL1OpID, FL2OpID;

public int ChCnt;

public boolean GetData;

// Produced parameters

public boolean CellFound;

public Roi ItsRoi;

public Roi EllipseRoi;

public double XCentroid;

public double YCentroid;

public double Major;

public double Minor;

public double Angle;

public double Edge;

public double Volume;

public double FL1Mean, FL2Mean;

public double FL1SDev, FL2SDev;

public double FL1Cluster, FL2Cluster;

public double FL1ClusterMean, FL2ClusterMean;

public double FL1ClusterSDev, FL2ClusterSDev;

public double FL1Foci, FL2Foci;

public double FL1FociMean, FL2FociMean;

public double FL1FociSDev, FL2FociSDev;

public double FL1FociSizeMean, FL2FociSizeMean;

public double FL1FociSizeSDev, FL2FociSizeSDev;

public double FL1FociDoublets, FL2FociDoublets;

// Other variables and objects

int MaxInc;

int MaxWinInc;

double PI;

int VectorAngle;

int PixelFoundCtr;

boolean[] PixelFound;

int[] FoundX;

int[] FoundY;

int[] FoundRad;

int[] FoundDif;

int[] FoundPtrDif;

int[] FoundSlope;

int[] ThisPixelValue;

int[] VectorX;

int[] VectorY;

int[] VectorPixelValue;

int Inc, WinInc;

int LimitPtr, CurrentMaxPtr, CurrentMinPtr, PtrDif;

int MaxDif, CurrentDif;

int PixelValRelDif;

int MeanPixelValue;

double MeanRad, SDevRad;

double MeanDif, SDevDif;

double MeanPtrDif;

double MeanSlope, SDevSlope;

int WinFoundCtr, PixelJumpedCtr;

Polygon CurrentPolygon;

ImageStatistics Stats;

Ellipse CurrentEllipse;

double alpha, beta;

double sinalpha, cosalpha;

double sinbeta, cosbeta;

Rectangle EllipseBounds;

double BFBkg, FLBkg;

int MaxPixelArea;

int[] DensPixelValue;

int XCoor, YCoor, XMin, YMin, XMax, YMax;

int SumX, SumY;

int MeanX, MeanY;

double MeanDoubleDist;

int XDif, YDif;

public void GetCellData() {

// Select subimage

Img.setPosition(BFChID,1,TimeID);

// Initialize used parameters

MaxInc = (int) Math.round(MaxCellRad/ScaleFactor);

MaxWinInc = (int) Math.round(MaxWinEdge/ScaleFactor);

PI=Math.PI;

// Create arrays

VectorX = new int[MaxInc+1];

VectorY = new int[MaxInc+1];

VectorPixelValue = new int[MaxInc+1];

FoundX = new int[360];

FoundY = new int[360];

FoundRad = new int[360];

FoundDif = new int[360];

FoundPtrDif = new int[360];

FoundSlope = new int[360];

PixelFound = new boolean[360];

ThisPixelValue = new int[4];

CellFound = true;

PixelFoundCtr = 0;

// Get background value for BFCh

Img.setRoi(0,0,ImgWidth-1,ImgHeight-1);

Stats = Img.getStatistics();

BFBkg=Stats.dmode;