package gdsc.smlm.ij.plugins;
import gdsc.core.ij.Utils;
/*-----------------------------------------------------------------------------
* GDSC SMLM Software
*
* Copyright (C) 2013 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 3 of the License, or
* (at your option) any later version.
*---------------------------------------------------------------------------*/
import gdsc.smlm.ij.IJImageSource;
import gdsc.smlm.ij.results.IJTablePeakResults;
import gdsc.smlm.results.MemoryPeakResults;
import gdsc.smlm.results.PeakResult;
import ij.IJ;
import ij.ImageListener;
import ij.ImagePlus;
import ij.WindowManager;
import ij.gui.ImageWindow;
import ij.gui.NonBlockingGenericDialog;
import ij.gui.Overlay;
import ij.gui.PointRoi;
import ij.plugin.PlugIn;
import ij.text.TextWindow;
import java.awt.Checkbox;
import java.awt.Choice;
import java.awt.Label;
import java.awt.Point;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Arrays;
/**
* Produces a summary table of the results that are stored in memory.
*/
public class OverlayResults implements PlugIn, ItemListener, ImageListener
{
private static final String TITLE = "Overlay Results";
private static String name = "";
private static boolean showTable = false;
private String[] names;
private int[] ids;
private Choice choice;
private Checkbox checkbox;
private Label label;
private int currentIndex = 0;
private int currentSlice = -1;
private class Job
{
final int index;
Job(int index)
{
this.index = index;
}
}
private class InBox
{
private Job job = null;
synchronized void add(int index)
{
this.job = new Job(index);
this.notify();
}
synchronized void close()
{
this.job = null;
this.notify();
}
synchronized Job next()
{
Job job = this.job;
this.job = null;
return job;
}
boolean isEmpty()
{
return job == null;
}
}
private InBox inbox = new InBox();
private class Worker implements Runnable
{
private boolean running = true;
private boolean[] error = new boolean[ids.length];
// The results text window (so we can close it)
private TextWindow tw = null;
public void run()
{
while (running)
{
try
{
Job job = null;
synchronized (inbox)
{
if (inbox.isEmpty())
inbox.wait();
job = inbox.next();
}
if (job == null || !running)
break;
if (job.index == 0)
{
// This may be selection of no image
clearOldOverlay();
continue;
}
// Check name of the image
if (currentIndex != job.index)
clearOldOverlay();
currentIndex = job.index;
drawOverlay();
}
catch (InterruptedException e)
{
break;
}
}
clearOldOverlay();
closeTextWindow();
}
private void clearOldOverlay()
{
if (currentIndex != 0)
{
ImagePlus oldImp = WindowManager.getImage(ids[currentIndex]);
if (oldImp != null)
oldImp.setOverlay(null);
}
currentSlice = -1;
currentIndex = 0;
}
private void closeTextWindow()
{
if (tw != null)
{
tw.close();
tw = null;
}
}
/**
* Draw the overlay.
* <p>
* This is only called when index > 0.
*/
private void drawOverlay()
{
ImagePlus imp = WindowManager.getImage(ids[currentIndex]);
String name = names[currentIndex];
if (imp == null)
{
// Image has been closed.
logError("Image not available", name);
return;
}
// Check slice
int newSlice = imp.getCurrentSlice();
if (currentSlice == newSlice)
{
boolean isShowing = tw != null;
if (showTable == isShowing)
// No change from last time
return;
}
currentSlice = newSlice;
MemoryPeakResults results = MemoryPeakResults.getResults(name);
if (results == null)
{
// Results have been cleared from memory (or renamed).
logError("Results not available", name);
return;
}
clearError();
IJTablePeakResults table = null;
if (showTable)
{
table = new IJTablePeakResults(false);
table.setPeakIdColumnName("Frame");
table.setTableTitle(TITLE);
table.copySettings(results);
table.setClearAtStart(true);
table.setAddCounter(true);
table.setHideSourceText(true);
table.begin();
// Position under thew window
tw = table.getResultsWindow();
ImageWindow win = imp.getWindow();
Point p = win.getLocation();
p.y += win.getHeight();
tw.setLocation(p);
}
else
{
closeTextWindow();
}
float[] ox = new float[100];
float[] oy = new float[100];
int points = 0;
for (PeakResult r : results.getResults())
{
if (r.getFrame() != currentSlice)
continue;
if (points == ox.length)
{
ox = Arrays.copyOf(ox, (int) (points * 1.5));
oy = Arrays.copyOf(oy, ox.length);
}
ox[points] = r.getXPosition();
oy[points] = r.getYPosition();
points++;
if (table != null)
table.add(r);
}
PointRoi roi = new PointRoi(ox, oy, points);
roi.setPointType(3);
imp.getWindow().toFront();
imp.setOverlay(new Overlay(roi));
if (table != null)
{
table.end();
TextWindow tw = table.getResultsWindow();
tw.getTextPanel().scrollToTop();
}
}
private void logError(String msg, String name)
{
if (!error[currentIndex])
{
Utils.log("%s Error: %s for results '%s'", TITLE, msg, name);
label.setText("Error: " + msg + ". Restart this plugin to refresh.");
}
error[currentIndex] = true;
}
private void clearError()
{
error[currentIndex] = false;
if (!Utils.isNullOrEmpty(label.getText()))
label.setText("");
}
}
/*
* (non-Javadoc)
*
* @see ij.plugin.PlugIn#run(java.lang.String)
*/
public void run(String arg)
{
SMLMUsageTracker.recordPlugin(this.getClass(), arg);
if (MemoryPeakResults.isMemoryEmpty())
{
IJ.error(TITLE, "There are no fitting results in memory");
return;
}
names = new String[MemoryPeakResults.getResultNames().size() + 1];
ids = new int[names.length];
int c = 0;
names[c++] = "(None)";
for (MemoryPeakResults results : MemoryPeakResults.getAllResults())
{
if (results.getSource().getOriginal() instanceof IJImageSource)
{
IJImageSource source = (IJImageSource) (results.getSource().getOriginal());
ImagePlus imp = WindowManager.getImage(source.getName());
if (imp != null)
{
ids[c] = imp.getID();
names[c++] = results.getName();
}
}
}
if (c == 1)
{
IJ.error(TITLE, "There are no result images available");
return;
}
names = Arrays.copyOf(names, c);
Thread t = null;
Worker w = null;
NonBlockingGenericDialog gd = new NonBlockingGenericDialog(TITLE);
gd.addMessage("Overlay results on current image frame");
gd.addChoice("Results", names, (name == null) ? "" : name);
gd.addCheckbox("Show_table", showTable);
gd.addMessage("");
gd.addHelp(About.HELP_URL);
gd.hideCancelButton();
gd.setOKLabel("Close");
if (!(IJ.isMacro() || java.awt.GraphicsEnvironment.isHeadless()))
{
choice = (Choice) gd.getChoices().get(0);
choice.addItemListener(this);
checkbox = (Checkbox) gd.getCheckboxes().get(0);
checkbox.addItemListener(this);
label = (Label) gd.getMessage();
// Listen for changes to an image
ImagePlus.addImageListener(this);
show();
t = new Thread(w = new Worker());
t.setDaemon(true);
t.start();
}
gd.showDialog();
if (!(IJ.isMacro() || java.awt.GraphicsEnvironment.isHeadless()))
ImagePlus.removeImageListener(this);
if (!gd.wasCanceled())
{
name = gd.getNextChoice();
showTable = gd.getNextBoolean();
}
if (t != null)
{
w.running = false;
inbox.close();
try
{
t.join(0);
}
catch (InterruptedException e)
{
}
t = null;
}
}
public void itemStateChanged(ItemEvent e)
{
show();
}
public void imageClosed(ImagePlus arg0)
{
}
public void imageOpened(ImagePlus arg0)
{
}
public void imageUpdated(ImagePlus imp)
{
if (imp == null)
return;
if (ids[currentIndex] == imp.getID())
{
show();
}
}
private void show()
{
showTable = checkbox.getState();
inbox.add(choice.getSelectedIndex());
}
}