import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.*;
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.plugin.*;
import ij.plugin.filter.Analyzer;
import ij.plugin.frame.PlugInFrame;
import ij.measure.*;
import ij.util.*;
/* This plugin is for the analysis of FRAP experiments.
* The plugin lets you choose the starting slice corresponding to the slice where the cell
* has been hitted and defines automatically a ROI corresponding to the zone hitted by the
* laser and plots the corresponding intensity profile.
*
* Given that the plugin is looking for the barycenter of the hitted spot, it can only be
* applied for FRAP experiments with a single spot.
* It able also to determine the intensity of the rest of the cell in order to normalize
* the FRAP measurements.
*
* Last updated: 04-13-2009
* author : Philippe Carl (phcarl@free.fr)
*/
public final class FRAP_Analysis extends PlugInFrame implements ActionListener, ChangeListener, Measurements
{
ImagePlus imp, imp1, imp2, imp3;
Roi roi;
ImageStack stack;
ImageProcessor ip, ip3;
JTextField startSlice;
JButton startButton, goButton, normalizeButton, doneButton;
JSlider maxLevelSlider;
JLabel maxLevelLabel;
static final boolean DEBUG = false;
String title;
int xcen, ycen;
double min_level;
double max_level;
double image_min;
double image_max;
public FRAP_Analysis()
{
super("FRAP_Analysis");
if (DEBUG)
IJ.log("FRAP_Analysis.<init>(): initialization happening");
FRAP_initialize();
FRAP_makeWindow();
}
@Override
public void windowActivated(WindowEvent e)
{
if (DEBUG)
IJ.log("FRAP_Analysis.windowActivated(" + e.toString() + ")");
super.windowActivated(e);
/* re-initialize if the current image has changed */
if (WindowManager.getCurrentImage() != imp)
{
FRAP_initialize();
FRAP_remakeWindow();
}
}
public void run(ImageProcessor ip)
{
if ( DEBUG ) IJ.log("FRAP_Analysis.run(ImageProcessor)...");
FRAP_initialize();
FRAP_makeWindow();
}
public void FRAP_initialize()
{
if ( DEBUG ) IJ.log("Cell_Outliner.initialize...");
imp = WindowManager.getCurrentImage();
stack = imp.getStack();
if (stack == null)
{
IJ.error("This plugin only works on image stacks, not single frames.");
return;
}
roi = imp.getRoi();
if (roi == null)
{ // ensure there is always some ROI.
roi = new Roi(0, 0, imp.getWidth(), imp.getHeight(), imp);
imp.setRoi(roi);
}
int measurements = Analyzer.getMeasurements();
// defined in Set Measurements dialog
measurements |= CENTROID; // make sure centroid is included
measurements |= MIN_MAX; // make sure min_max is included
Analyzer.setMeasurements(measurements);
ImageStatistics stats = imp.getStatistics(measurements);
min_level = stats.min;
max_level = stats.max;
imp.killRoi();
stats = imp.getStatistics(measurements);
image_min = stats.min;
image_max = stats.max;
// IJ.log("min max = " + image_min + " " + image_max );
}
protected void FRAP_makeWindow()
{
if ( DEBUG ) IJ.log("FRAP_Analysis.makeWindow...");
goButton = new JButton ( "find ROI" );
// Create a label for the max level slider:
maxLevelLabel = new JLabel( "Max Level: " + (int)max_level, JLabel.LEFT );
maxLevelLabel.setAlignmentX( Component.LEFT_ALIGNMENT );
// Create the max level slider:
maxLevelSlider = new JSlider( JSlider.HORIZONTAL, (int) image_min, (int) image_max, (int) max_level );
maxLevelSlider.setMajorTickSpacing( (int) (image_max - image_min) / 4 );
maxLevelSlider.setMinorTickSpacing( (int) (image_max - image_min) / 16 );
maxLevelSlider.setPaintTicks( true );
maxLevelSlider.setPaintLabels( true );
maxLevelSlider.setBorder( BorderFactory.createEmptyBorder( 0, 0, 10, 0 ) );
maxLevelSlider.addChangeListener( this );
normalizeButton = new JButton ( "Normalize" );
doneButton = new JButton( "Done" );
// For stacks:
int ns = stack.getSize();
if ( DEBUG ) IJ.log( "getSize = " + ns );
/* Set a too-long label here, so that when things are packed,
they will be sufficiently big. Then, we re-set the label text
to say what we actually want to say. */
startButton = new JButton("Starting Slice:");
startButton.setActionCommand("starting slice");
startButton.addActionListener( this );
startSlice = new JTextField("1", 4);
// Position everything in the content pane:
JPanel contentPane = new JPanel();
contentPane.setLayout( new BoxLayout( contentPane, BoxLayout.Y_AXIS ) );
JPanel slicerPanel = new JPanel();
slicerPanel.add(startButton);
slicerPanel.add(startSlice);
contentPane.add(slicerPanel);
addAButton( goButton, contentPane );
contentPane.add(Box.createVerticalStrut(20));
contentPane.add( maxLevelLabel );
contentPane.add( maxLevelSlider );
addAButton( normalizeButton, contentPane );
contentPane.add(Box.createVerticalStrut(20));
addAButton( doneButton, contentPane );
add(contentPane);
pack();
setVisible( true );
}
void addAButton(JButton button, Container container)
{
button.addActionListener( this );
button.setAlignmentX(Component.CENTER_ALIGNMENT);
container.add(button);
}
protected void FRAP_remakeWindow()
{
maxLevelLabel.setText("Max Level: " + (int)max_level);
maxLevelSlider.setMinimum((int)image_min);
maxLevelSlider.setMaximum((int)image_max);
maxLevelSlider.setValue((int)max_level);
maxLevelSlider.setMajorTickSpacing((int)(image_max-image_min) / 4);
maxLevelSlider.setMinorTickSpacing((int)(image_max-image_min) / 16);
maxLevelSlider.repaint(); // shouldn't be needed...
maxLevelLabel.setText("Max Level: " + (int)max_level);
maxLevelSlider.setMinimum((int)image_min);
maxLevelSlider.setMaximum((int)image_max);
maxLevelSlider.setValue((int)max_level);
maxLevelSlider.setMajorTickSpacing((int)(image_max-image_min) / 4);
maxLevelSlider.setMinorTickSpacing((int)(image_max-image_min) / 16);
maxLevelSlider.repaint(); // shouldn't be needed...
// firstSlice.setText("1");
// updateSliceLabel();
}
public void stateChanged(ChangeEvent e)
{
if(e.getSource() == maxLevelSlider)
{
maxLevelLabel.setText( "Max Level: " + maxLevelSlider.getValue() );
max_level = (double)maxLevelSlider.getValue();
imp.killRoi();
Wand wand = new Wand(ip);
wand.npoints = 0;
wand.autoOutline( xcen, ycen, (int)min_level, (int)max_level );
roi = new PolygonRoi( wand.xpoints, wand.ypoints, wand.npoints, imp, Roi.TRACED_ROI);
imp.setRoi( roi );
}
}
public void actionPerformed(ActionEvent e)
{
Object b = e.getSource();
if (b == startButton)
{
startSlice.setText(Integer.toString(imp.getCurrentSlice()));
}
if (b == goButton)
{
if ( DEBUG )
IJ.log( "Button pressed: " + e.getActionCommand() );
testStack();
title = stack.getShortSliceLabel( Integer.parseInt(startSlice.getText()) - 1 );
ip = stack.getProcessor( Integer.parseInt(startSlice.getText()) - 1);
imp1 = new ImagePlus(title, ip);
title = stack.getShortSliceLabel( Integer.parseInt(startSlice.getText()) );
ip = stack.getProcessor( Integer.parseInt(startSlice.getText()) );
imp2 = new ImagePlus(title, ip);
ImageCalculator ic = new ImageCalculator();
ic.calculate("create, sub", imp1, imp2);
imp3 = WindowManager.getCurrentImage();
ip3 = imp3.getProcessor();
ip3.autoThreshold();
imp3.updateAndDraw();
findRoi();
imp.restoreRoi();
double[] y = getZAxisProfile();
if (y!=null)
{
double[] x = new double[y.length];
for (int i = 0; i != x.length; i++)
// x[i] = i + Integer.parseInt(startSlice.getText());
x[i] = i + 1;
PlotWindow pw = new PlotWindow("FRAP Analysis: "+ imp.getTitle(), "Slice", "Mean", x, y);
double [] a = Tools.getMinMax(x);
double xmin=a[0], xmax=a[1];
a = Tools.getMinMax(y);
double ymin=a[0], ymax=a[1];
pw.setLimits(xmin, xmax, ymin, ymax);
pw.draw();
}
}
if (b == normalizeButton)
{
double[] y = getZAxisProfile();
if (y!=null)
{
double[] x = new double[y.length];
for (int i = 0; i != x.length; i++)
// x[i] = i + Integer.parseInt(startSlice.getText());
x[i] = i + 1;
PlotWindow pw = new PlotWindow("FRAP Analysis: "+ imp.getTitle(), "Slice", "Mean", x, y);
double [] a = Tools.getMinMax(x);
double xmin=a[0], xmax=a[1];
a = Tools.getMinMax(y);
double ymin=a[0], ymax=a[1];
pw.setLimits(xmin, xmax, ymin, ymax);
pw.draw();
}
}
if (b == doneButton)
{
setVisible(false);
dispose();
}
}
private void findRoi()
{
int i, j;
int nb = 0;
xcen = 0;
ycen = 0;
ImageConverter imageConvert = new ImageConverter(imp3);
imageConvert.convertToGray8();
int background = 0;
int foreground = 255;
fill(ip3, foreground, background);
for(j = 0; j != imp3.getHeight(); j++)
{
for(i = 0; i != imp3.getWidth(); i++)
{
if(ip3.getPixelValue(i, j) != 0)
{
xcen += i;
ycen += j;
nb++;
}
}
}
xcen /= nb;
ycen /= nb;
imp3.killRoi();
Wand wand = new Wand(ip3);
// wand.npoints = 0;
wand.autoOutline(xcen, ycen);
roi = new PolygonRoi( wand.xpoints, wand.ypoints, wand.npoints, imp3, Roi.TRACED_ROI);
imp3.setRoi(roi);
}
private void fill(ImageProcessor ip, int foreground, int background)
{
int i, j;
FloodFiller ff = new FloodFiller(ip);
ip.setColor(127);
for (j = 0; j != ip.getHeight(); j++)
{
if (ip.getPixel(0, j) == background) ff.fill(0, j);
if (ip.getPixel(ip.getWidth() - 1, j) == background) ff.fill(ip.getWidth() - 1, j);
}
for (i = 0; i != ip.getWidth(); i++)
{
if (ip.getPixel(i, 0) == background) ff.fill(i, 0);
if (ip.getPixel(i, ip.getHeight() - 1) == background) ff.fill(i, ip.getHeight() - 1);
}
for (j = 0; j != ip.getHeight(); j++)
{
for (i = 0; i != ip.getWidth(); i++)
{
if (ip.getPixel(i, j) == 127)
ip.putPixel(i, j, background);
else
ip.putPixel(i, j, foreground);
}
}
}
double[] getZAxisProfile()
{
// int size = stack.getSize() - Integer.parseInt(startSlice.getText()) + 1;
int size = stack.getSize();
double[] values = new double[size];
Calibration cal = imp.getCalibration();
Analyzer analyzer = new Analyzer(imp);
int measurements = analyzer.getMeasurements();
boolean showResults = measurements!=0 && measurements!=LIMIT;
measurements |= MEAN;
if (showResults)
{
if (!analyzer.resetCounter())
return null;
}
for (int i = 0; i != size; i++)
{
// ip = stack.getProcessor(i + Integer.parseInt(startSlice.getText()));
ip = stack.getProcessor(i + 1);
ip.setRoi(roi);
ImageStatistics stats = ImageStatistics.getStatistics(ip, measurements, cal);
analyzer.saveResults(stats, roi);
// if (showResults)
// analyzer.displayResults();
values[i] = (double)stats.mean;
}
return values;
}
public void testStack() throws IllegalArgumentException
{
int first;
try
{
first = Integer.parseInt(startSlice.getText());
}
catch (NumberFormatException ex)
{
IJ.error("Invalid slice number: please use an integer!");
throw new IllegalArgumentException();
}
if (first < 1 || first >= imp.getStackSize())
{
IJ.error("Invalid slice range: please stick within the bounds of the current image stack.");
throw new IllegalArgumentException();
}
}
}