1 package ij.plugin.frame;
3 import java.awt.event.*;
4 import java.awt.image.*;
10 import ij.plugin.frame.Recorder;
15 Runnable, ActionListener, AdjustmentListener, ItemListener {
17 static final int RED=0, BLACK_AND_WHITE=1, OVER_UNDER=2;
18 static final String[] modes = {
"Red",
"Black & White",
"Over/Under"};
19 static final double defaultMinThreshold = 85;
20 static final double defaultMaxThreshold = 170;
21 static boolean fill1 =
true;
22 static boolean fill2 =
true;
23 static boolean useBW =
true;
24 static boolean backgroundToNaN =
true;
25 static Frame instance;
26 static int mode = RED;
27 ThresholdPlot plot =
new ThresholdPlot();
32 int sliderRange = 256;
33 boolean doAutoAdjust,doReset,doApplyLut,doStateChange,doSet;
36 Button autoB, resetB, applyB, setB;
38 int previousImageType;
39 double previousMin, previousMax;
41 double minThreshold, maxThreshold;
42 Scrollbar minSlider, maxSlider;
48 boolean firstActivation;
61 Font font =
new Font(
"SansSerif", Font.PLAIN, 10);
62 GridBagLayout gridbag =
new GridBagLayout();
63 GridBagConstraints c =
new GridBagConstraints();
71 c.fill = GridBagConstraints.BOTH;
72 c.anchor = GridBagConstraints.CENTER;
73 c.insets =
new Insets(10, 10, 0, 10);
77 minSlider =
new Scrollbar(Scrollbar.HORIZONTAL, sliderRange/3, 1, 0, sliderRange);
82 c.fill = GridBagConstraints.HORIZONTAL;
83 c.insets =
new Insets(5, 10, 0, 0);
85 minSlider.addAdjustmentListener(
this);
86 minSlider.setUnitIncrement(1);
92 c.insets =
new Insets(5, 0, 0, 10);
93 label1 =
new Label(
" ", Label.RIGHT);
98 maxSlider =
new Scrollbar(Scrollbar.HORIZONTAL, sliderRange*2/3, 1, 0, sliderRange);
103 c.insets =
new Insets(0, 10, 0, 0);
105 maxSlider.addAdjustmentListener(
this);
106 maxSlider.setUnitIncrement(1);
112 c.insets =
new Insets(0, 0, 0, 10);
113 label2 =
new Label(
" ", Label.RIGHT);
114 label2.setFont(font);
118 choice =
new Choice();
119 for (
int i=0; i<modes.length; i++)
120 choice.addItem(modes[i]);
122 choice.addItemListener(
this);
126 c.insets =
new Insets(5, 5, 0, 5);
127 c.anchor = GridBagConstraints.CENTER;
128 c.fill = GridBagConstraints.NONE;
134 autoB =
new TrimmedButton(
"Auto",trim);
135 autoB.addActionListener(
this);
136 autoB.addKeyListener(ij);
138 applyB =
new TrimmedButton(
"Apply",trim);
139 applyB.addActionListener(
this);
140 applyB.addKeyListener(ij);
142 resetB =
new TrimmedButton(
"Reset",trim);
143 resetB.addActionListener(
this);
144 resetB.addKeyListener(ij);
146 setB =
new TrimmedButton(
"Set",trim);
147 setB.addActionListener(
this);
148 setB.addKeyListener(ij);
153 c.insets =
new Insets(0, 5, 10, 5);
159 firstActivation =
true;
162 thread =
new Thread(
this,
"ThresholdAdjuster");
170 public synchronized void adjustmentValueChanged(AdjustmentEvent e) {
171 if (e.getSource()==minSlider)
172 minValue = minSlider.getValue();
174 maxValue = maxSlider.getValue();
178 public synchronized void actionPerformed(ActionEvent e) {
179 Button b = (Button)e.getSource();
192 void setLutColor(
int mode) {
195 lutColor = ImageProcessor.RED_LUT;
197 case BLACK_AND_WHITE:
198 lutColor = ImageProcessor.BLACK_AND_WHITE_LUT;
201 lutColor = ImageProcessor.OVER_UNDER_LUT;
206 public synchronized void itemStateChanged(ItemEvent e) {
207 mode = choice.getSelectedIndex();
209 doStateChange =
true;
219 boolean minMaxChange =
false;
221 if (ip.getMin()!=previousMin || ip.getMax()!=previousMax)
223 previousMin = ip.getMin();
224 previousMax = ip.getMax();
226 int id = imp.
getID();
227 if (minMaxChange ||
id!=previousImageID || type!=previousImageType) {
229 minThreshold = ip.getMinThreshold();
230 maxThreshold = ip.getMaxThreshold();
231 ImageStatistics stats = plot.setHistogram(imp);
232 if (minThreshold==ip.NO_THRESHOLD)
233 autoSetLevels(ip, stats);
235 minThreshold = scaleDown(ip, minThreshold);
236 maxThreshold = scaleDown(ip, maxThreshold);
238 scaleUpAndSet(ip, minThreshold, maxThreshold);
239 updateLabels(imp, ip);
244 previousImageID = id;
245 previousImageType = type;
249 void autoSetLevels(ImageProcessor ip, ImageStatistics stats) {
250 if (stats==null || stats.histogram==null) {
251 minThreshold = defaultMinThreshold;
252 maxThreshold = defaultMaxThreshold;
255 int threshold = ip.getAutoThreshold(stats.histogram);
257 if ((stats.max-stats.dmode)>(stats.dmode-stats.min)) {
258 minThreshold = threshold;
259 maxThreshold = stats.max;
261 minThreshold = stats.min;
262 maxThreshold = threshold;
267 void scaleUpAndSet(ImageProcessor ip,
double minThreshold,
double maxThreshold) {
268 if (!(ip instanceof ByteProcessor) && minThreshold!=ImageProcessor.NO_THRESHOLD) {
269 double min = ip.getMin();
270 double max = ip.getMax();
272 minThreshold = min + (minThreshold/255.0)*(max-min);
273 maxThreshold = min + (maxThreshold/255.0)*(max-min);
275 minThreshold = ImageProcessor.NO_THRESHOLD;
277 ip.setThreshold(minThreshold, maxThreshold, lutColor);
281 double scaleDown(ImageProcessor ip,
double threshold) {
282 if (ip instanceof ByteProcessor)
284 double min = ip.getMin();
285 double max = ip.getMax();
287 return ((threshold-min)/(max-min))*255.0;
289 return ImageProcessor.NO_THRESHOLD;
293 double scaleUp(ImageProcessor ip,
double threshold) {
294 double min = ip.getMin();
295 double max = ip.getMax();
297 return min + (threshold/255.0)*(max-min);
299 return ImageProcessor.NO_THRESHOLD;
303 plot.minThreshold = minThreshold;
304 plot.maxThreshold = maxThreshold;
309 void updateLabels(
ImagePlus imp, ImageProcessor ip) {
310 double min = ip.getMinThreshold();
311 double max = ip.getMaxThreshold();
312 if (min==ImageProcessor.NO_THRESHOLD) {
321 if (((
int)min==min && (
int)max==max) || (ip instanceof ShortProcessor)) {
322 label1.setText(
""+(
int)min);
323 label2.setText(
""+(
int)max);
325 label1.setText(
""+
IJ.
d2s(min,2));
326 label2.setText(
""+
IJ.
d2s(max,2));
331 void updateScrollBars() {
332 minSlider.setValue((
int)minThreshold);
333 maxSlider.setValue((
int)maxThreshold);
337 void doMasking(
ImagePlus imp, ImageProcessor ip) {
338 ImageProcessor mask = imp.
getMask();
343 void adjustMinThreshold(
ImagePlus imp, ImageProcessor ip,
double value) {
345 double width = maxThreshold-minThreshold;
346 if (width<1.0) width = 1.0;
347 minThreshold = value;
348 maxThreshold = minThreshold+width;
349 if ((minThreshold+width)>255) {
350 minThreshold = 255-width;
351 maxThreshold = minThreshold+width;
352 minSlider.setValue((
int)minThreshold);
354 maxSlider.setValue((
int)maxThreshold);
355 scaleUpAndSet(ip, minThreshold, maxThreshold);
358 minThreshold = value;
359 if (maxThreshold<minThreshold) {
360 maxThreshold = minThreshold;
361 maxSlider.setValue((
int)maxThreshold);
363 scaleUpAndSet(ip, minThreshold, maxThreshold);
366 void adjustMaxThreshold(
ImagePlus imp, ImageProcessor ip,
int cvalue) {
367 maxThreshold = cvalue;
368 if (minThreshold>maxThreshold) {
369 minThreshold = maxThreshold;
370 minSlider.setValue((
int)minThreshold);
372 scaleUpAndSet(ip, minThreshold, maxThreshold);
375 void reset(
ImagePlus imp, ImageProcessor ip) {
376 plot.setHistogram(imp);
383 void doSet(
ImagePlus imp, ImageProcessor ip) {
384 double level1 = ip.getMinThreshold();
385 double level2 = ip.getMaxThreshold();
386 if (level1==ImageProcessor.NO_THRESHOLD) {
387 level1 = scaleUp(ip, defaultMinThreshold);
388 level2 = scaleUp(ip, defaultMaxThreshold);
391 int digits = (ip instanceof FloatProcessor)||cal.
calibrated()?2:0;
394 GenericDialog gd =
new GenericDialog(
"Set Threshold Levels");
395 gd.addNumericField(
"Lower Threshold Level: ", level1, digits);
396 gd.addNumericField(
"Upper Threshold Level: ", level2, digits);
398 if (gd.wasCanceled())
400 level1 = gd.getNextNumber();
401 level2 = gd.getNextNumber();
406 double minDisplay = ip.getMin();
407 double maxDisplay = ip.getMax();
409 double minValue = ip.getMin();
410 double maxValue = ip.getMax();
411 if (level1<minValue) level1 = minValue;
412 if (level2>maxValue) level2 = maxValue;
413 boolean outOfRange = level1<minDisplay || level2>maxDisplay;
415 plot.setHistogram(imp);
417 ip.setMinAndMax(minDisplay, maxDisplay);
419 minThreshold = scaleDown(ip,level1);
420 maxThreshold = scaleDown(ip,level2);
421 scaleUpAndSet(ip, minThreshold, maxThreshold);
425 Recorder.
record(
"setThreshold", ip.getMinThreshold(), ip.getMaxThreshold());
427 Recorder.
record(
"setThreshold", (
int)ip.getMinThreshold(), (int)ip.getMaxThreshold());
431 void changeState(
ImagePlus imp, ImageProcessor ip) {
432 scaleUpAndSet(ip, minThreshold, maxThreshold);
436 void autoThreshold(
ImagePlus imp, ImageProcessor ip) {
445 GenericDialog gd =
new GenericDialog(
"NaN Backround");
446 gd.addCheckbox(
"Set Background Pixels to NaN", backgroundToNaN);
448 if (gd.wasCanceled()) {
452 backgroundToNaN = gd.getNextBoolean();
454 IJ.
run(
"NaN Background");
459 }
catch (Exception e)
464 static final int RESET=0, AUTO=1, HIST=2, APPLY=3, STATE_CHANGE=4, MIN_THRESHOLD=5, MAX_THRESHOLD=6, SET=7;
471 catch(InterruptedException e) {}
483 if (doReset) action = RESET;
484 else if (doAutoAdjust) action = AUTO;
485 else if (doApplyLut) action = APPLY;
486 else if (doStateChange) action = STATE_CHANGE;
487 else if (doSet) action = SET;
488 else if (minValue>=0) action = MIN_THRESHOLD;
489 else if (maxValue>=0) action = MAX_THRESHOLD;
494 doAutoAdjust =
false;
496 doStateChange =
false;
513 case RESET: reset(imp, ip);
break;
514 case AUTO: autoThreshold(imp, ip);
break;
515 case APPLY: apply(imp);
break;
516 case STATE_CHANGE: changeState(imp, ip);
break;
517 case SET: doSet(imp, ip);
break;
518 case MIN_THRESHOLD: adjustMinThreshold(imp, ip, min);
break;
519 case MAX_THRESHOLD: adjustMaxThreshold(imp, ip, max);
break;
522 updateLabels(imp, ip);
523 ip.setLutAnimation(
true);
527 public void windowClosing(WindowEvent e) {
541 public void windowActivated(WindowEvent e) {
542 super.windowActivated(e);
545 if (!firstActivation) {
549 firstActivation =
false;
556 class ThresholdPlot
extends Canvas implements Measurements, MouseListener {
558 static final int WIDTH = 256, HEIGHT=48;
559 double minThreshold = 85;
560 double maxThreshold = 170;
568 public ThresholdPlot() {
569 addMouseListener(
this);
570 setSize(WIDTH+1, HEIGHT+1);
575 public Dimension getPreferredSize() {
576 return new Dimension(WIDTH+1, HEIGHT+1);
579 ImageStatistics setHistogram(ImagePlus imp) {
580 ImageProcessor ip = imp.getProcessor();
581 if (!(ip instanceof ByteProcessor)) {
582 double min = ip.getMin();
583 double max = ip.getMax();
584 ip.setMinAndMax(min, max);
585 Rectangle r = ip.getRoi();
586 ip =
new ByteProcessor(ip.createImage());
589 ip.setMask(imp.getMask());
590 ImageStatistics stats = ImageStatistics.getStatistics(ip, AREA+MIN_MAX+MODE, null);
592 histogram = stats.histogram;
593 for (
int i = 0; i < stats.nBins; i++)
594 if ((histogram[i] > maxCount2) && (i != stats.mode))
595 maxCount2 = histogram[i];
596 hmax = stats.maxCount;
597 if ((hmax>(maxCount2 * 2)) && (maxCount2 != 0)) {
598 hmax = (int)(maxCount2 * 1.5);
599 histogram[stats.mode] = hmax;
603 ColorModel cm = ip.getColorModel();
604 if (!(cm instanceof IndexColorModel))
606 IndexColorModel icm = (IndexColorModel)cm;
607 int mapSize = icm.getMapSize();
610 byte[] r =
new byte[256];
611 byte[] g =
new byte[256];
612 byte[] b =
new byte[256];
616 hColors =
new Color[256];
617 for (
int i=0; i<256; i++)
618 hColors[i] =
new Color(r[i]&255, g[i]&255, b[i]&255);
622 public void update(Graphics g) {
626 public void paint(Graphics g) {
627 if (histogram!=null) {
628 if (os==null && hmax>0) {
629 os = createImage(WIDTH,HEIGHT);
630 osg = os.getGraphics();
631 osg.setColor(Color.white);
632 osg.fillRect(0, 0, WIDTH, HEIGHT);
633 osg.setColor(Color.gray);
634 for (
int i = 0; i < WIDTH; i++) {
635 if (hColors!=null) osg.setColor(hColors[i]);
636 osg.drawLine(i, HEIGHT, i, HEIGHT - ((
int)(HEIGHT * histogram[i])/hmax));
640 g.drawImage(os, 0, 0,
this);
642 g.setColor(Color.white);
643 g.fillRect(0, 0, WIDTH, HEIGHT);
645 g.setColor(Color.black);
646 g.drawRect(0, 0, WIDTH, HEIGHT);
647 if (mode==ThresholdAdjuster.RED)
648 g.setColor(Color.red);
649 else if (mode==ThresholdAdjuster.OVER_UNDER) {
650 g.setColor(Color.blue);
651 g.drawRect(1, 1, (
int)minThreshold-2, HEIGHT);
652 g.drawRect(1, 0, (
int)minThreshold-2, 0);
653 g.setColor(Color.green);
654 g.drawRect((
int)maxThreshold+1, 1, WIDTH-(
int)maxThreshold, HEIGHT);
655 g.drawRect((
int)maxThreshold+1, 0, WIDTH-(
int)maxThreshold, 0);
658 g.drawRect((
int)minThreshold, 1, (
int)(maxThreshold-minThreshold), HEIGHT);
659 g.drawLine((
int)minThreshold, 0, (
int)maxThreshold, 0);
662 public void mousePressed(MouseEvent e) {}
663 public void mouseReleased(MouseEvent e) {}
664 public void mouseExited(MouseEvent e) {}
665 public void mouseClicked(MouseEvent e) {}
666 public void mouseEntered(MouseEvent e) {}