package gdsc.colocalisation;
/*-----------------------------------------------------------------------------
* GDSC Plugins for ImageJ
*
* Copyright (C) 2011 Alex Herbert
* Genome Damage and Stability Centre
* University of Sussex, UK
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*---------------------------------------------------------------------------*/
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import gdsc.UsageTracker;
import gdsc.core.ij.Utils;
import ij.IJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.WindowManager;
import ij.gui.GenericDialog;
import ij.plugin.PlugIn;
import ij.process.ImageProcessor;
import ij.text.TextWindow;
/**
* For all particles in a mask (defined by their unique pixel value), count the overlap with a second mask image. An
* intensity image is used to calculate the Manders coefficient of each particle.
*/
public class ParticleOverlap implements PlugIn
{
private static String TITLE = "Particle Overlap";
private static String newLine = System.getProperty("line.separator");
private static String header = "Mask1\tImage1\tMask2\tID\tN\tNo\t%\tI\tIo\tManders";
private static String maskTitle1 = "";
private static String imageTitle = "";
private static String maskTitle2 = "";
private static boolean showTotal = false;
private static boolean showTable = true;
private static String filename = "";
private static TextWindow tw = null;
private ImagePlus mask1Imp, imageImp, mask2Imp;
private OutputStreamWriter out = null;
public void run(String arg)
{
UsageTracker.recordPlugin(this.getClass(), arg);
if (WindowManager.getImageCount() == 0)
{
IJ.showMessage(TITLE, "No images opened.");
return;
}
if (!showDialog())
return;
analyse();
}
private boolean showDialog()
{
GenericDialog gd = new GenericDialog(TITLE);
gd.addMessage("For each particle in a mask (defined by unique pixel value)\n"
+ "count the overlap and Manders coefficient with a second mask image");
String[] imageList = Utils.getImageList(Utils.GREY_SCALE, null);
String[] maskList = Utils.getImageList(Utils.GREY_8_16, null);
gd.addChoice("Particle_mask", maskList, maskTitle1);
gd.addChoice("Particle_image", imageList, imageTitle);
gd.addChoice("Overlap_mask", maskList, maskTitle2);
gd.addCheckbox("Show_total", showTotal);
gd.addCheckbox("Show_table", showTable);
gd.addStringField("File", filename, 30);
gd.showDialog();
if (gd.wasCanceled())
return false;
maskTitle1 = gd.getNextChoice();
imageTitle = gd.getNextChoice();
maskTitle2 = gd.getNextChoice();
showTotal = gd.getNextBoolean();
showTable = gd.getNextBoolean();
filename = gd.getNextString();
mask1Imp = WindowManager.getImage(maskTitle1);
imageImp = WindowManager.getImage(imageTitle);
mask2Imp = WindowManager.getImage(maskTitle2);
if (mask1Imp == null)
{
IJ.error(TITLE, "No particle mask specified");
return false;
}
if (!checkDimensions(mask1Imp, imageImp))
{
IJ.error(TITLE, "Particle image must match the dimensions of the particle mask");
return false;
}
if (!checkDimensions(mask1Imp, mask2Imp))
{
IJ.error(TITLE, "Overlap mask must match the dimensions of the particle mask");
return false;
}
if (!showTable && emptyFilename())
{
IJ.error(TITLE, "No output specified");
return false;
}
return true;
}
private boolean emptyFilename()
{
return (filename == null || filename.length() == 0);
}
private boolean checkDimensions(ImagePlus imp1, ImagePlus imp2)
{
if (imp2 == null)
return false;
int[] d1 = imp1.getDimensions();
int[] d2 = imp2.getDimensions();
// Just check width, height, depth
for (int i : new int[] { 0, 1, 3 })
if (d1[i] != d2[i])
return false;
return true;
}
private void analyse()
{
// Dimensions are the same. Extract a stack for each image;
ImageStack m1 = extractStack(mask1Imp);
ImageStack m2 = extractStack(mask2Imp);
ImageStack i1 = extractStack(imageImp);
// Count the pixels in the particle mask
int[] n1 = new int[(mask1Imp.getBitDepth() == 8) ? 256 : 65536];
// Count the pixels in the particle mask that overlap
int[] no1 = new int[n1.length];
// Sum the pixels in the particle image
double[] s1 = new double[n1.length];
// Sum the pixels in the particle image that overlap
double[] so1 = new double[n1.length];
final int size = m1.getWidth() * m1.getHeight();
for (int n = 1; n <= m1.getSize(); n++)
{
ImageProcessor mask1 = m1.getProcessor(n);
ImageProcessor mask2 = m2.getProcessor(n);
ImageProcessor image1 = i1.getProcessor(n);
for (int i = 0; i < size; i++)
{
final int p1 = mask1.get(i);
if (p1 != 0)
{
final int p2 = mask2.get(i);
final float v1 = image1.getf(i);
n1[p1]++;
s1[p1] += v1;
if (p2 != 0)
{
no1[p1]++;
so1[p1] += v1;
}
}
}
}
// Summarise results
createResultsTable();
createResultsFile();
final String title = createTitle();
long sn1 = 0, sno1 = 0;
double ss1 = 0, sso1 = 0;
for (int id = 1; id < n1.length; id++)
{
if (n1[id] == 0)
continue;
sn1 += n1[id];
sno1 += no1[id];
ss1 += s1[id];
sso1 += so1[id];
addResult(title, id, n1[id], no1[id], s1[id], so1[id]);
}
if (showTotal)
addResult(title, 0, sn1, sno1, ss1, sso1);
closeResultsFile();
}
private ImageStack extractStack(ImagePlus imp)
{
ImageStack oldStack = imp.getImageStack();
int channel = imp.getChannel();
int frame = imp.getFrame();
int size = imp.getNSlices();
ImageStack stack = new ImageStack(imp.getWidth(), imp.getHeight(), size);
for (int slice = 1; slice <= size; slice++)
{
int index = imp.getStackIndex(channel, slice, frame);
stack.setPixels(oldStack.getPixels(index), slice);
}
return stack;
}
private void createResultsTable()
{
if (!showTable)
return;
if (tw == null || !tw.isShowing())
{
tw = new TextWindow(TITLE + " Results", header, "", 800, 500);
}
}
private void createResultsFile()
{
if (emptyFilename())
return;
try
{
if (filename.lastIndexOf('.') < 0)
filename += ".xls";
FileOutputStream fos = new FileOutputStream(filename);
out = new OutputStreamWriter(fos, "UTF-8");
try
{
out.write(header);
out.write(newLine);
}
catch (IOException e)
{
closeResultsFile();
}
}
catch (FileNotFoundException e)
{
}
catch (UnsupportedEncodingException e)
{
}
}
private String createTitle()
{
StringBuilder sb = new StringBuilder();
sb.append(getName(mask1Imp)).append("\t");
sb.append(getName(imageImp)).append("\t");
sb.append(getName(mask2Imp)).append("\t");
return sb.toString();
}
private String getName(ImagePlus imp)
{
String name = imp.getTitle();
int suffix = 0;
String[] prefix = { " [", "," };
if (imp.getNChannels() > 1)
{
name += prefix[suffix++] + "C=" + imp.getChannel();
}
if (imp.getNFrames() > 1)
name += prefix[suffix++] + "T=" + imp.getFrame();
if (suffix > 0)
name += "]";
return name;
}
private void addResult(String title, int id, long n1, long no1, double s1, double so1)
{
StringBuilder sb = new StringBuilder(title);
sb.append(id).append("\t");
sb.append(n1).append("\t");
sb.append(no1).append("\t");
sb.append(Utils.rounded((100.0 * no1) / n1)).append("\t");
sb.append(s1).append("\t");
sb.append(so1).append("\t");
sb.append(Utils.rounded(so1 / s1, 5)).append("\t");
recordResult(sb.toString());
}
private void recordResult(String string)
{
if (showTable)
tw.append(string);
if (out != null)
{
try
{
out.write(string);
out.write(newLine);
}
catch (IOException e)
{
closeResultsFile();
}
}
}
private void closeResultsFile()
{
if (out != null)
{
try
{
out.close();
}
catch (IOException e)
{
}
finally
{
out = null;
}
}
}
}