package cz.cuni.lf1.lge.ThunderSTORM.results;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.io.*;
import java.awt.datatransfer.*;
import ij.*;
import ij.gui.ImageCanvas;
import ij.gui.ImageWindow;
import ij.gui.NewImage;
import ij.gui.TrimmedButton;
import ij.process.*;
import ij.measure.*;
import ij.plugin.filter.Analyzer;
import ij.text.TextWindow;
import static cz.cuni.lf1.lge.ThunderSTORM.util.MathProxy.min;
import static cz.cuni.lf1.lge.ThunderSTORM.util.MathProxy.max;
/**
* This class is an extended ImageWindow that displays histograms.
*/
public final class IJHistogramWindow extends ImageWindow implements Measurements, ActionListener, ClipboardOwner,
MouseListener, MouseMotionListener, ImageListener, KeyListener, Runnable {
static final int WIN_WIDTH = 300;
static final int WIN_HEIGHT = 240;
static final int HIST_WIDTH = 256;
static final int HIST_HEIGHT = 128;
static final int BAR_HEIGHT = 12;
static final int XMARGIN = 20;
static final int YMARGIN = 10;
static final int INTENSITY = 0, RED = 1, GREEN = 2, BLUE = 3;
protected ImageStatistics stats;
protected int[] histogram;
protected LookUpTable lut;
protected Rectangle frame = null;
protected Button list, save, copy, log, live, rgb, roiFilter;
protected Label value, count;
protected static String defaultDirectory = null;
protected int decimalPlaces;
protected int digits;
protected long newMaxCount;
protected int plotScale = 1;
protected boolean logScale;
protected Calibration cal;
protected int yMax;
public static int nBins = 256;
private int srcImageID; // ID of source image
private ImagePlus srcImp; // source image for live histograms
private Thread bgThread; // thread background drawing
private boolean doUpdate; // tells background thread to update
private int channel; // RGB channel
private String blankLabel;
private String paramName;
/**
* Displays a histogram using the title "Histogram of ImageName".
*/
public IJHistogramWindow(ImagePlus imp) {
super(NewImage.createRGBImage("Histogram of " + imp.getShortTitle(), WIN_WIDTH, WIN_HEIGHT, 1, NewImage.FILL_WHITE));
showHistogram(imp, 256, 0.0, 0.0);
}
/**
* Displays a histogram using the specified title and number of bins.
* Currently, the number of bins must be 256 expect for 32 bit images.
*/
public IJHistogramWindow(String title, ImagePlus imp, int bins) {
super(NewImage.createRGBImage(title, WIN_WIDTH, WIN_HEIGHT, 1, NewImage.FILL_WHITE));
showHistogram(imp, bins, 0.0, 0.0);
}
/**
* Displays a histogram using the specified title, number of bins and
* histogram range. Currently, the number of bins must be 256 and the
* histogram range range must be the same as the image range expect for 32
* bit images.
*/
public IJHistogramWindow(String title, ImagePlus imp, int bins, double histMin, double histMax) {
super(NewImage.createRGBImage(title, WIN_WIDTH, WIN_HEIGHT, 1, NewImage.FILL_WHITE));
showHistogram(imp, bins, histMin, histMax);
}
/**
* Displays a histogram using the specified title, number of bins, histogram
* range and yMax.
*/
public IJHistogramWindow(String title, ImagePlus imp, int bins, double histMin, double histMax, int yMax) {
super(NewImage.createRGBImage(title, WIN_WIDTH, WIN_HEIGHT, 1, NewImage.FILL_WHITE));
this.yMax = yMax;
showHistogram(imp, bins, histMin, histMax);
}
/**
* Displays a histogram using the specified title and ImageStatistics.
*/
public IJHistogramWindow(String title, ImagePlus imp, ImageStatistics stats) {
super(NewImage.createRGBImage(title, WIN_WIDTH, WIN_HEIGHT, 1, NewImage.FILL_WHITE));
//IJ.log("HistogramWindow: "+stats.histMin+" "+stats.histMax+" "+stats.nBins);
this.yMax = stats.histYMax;
showHistogram(imp, stats);
}
/**
* Displays a histogram using the specified parameter name, title and ImageStatistics.
*/
public IJHistogramWindow(String paramName, String title, ImagePlus imp, ImageStatistics stats) {
super(NewImage.createRGBImage(title, WIN_WIDTH, WIN_HEIGHT, 1, NewImage.FILL_WHITE));
//IJ.log("HistogramWindow: "+stats.histMin+" "+stats.histMax+" "+stats.nBins);
this.paramName = paramName;
this.yMax = stats.histYMax;
showHistogram(imp, stats);
}
/**
* Draws the histogram using the specified title and number of bins.
* Currently, the number of bins must be 256 expect for 32 bit images.
*/
public void showHistogram(ImagePlus imp, int bins) {
showHistogram(imp, bins, 0.0, 0.0);
}
/**
* Draws the histogram using the specified title, number of bins and
* histogram range. Currently, the number of bins must be 256 and the
* histogram range range must be the same as the image range expect for 32
* bit images.
*/
public void showHistogram(ImagePlus imp, int bins, double histMin, double histMax) {
boolean limitToThreshold = (Analyzer.getMeasurements() & LIMIT) != 0;
if(channel != INTENSITY && imp.getType() == ImagePlus.COLOR_RGB) {
ColorProcessor cp = (ColorProcessor) imp.getProcessor();
ImageProcessor ip = cp.toFloat(channel, null);
ImagePlus imp2 = new ImagePlus("", ip);
imp2.setRoi(imp.getRoi());
stats = imp2.getStatistics(AREA + MEAN + MODE + MIN_MAX, bins, histMin, histMax);
} else {
stats = imp.getStatistics(AREA + MEAN + MODE + MIN_MAX + (limitToThreshold ? LIMIT : 0), bins, histMin, histMax);
}
showHistogram(imp, stats);
}
/**
* Draws the histogram using the specified title and ImageStatistics.
*/
public void showHistogram(ImagePlus imp, ImageStatistics stats) {
if(list == null) {
setup(imp);
}
this.stats = stats;
cal = imp.getCalibration();
boolean limitToThreshold = (Analyzer.getMeasurements() & LIMIT) != 0;
imp.getMask();
histogram = stats.histogram;
if(limitToThreshold && histogram.length == 256) {
ImageProcessor ip = imp.getProcessor();
if(ip.getMinThreshold() != ImageProcessor.NO_THRESHOLD) {
int lower = scaleDown(ip, ip.getMinThreshold());
int upper = scaleDown(ip, ip.getMaxThreshold());
for(int i = 0; i < lower; i++) {
histogram[i] = 0;
}
for(int i = upper + 1; i < 256; i++) {
histogram[i] = 0;
}
}
}
lut = imp.createLut();
int type = imp.getType();
boolean fixedRange = type == ImagePlus.GRAY8 || type == ImagePlus.COLOR_256 || type == ImagePlus.COLOR_RGB;
ImageProcessor ip = this.imp.getProcessor();
ip.setColor(Color.white);
ip.resetRoi();
ip.fill();
ImageProcessor srcIP = imp.getProcessor();
drawHistogram(imp, ip, fixedRange, stats.histMin, stats.histMax);
this.imp.updateAndDraw();
}
private void setup(ImagePlus imp) {
boolean isRGB = imp.getType() == ImagePlus.COLOR_RGB;
Panel buttons = new Panel();
int hgap = IJ.isMacOSX() || isRGB ? 1 : 5;
buttons.setLayout(new FlowLayout(FlowLayout.RIGHT, hgap, 0));
int trim = IJ.isMacOSX() ? 6 : 0;
list = new TrimmedButton("List", trim);
list.addActionListener(this);
buttons.add(list);
//copy = new TrimmedButton("Copy", trim);
//copy.addActionListener(this);
//buttons.add(copy);
log = new TrimmedButton("Log", trim);
log.addActionListener(this);
buttons.add(log);
//live = new TrimmedButton("Live", trim);
//live.addActionListener(this);
//buttons.add(live);
/*if(imp != null && isRGB) {
rgb = new TrimmedButton("RGB", trim);
rgb.addActionListener(this);
buttons.add(rgb);
}*/
roiFilter = new TrimmedButton("Apply ROI to filter", trim);
roiFilter.addActionListener(this);
buttons.add(roiFilter);
add(buttons);
if(!(IJ.isMacOSX() && isRGB)) {
Panel valueAndCount = new Panel();
valueAndCount.setLayout(new GridLayout(2, 1, 0, 0));
blankLabel = IJ.isMacOSX() ? " " : " ";
value = new Label(blankLabel);
Font font = new Font("Monospaced", Font.PLAIN, 12);
value.setFont(font);
valueAndCount.add(value);
count = new Label(blankLabel);
count.setFont(font);
valueAndCount.add(count);
buttons.add(valueAndCount);
}
pack();
}
public void setup() {
setup(null);
}
@Override
public void mouseMoved(int x, int y) {
if(value == null || count == null) {
return;
}
if((frame != null) && x >= frame.x && x <= (frame.x + frame.width)) {
x = x - frame.x;
if(x > 255) {
x = 255;
}
int index = (int) (x * ((double) histogram.length) / HIST_WIDTH);
String vlabel = null, clabel = null;
if(blankLabel.length() == 11) // OS X
{
vlabel = " ";
clabel = " ";
} else {
vlabel = " value=";
clabel = " count=";
}
String v = vlabel + d2s(cal.getCValue(stats.histMin + index * stats.binSize)) + blankLabel;
String c = clabel + histogram[index] + blankLabel;
int len = vlabel.length() + blankLabel.length();
value.setText(v.substring(0, len));
count.setText(c.substring(0, len));
} else {
value.setText(blankLabel);
count.setText(blankLabel);
}
}
protected void drawHistogram(ImageProcessor ip, boolean fixedRange) {
drawHistogram(null, ip, fixedRange, 0.0, 0.0);
}
void drawHistogram(ImagePlus imp, ImageProcessor ip, boolean fixedRange, double xMin, double xMax) {
int x, y;
long maxCount2 = 0;
int mode2 = 0;
int saveModalCount;
ip.setColor(Color.black);
ip.setLineWidth(1);
decimalPlaces = Analyzer.getPrecision();
digits = cal.calibrated() || stats.binSize != 1.0 ? decimalPlaces : 0;
saveModalCount = histogram[stats.mode];
for(int i = 0; i < histogram.length; i++) {
if((histogram[i] > maxCount2) && (i != stats.mode)) {
maxCount2 = histogram[i];
mode2 = i;
}
}
newMaxCount = histogram[stats.mode];
if((newMaxCount > (maxCount2 * 2)) && (maxCount2 != 0)) {
newMaxCount = (int) (maxCount2 * 1.5);
//histogram[stats.mode] = newMaxCount;
}
if(logScale || IJ.shiftKeyDown() && !liveMode()) {
drawLogPlot(yMax > 0 ? yMax : newMaxCount, ip);
}
drawPlot(yMax > 0 ? yMax : newMaxCount, ip);
histogram[stats.mode] = saveModalCount;
x = XMARGIN + 1;
y = YMARGIN + HIST_HEIGHT + 2;
if(imp == null) {
lut.drawUnscaledColorBar(ip, x - 1, y, 256, BAR_HEIGHT);
} else {
drawAlignedColorBar(imp, xMin, xMax, ip, x - 1, y, 256, BAR_HEIGHT);
}
y += BAR_HEIGHT + 15;
drawText(ip, x, y, fixedRange);
srcImageID = imp.getID();
}
void drawAlignedColorBar(ImagePlus imp, double xMin, double xMax, ImageProcessor ip, int x, int y, int width, int height) {
ImageProcessor ipSource = imp.getProcessor();
float[] pixels = null;
ImageProcessor ipRamp = null;
if(ipSource instanceof ColorProcessor) {
ipRamp = new FloatProcessor(width, height);
if(channel == RED) {
ipRamp.setColorModel(LUT.createLutFromColor(Color.red));
} else if(channel == GREEN) {
ipRamp.setColorModel(LUT.createLutFromColor(Color.green));
} else if(channel == BLUE) {
ipRamp.setColorModel(LUT.createLutFromColor(Color.blue));
}
pixels = (float[]) ipRamp.getPixels();
} else {
pixels = new float[width * height];
}
for(int j = 0; j < height; j++) {
for(int i = 0; i < width; i++) {
pixels[i + width * j] = (float) (xMin + i * (xMax - xMin) / (width - 1));
}
}
if(!(ipSource instanceof ColorProcessor)) {
ColorModel cm = null;
if(imp.isComposite()) {
cm = ((CompositeImage) imp).getChannelLut();
} else if(ipSource.getMinThreshold() == ImageProcessor.NO_THRESHOLD) {
cm = ipSource.getColorModel();
} else {
cm = ipSource.getCurrentColorModel();
}
ipRamp = new FloatProcessor(width, height, pixels, cm);
}
double min = ipSource.getMin();
double max = ipSource.getMax();
ipRamp.setMinAndMax(min, max);
ImageProcessor bar = null;
if(ip instanceof ColorProcessor) {
bar = ipRamp.convertToRGB();
} else {
bar = ipRamp.convertToByte(true);
}
ip.insert(bar, x, y);
ip.setColor(Color.black);
ip.drawRect(x - 1, y, width + 2, height);
}
/**
* Scales a threshold level to the range 0-255.
*/
int scaleDown(ImageProcessor ip, double threshold) {
double min = ip.getMin();
double max = ip.getMax();
if(max > min) {
return (int) (((threshold - min) / (max - min)) * 255.0);
} else {
return 0;
}
}
void drawPlot(long maxCount, ImageProcessor ip) {
if(maxCount == 0) {
maxCount = 1;
}
frame = new Rectangle(XMARGIN, YMARGIN, HIST_WIDTH, HIST_HEIGHT);
ip.drawRect(frame.x - 1, frame.y, frame.width + 2, frame.height + 1);
int index, y;
for(int i = 0; i < HIST_WIDTH; i++) {
index = (int) (i * (double) histogram.length / HIST_WIDTH);
y = (int) (((double) HIST_HEIGHT * (double) histogram[index]) / maxCount);
if(y > HIST_HEIGHT) {
y = HIST_HEIGHT;
}
ip.drawLine(i + XMARGIN, YMARGIN + HIST_HEIGHT, i + XMARGIN, YMARGIN + HIST_HEIGHT - y);
}
}
void drawLogPlot(long maxCount, ImageProcessor ip) {
frame = new Rectangle(XMARGIN, YMARGIN, HIST_WIDTH, HIST_HEIGHT);
ip.drawRect(frame.x - 1, frame.y, frame.width + 2, frame.height + 1);
double max = Math.log(maxCount);
ip.setColor(Color.gray);
int index, y;
for(int i = 0; i < HIST_WIDTH; i++) {
index = (int) (i * (double) histogram.length / HIST_WIDTH);
y = histogram[index] == 0 ? 0 : (int) (HIST_HEIGHT * Math.log(histogram[index]) / max);
if(y > HIST_HEIGHT) {
y = HIST_HEIGHT;
}
ip.drawLine(i + XMARGIN, YMARGIN + HIST_HEIGHT, i + XMARGIN, YMARGIN + HIST_HEIGHT - y);
}
ip.setColor(Color.black);
}
void drawText(ImageProcessor ip, int x, int y, boolean fixedRange) {
ip.setFont(new Font("SansSerif", Font.PLAIN, 12));
ip.setAntialiasedText(true);
double hmin = cal.getCValue(stats.histMin);
double hmax = cal.getCValue(stats.histMax);
double range = hmax - hmin;
if(fixedRange && !cal.calibrated() && hmin == 0 && hmax == 255) {
range = 256;
}
ip.drawString(d2s(hmin), x - 4, y);
ip.drawString(d2s(hmax), x + HIST_WIDTH - getWidth(hmax, ip) + 10, y);
double binWidth = range / stats.nBins;
binWidth = Math.abs(binWidth);
boolean showBins = binWidth != 1.0 || !fixedRange;
int col1 = XMARGIN + 5;
int col2 = XMARGIN + HIST_WIDTH / 2;
int row1 = y + 25;
if(showBins) {
row1 -= 8;
}
int row2 = row1 + 15;
int row3 = row2 + 15;
int row4 = row3 + 15;
long count = stats.longPixelCount > 0 ? stats.longPixelCount : stats.pixelCount;
String modeCount = " (" + stats.maxCount + ")";
if(modeCount.length() > 12) {
modeCount = "";
}
ip.drawString("Count: " + count, col1, row1);
ip.drawString("Mean: " + d2s(stats.mean), col1, row2);
ip.drawString("StdDev: " + d2s(stats.stdDev), col1, row3);
ip.drawString("Mode: " + d2s(stats.dmode) + modeCount, col2, row3);
ip.drawString("Min: " + d2s(stats.min), col2, row1);
ip.drawString("Max: " + d2s(stats.max), col2, row2);
if(showBins) {
ip.drawString("Bins: " + d2s(stats.nBins), col1, row4);
ip.drawString("Bin Width: " + d2s(binWidth), col2, row4);
}
}
/*
String d2s(double d) {
if (d==Double.MAX_VALUE||d==-Double.MAX_VALUE)
return "0";
else if (Double.isNaN(d))
return("NaN");
else if (Double.isInfinite(d))
return("Infinity");
else if ((int)d==d)
return ResultsTable.d2s(d,0);
else
return ResultsTable.d2s(d,decimalPlaces);
}
*/
private String d2s(double d) {
if((int) d == d) {
return IJ.d2s(d, 0);
} else {
return IJ.d2s(d, 8);
}
}
int getWidth(double d, ImageProcessor ip) {
return ip.getStringWidth(d2s(d));
}
protected void showList() {
StringBuilder sb = new StringBuilder();
String vheading = stats.binSize == 1.0 ? "value" : "bin start";
if(cal.calibrated() && !cal.isSigned16Bit()) {
for(int i = 0; i < stats.nBins; i++) {
sb.append(i).append("\t").append(ResultsTable.d2s(cal.getCValue(stats.histMin + i * stats.binSize), digits)).append("\t").append(histogram[i]).append("\n");
}
TextWindow tw = new TextWindow(getTitle(), "level\t" + vheading + "\tcount", sb.toString(), 200, 400);
} else {
for(int i = 0; i < stats.nBins; i++) {
sb.append(ResultsTable.d2s(cal.getCValue(stats.histMin + i * stats.binSize), digits)).append("\t").append(histogram[i]).append("\n");
}
TextWindow tw = new TextWindow(getTitle(), vheading + "\tcount", sb.toString(), 200, 400);
}
}
protected void copyToClipboard() {
Clipboard systemClipboard = null;
try {
systemClipboard = getToolkit().getSystemClipboard();
} catch(Exception e) {
systemClipboard = null;
}
if(systemClipboard == null) {
IJ.error("Unable to copy to Clipboard.");
return;
}
IJ.showStatus("Copying histogram values...");
CharArrayWriter aw = new CharArrayWriter(stats.nBins * 4);
PrintWriter pw = new PrintWriter(aw);
for(int i = 0; i < stats.nBins; i++) {
pw.print(ResultsTable.d2s(cal.getCValue(stats.histMin + i * stats.binSize), digits) + "\t" + histogram[i] + "\n");
}
String text = aw.toString();
pw.close();
StringSelection contents = new StringSelection(text);
systemClipboard.setContents(contents, this);
IJ.showStatus(text.length() + " characters copied to Clipboard");
}
void replot() {
ImageProcessor ip = this.imp.getProcessor();
frame = new Rectangle(XMARGIN, YMARGIN, HIST_WIDTH, HIST_HEIGHT);
ip.setColor(Color.white);
ip.setRoi(frame.x - 1, frame.y, frame.width + 2, frame.height);
ip.fill();
ip.resetRoi();
ip.setColor(Color.black);
if(logScale) {
drawLogPlot(yMax > 0 ? yMax : newMaxCount, ip);
drawPlot(yMax > 0 ? yMax : newMaxCount, ip);
} else {
drawPlot(yMax > 0 ? yMax : newMaxCount, ip);
}
this.imp.updateAndDraw();
}
/*
void rescale() {
Graphics g = img.getGraphics();
plotScale *= 2;
if ((newMaxCount/plotScale)<50) {
plotScale = 1;
frame = new Rectangle(XMARGIN, YMARGIN, HIST_WIDTH, HIST_HEIGHT);
g.setColor(Color.white);
g.fillRect(frame.x, frame.y, frame.width, frame.height);
g.setColor(Color.black);
}
drawPlot(newMaxCount/plotScale, g);
//ImageProcessor ip = new ColorProcessor(img);
//this.imp.setProcessor(null, ip);
this.imp.setImage(img);
}
*/
@Override
public void actionPerformed(ActionEvent e) {
Object b = e.getSource();
if(b == live) {
toggleLiveMode();
} else if(b == rgb) {
changeChannel();
} else if(b == list) {
showList();
} else if(b == copy) {
copyToClipboard();
} else if(b == log) {
logScale = !logScale;
replot();
} else if(b == roiFilter) {
applyRoiAsFilter();
}
}
@Override
public void lostOwnership(Clipboard clipboard, Transferable contents) {
//
}
public int[] getHistogram() {
int[] hist = new int[histogram.length];
for(int i = 0; i < histogram.length; i++) {
hist[i] = (int) histogram[i];
}
return hist;
}
public double[] getXValues() {
double[] values = new double[stats.nBins];
for(int i = 0; i < stats.nBins; i++) {
values[i] = cal.getCValue(stats.histMin + i * stats.binSize);
}
return values;
}
private void toggleLiveMode() {
if(liveMode()) {
removeListeners();
} else {
enableLiveMode();
}
}
private void changeChannel() {
ImagePlus improc = WindowManager.getImage(srcImageID);
if(improc == null || improc.getType() != ImagePlus.COLOR_RGB) {
channel = INTENSITY;
} else {
channel++;
if(channel > BLUE) {
channel = INTENSITY;
}
showHistogram(improc, 256);
String name = this.imp.getTitle();
if(name.startsWith("Red ")) {
name = name.substring(4);
} else if(name.startsWith("Green ")) {
name = name.substring(6);
} else if(name.startsWith("Blue ")) {
name = name.substring(5);
}
switch(channel) {
case INTENSITY:
this.imp.setTitle(name);
break;
case RED:
this.imp.setTitle("Red " + name);
break;
case GREEN:
this.imp.setTitle("Green " + name);
break;
case BLUE:
this.imp.setTitle("Blue " + name);
break;
}
}
}
private boolean liveMode() {
return live != null && live.getForeground() == Color.red;
}
private void enableLiveMode() {
if(bgThread == null) {
srcImp = WindowManager.getImage(srcImageID);
if(srcImp == null) {
return;
}
bgThread = new Thread(this, "Live Histogram");
bgThread.setPriority(Math.max(bgThread.getPriority() - 3, Thread.MIN_PRIORITY));
bgThread.start();
imageUpdated(srcImp);
}
createListeners();
if(srcImp != null) {
imageUpdated(srcImp);
}
}
// these listeners are activated if there are in the source ImagePlus
@Override
public synchronized void mousePressed(MouseEvent e) {
doUpdate = true;
notify();
}
@Override
public synchronized void mouseDragged(MouseEvent e) {
doUpdate = true;
notify();
}
@Override
public synchronized void mouseClicked(MouseEvent e) {
doUpdate = true;
notify();
}
@Override
public synchronized void keyPressed(KeyEvent e) {
ImagePlus improc = WindowManager.getImage(srcImageID);
if(improc == null || improc.getRoi() != null) {
doUpdate = true;
notify();
}
}
// unused listeners
@Override
public void mouseReleased(MouseEvent e) {
//
}
@Override
public void mouseExited(MouseEvent e) {
//
}
@Override
public void mouseEntered(MouseEvent e) {
//
}
@Override
public void mouseMoved(MouseEvent e) {
//
}
@Override
public void imageOpened(ImagePlus imp) {
//
}
@Override
public void keyTyped(KeyEvent e) {
//
}
@Override
public void keyReleased(KeyEvent e) {
//
}
// This listener is called if the source image content is changed
@Override
public synchronized void imageUpdated(ImagePlus imp) {
if(imp == srcImp) {
doUpdate = true;
notify();
}
}
// If either the source image or this image are closed, exit
@Override
public void imageClosed(ImagePlus imp) {
if(imp == srcImp || imp == this.imp) {
if(bgThread != null) {
bgThread.interrupt();
}
bgThread = null;
removeListeners();
srcImp = null;
}
}
// the background thread for live plotting.
@Override
public void run() {
while(true) {
if(doUpdate && srcImp != null) {
if(srcImp.getRoi() != null) {
IJ.wait(50); //delay to make sure the roi has been updated
}
if(srcImp != null) {
if(srcImp.getBitDepth() == 16 && ImagePlus.getDefault16bitRange() != 0) {
showHistogram(srcImp, 256, 0, Math.pow(2, ImagePlus.getDefault16bitRange()) - 1);
} else {
showHistogram(srcImp, 256);
}
}
}
synchronized(this) {
if(doUpdate) {
doUpdate = false; //and loop again
} else {
try {
wait();
} //notify wakes up the thread
catch(InterruptedException e) { //interrupted tells the thread to exit
return;
}
}
}
}
}
private void createListeners() {
//IJ.log("createListeners");
if(srcImp == null) {
return;
}
ImageCanvas canvas = srcImp.getCanvas();
if(canvas == null) {
return;
}
canvas.addMouseListener(this);
canvas.addMouseMotionListener(this);
canvas.addKeyListener(this);
ImagePlus.addImageListener(this);
Font font = live.getFont();
live.setFont(new Font(font.getName(), Font.BOLD, font.getSize()));
live.setForeground(Color.red);
}
private void removeListeners() {
//IJ.log("removeListeners");
if(srcImp == null) {
return;
}
ImageCanvas canvas = srcImp.getCanvas();
canvas.removeMouseListener(this);
canvas.removeMouseMotionListener(this);
canvas.removeKeyListener(this);
ImagePlus.removeImageListener(this);
Font font = live.getFont();
live.setFont(new Font(font.getName(), Font.PLAIN, font.getSize()));
live.setForeground(Color.black);
}
private void applyRoiAsFilter() {
Rectangle roi = this.imp.getProcessor().getRoi();
int left = max(frame.x, roi.x) - frame.x;
int right = min((frame.x + frame.width - 1), (roi.x + roi.width - 1)) - frame.x;
int indexLeft = (int) (left * ((double) histogram.length) / HIST_WIDTH);
int indexRight = (int) (right * ((double) histogram.length) / HIST_WIDTH);
double leftVal = cal.getCValue(stats.histMin + indexLeft * stats.binSize); // param > val
double rightVal = cal.getCValue(stats.histMin + indexRight * stats.binSize); // param < val
IJResultsTable.getResultsTable().tableWindow.getFilter().addNewFilter(paramName, leftVal, rightVal);
}
}