Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
ContrastAdjuster.java
1 package ij.plugin.frame;
2 import java.awt.*;
3 import java.awt.event.*;
4 import java.awt.image.*;
5 import ij.*;
6 import ij.plugin.*;
7 import ij.process.*;
8 import ij.gui.*;
9 import ij.measure.*;
10 import javax.swing.*;
11 
14 public class ContrastAdjuster
15  extends PlugInFrame
16  implements Runnable, ActionListener, AdjustmentListener, ItemListener
17 {
18 
19  static final int AUTO_THRESHOLD = 5000;
20  static final String[] channelLabels = {"Red", "Green", "Blue", "Cyan", "Magenta", "Yellow", "RGB"};
21  static final int[] channelConstants = {4, 2, 1, 3, 5, 6, 7};
22 
23  ContrastPlot plot = new ContrastPlot();
24  Thread thread;
25  private static Frame instance;
26 
27  int minSliderValue=-1, maxSliderValue=-1, brightnessValue=-1, contrastValue=-1;
28  int sliderRange = 256;
29  boolean doAutoAdjust,doReset,doSet,doApplyLut,doThreshold,doUpdate;
30 
31  JPanel panel, tPanel;
32  Button autoB, resetB, setB, applyB, threshB, updateB;
33  int previousImageID;
34  int previousType;
35  Object previousSnapshot;
36  ImageJ ij;
37  double min, max;
38  double previousMin, previousMax;
39  double defaultMin, defaultMax;
40  int contrast, brightness;
41  boolean RGBImage;
42  Scrollbar minSlider, maxSlider, contrastSlider, brightnessSlider;
43  boolean done;
44  int autoThreshold;
45  GridBagLayout gridbag;
46  GridBagConstraints c;
47  int y = 0;
48  boolean windowLevel, balance;
49  int channels = 7; // RGB
50  Choice choice;
51 
59  {
60  super("Brightness & Contrast");
61 
62  }//end ContrastAdjuster()
63 
64 
71  public void run(String arg)
72  { ij = IJ.getInstance();
73  SliderPane pane = null;
74  windowLevel = arg.equals("wl");
75  balance = arg.equals("balance");
76  if (windowLevel) {
77  setTitle("W&L");
78  } else if (balance) {
79  setTitle("Color");
80  channels = 4;
81  }
82 
83  if (instance != null) {
84  instance.toFront();
85  return;
86  }
87  instance = this;
89 
90  gridbag = new GridBagLayout();
91  c = new GridBagConstraints();
92  getContentPane().setLayout(gridbag);
93 
94  // plot
95  c.gridx = 0;
96  y = 0;
97  c.gridy = y++;
98  c.fill = GridBagConstraints.BOTH;
99  c.anchor = GridBagConstraints.CENTER;
100  c.insets = new Insets(4, 4, 4, 4);
101  gridbag.setConstraints(plot, c);
102  getContentPane().add(plot);
103 
104  // min slider
105  if (!windowLevel) {
106  minSlider = new Scrollbar(Scrollbar.HORIZONTAL, sliderRange/2, 1, 0, sliderRange);
107  pane = new SliderPane(minSlider, "Minimum: ");
108  c.gridy = y++;
109  gridbag.setConstraints(pane, c);
110  getContentPane().add(pane);
111  minSlider.addAdjustmentListener(this);
112  minSlider.setUnitIncrement(1);
113  }
114 
115  // max slider
116  if (!windowLevel) {
117  maxSlider = new Scrollbar(Scrollbar.HORIZONTAL, sliderRange/2, 1, 0, sliderRange);
118  pane = new SliderPane(maxSlider, "Maximum: ");
119  c.gridy = y++;
120  gridbag.setConstraints(pane, c);
121  getContentPane().add(pane);
122  maxSlider.addAdjustmentListener(this);
123  maxSlider.setUnitIncrement(1);
124  }
125 
126  // brightness slider
127  brightnessSlider = new Scrollbar(Scrollbar.HORIZONTAL, sliderRange/2, 1, 0, sliderRange);
128  pane = new SliderPane(brightnessSlider, windowLevel ? "Level: " : "Brightness: ");
129  c.gridy = y++;
130  gridbag.setConstraints(pane, c);
131  getContentPane().add(pane);
132  brightnessSlider.addAdjustmentListener(this);
133  brightnessSlider.setUnitIncrement(1);
134 
135  // contrast slider
136  if (!balance) {
137  contrastSlider = new Scrollbar(Scrollbar.HORIZONTAL, sliderRange/2, 1, 0, sliderRange);
138  pane = new SliderPane(contrastSlider, windowLevel ? "Window: " : "Contrast: ");
139  c.gridy = y++;
140  gridbag.setConstraints(pane, c);
141  getContentPane().add(pane);
142  contrastSlider.addAdjustmentListener(this);
143  contrastSlider.setUnitIncrement(1);
144  }
145 
146  // color channel popup menu
147  if (balance) {
148  c.gridy = y++;
149  choice = new Choice();
150  for (int i=0; i<channelLabels.length; i++) {
151  choice.addItem(channelLabels[i]);
152  }
153  gridbag.setConstraints(choice, c);
154  choice.addItemListener(this);
155  getContentPane().add(choice);
156  }
157 
158  // buttons
159  int trim = IJ.isMacOSX()?20:0;
160  panel = new JPanel();
161  panel.setLayout(new GridLayout(0,2, 0, 0));
162  autoB = new TrimmedButton("Auto",trim);
163  autoB.addActionListener(this);
164  autoB.addKeyListener(ij);
165  panel.add(autoB);
166  resetB = new TrimmedButton("Reset",trim);
167  resetB.addActionListener(this);
168  resetB.addKeyListener(ij);
169  panel.add(resetB);
170  setB = new TrimmedButton("Set",trim);
171  setB.addActionListener(this);
172  setB.addKeyListener(ij);
173  panel.add(setB);
174  applyB = new TrimmedButton("Apply",trim);
175  applyB.addActionListener(this);
176  applyB.addKeyListener(ij);
177  panel.add(applyB);
178  if (!windowLevel && !balance) {
179  threshB = new TrimmedButton("Thresh",trim);
180  threshB.addActionListener(this);
181  threshB.addKeyListener(ij);
182  panel.add(threshB);
183  updateB = new TrimmedButton("Update",trim);
184  updateB.addActionListener(this);
185  updateB.addKeyListener(ij);
186  panel.add(updateB);
187  }
188  c.gridy = y++;
189  gridbag.setConstraints(panel, c);
190  getContentPane().add(panel);
191 
192  addKeyListener(ij); // ImageJ handles keyboard shortcuts
193  setVisible(true);
194 
195  thread = new Thread(this, "ContrastAdjuster");
196  thread.start();
197  setup();
198 
199  }//end run()
200 
201 
208  void setup()
209  {
210  ImagePlus imp = ij.getImagePlus();
211  if (imp!=null) {
212  //IJ.write("setup");
213  ImageProcessor ip = imp.getProcessor();
214  setup(imp);
215  updatePlot();
216  updateLabels(imp, ip);
217  imp.updateAndDraw();
218  }
219 
220  }//end setup()
221 
222 
229  public synchronized void adjustmentValueChanged(AdjustmentEvent e)
230  {
231  if (e.getSource()==minSlider)
232  minSliderValue = minSlider.getValue();
233  else if (e.getSource()==maxSlider)
234  maxSliderValue = maxSlider.getValue();
235  else if (e.getSource()==contrastSlider)
236  contrastValue = contrastSlider.getValue();
237  else
238  brightnessValue = brightnessSlider.getValue();
239  notify();
240 
241  }//end adjustmentValueChanged()
242 
243 
250  public synchronized void actionPerformed(ActionEvent e) {
251  Button b = (Button)e.getSource();
252  if (b==null) return;
253  if (b==resetB)
254  doReset = true;
255  else if (b==autoB)
256  doAutoAdjust = true;
257  else if (b==setB)
258  doSet = true;
259  else if (b==applyB)
260  doApplyLut = true;
261  else if (b==threshB)
262  doThreshold = true;
263  else if (b==updateB)
264  doUpdate = true;
265  notify();
266 
267  }//end actionPerformed()
268 
269 
276  ImageProcessor setup(ImagePlus imp)
277  {
278  ImageProcessor ip = imp.getProcessor();
279  int type = imp.getType();
280  RGBImage = type==ImagePlus.COLOR_RGB;
281  boolean snapshotChanged = RGBImage && previousSnapshot!=null && ((ColorProcessor)ip).getSnapshotPixels()!=previousSnapshot;
282  if (imp.getID()!=previousImageID || snapshotChanged || type!=previousType)
283  setupNewImage(imp, ip);
284  previousImageID = imp.getID();
285  previousType = type;
286  return ip;
287 
288  }//end setup()
289 
290 
297  void setupNewImage(ImagePlus imp, ImageProcessor ip)
298  {
299  previousMin = min;
300  previousMax = max;
301  if (RGBImage) {
302  ip.snapshot();
303  previousSnapshot = ((ColorProcessor)ip).getSnapshotPixels();
304  } else
305  previousSnapshot = null;
306  double min2 = ip.getMin();
307  double max2 = ip.getMax();
308  if (imp.getType()==ImagePlus.COLOR_RGB)
309  {min2=0.0; max2=255.0;}
310  if ((ip instanceof ShortProcessor) || (ip instanceof FloatProcessor)) {
311  ip.resetMinAndMax();
312  defaultMin = ip.getMin();
313  defaultMax = ip.getMax();
314  } else {
315  defaultMin = 0;
316  defaultMax = 255;
317  }
318  setMinAndMax(ip, min2, max2);
319  min = ip.getMin();
320  max = ip.getMax();
321  if (IJ.debugMode) {
322  IJ.log("min: " + min);
323  IJ.log("max: " + max);
324  IJ.log("defaultMin: " + defaultMin);
325  IJ.log("defaultMax: " + defaultMax);
326  }
327  plot.defaultMin = defaultMin;
328  plot.defaultMax = defaultMax;
329  //plot.histogram = null;
330  updateScrollBars(null);
331  if (!doReset)
332  plotHistogram(imp);
333  autoThreshold = 0;
334 
335  }//end setupNewImage()
336 
337 
344  void setMinAndMax(ImageProcessor ip, double min, double max)
345  {
346  if (channels!=7 && ip instanceof ColorProcessor)
347  ((ColorProcessor)ip).setMinAndMax(min, max, channels);
348  else
349  ip.setMinAndMax(min, max);
350 
351  }//end setMinAndMax()
352 
353 
360  void updatePlot()
361  {
362  plot.min = min;
363  plot.max = max;
364  plot.repaint();
365 
366  }//end updatePlot()
367 
368 
369  void updateLabels(ImagePlus imp, ImageProcessor ip) {
370  /*
371  double min = ip.getMin();
372  double max = ip.getMax();
373  int type = imp.getType();
374  Calibration cal = imp.getCalibration();
375  boolean realValue = type==ImagePlus.GRAY32;
376  if (cal.calibrated()) {
377  min = cal.getCValue((int)min);
378  max = cal.getCValue((int)max);
379  if (type!=ImagePlus.GRAY16)
380  realValue = true;
381  }
382  int digits = realValue?2:0;
383  if (windowLevel) {
384  //IJ.log(min+" "+max);
385  double window = max-min;
386  double level = min+(window)/2.0;
387  windowLabel.setText(IJ.d2s(window, digits));
388  levelLabel.setText(IJ.d2s(level, digits));
389  } else {
390  minLabel.setText(IJ.d2s(min, digits));
391  maxLabel.setText(IJ.d2s(max, digits));
392  }*/
393  }
394 
395 
402  void updateScrollBars(Scrollbar sb)
403  {
404  if (sb==null || sb!=contrastSlider) {
405  double mid = sliderRange/2;
406  double c = ((defaultMax-defaultMin)/(max-min))*mid;
407  if (c>mid)
408  c = sliderRange - ((max-min)/(defaultMax-defaultMin))*mid;
409  contrast = (int)c;
410  if (contrastSlider!=null)
411  contrastSlider.setValue(contrast);
412  }
413  if (sb==null || sb!=brightnessSlider) {
414  double level = min + (max-min)/2.0;
415  double normalizedLevel = 1.0 - (level - defaultMin)/(defaultMax-defaultMin);
416  brightness = (int)(normalizedLevel*sliderRange);
417  brightnessSlider.setValue(brightness);
418  }
419  if (minSlider!=null && (sb==null || sb!=minSlider))
420  minSlider.setValue(scaleDown(min));
421  if (maxSlider!=null && (sb==null || sb!=maxSlider))
422  maxSlider.setValue(scaleDown(max));
423 
424  }//end updateScrollBars()
425 
426 
433  int scaleDown(double v)
434  {
435  if (v<defaultMin) v = defaultMin;
436  if (v>defaultMax) v = defaultMax;
437  return (int)((v-defaultMin)*255.0/(defaultMax-defaultMin));
438 
439  }//end scaleDown()
440 
441 
448  void doMasking(ImagePlus imp, ImageProcessor ip)
449  {
450  ImageProcessor mask = imp.getMask();
451  if (mask!=null)
452  ip.reset(mask);
453  }//end doMasking()
454 
455 
462  void adjustMin(ImagePlus imp, ImageProcessor ip, double minvalue)
463  {
464  //IJ.log((int)min+" "+(int)max+" "+minvalue+" "+defaultMin+" "+defaultMax);
465  min = defaultMin + minvalue*(defaultMax-defaultMin)/255.0;
466  if (max>defaultMax)
467  max = defaultMax;
468  if (min>max)
469  max = min;
470  setMinAndMax(ip, min, max);
471  if (min==max)
472  setThreshold(ip);
473  if (RGBImage) doMasking(imp, ip);
474  updateScrollBars(minSlider);
475 
476  }//end adjustMin()
477 
478 
485  void adjustMax(ImagePlus imp, ImageProcessor ip, double maxvalue)
486  {
487  //IJ.log(min+" "+max+" "+maxvalue);
488  max = defaultMin + maxvalue*(defaultMax-defaultMin)/255.0;
489  if (min<0)
490  min = 0;
491  if (max<min)
492  min = max;
493  setMinAndMax(ip, min, max);
494  if (min==max)
495  setThreshold(ip);
496  if (RGBImage) doMasking(imp, ip);
497  updateScrollBars(maxSlider);
498 
499  }//end adjustMax()
500 
501 
508  void adjustBrightness(ImagePlus imp, ImageProcessor ip, double bvalue)
509  {
510  double center = defaultMin + (defaultMax-defaultMin)*((sliderRange-bvalue)/sliderRange);
511  double width = max-min;
512  min = center - width/2.0;
513  max = center + width/2.0;
514  setMinAndMax(ip, min, max);
515  if (min==max)
516  setThreshold(ip);
517  if (RGBImage) doMasking(imp, ip);
518  updateScrollBars(brightnessSlider);
519 
520  }//end adjustBrightness()
521 
522 
529  void adjustContrast(ImagePlus imp, ImageProcessor ip, int cvalue)
530  {
531  double slope;
532  double center = min + (max-min)/2.0;
533  double range = defaultMax-defaultMin;
534  double mid = sliderRange/2;
535  if (cvalue<=mid)
536  slope = cvalue/mid;
537  else
538  slope = mid/(sliderRange-cvalue);
539  if (slope>0.0) {
540  min = center-(0.5*range)/slope;
541  max = center+(0.5*range)/slope;
542  }
543  setMinAndMax(ip, min, max);
544  if (RGBImage) doMasking(imp, ip);
545  updateScrollBars(contrastSlider);
546 
547  }//end adjustContrast()
548 
549 
556  void reset(ImagePlus imp, ImageProcessor ip)
557  {
558  if (RGBImage)
559  ip.reset();
560  if ((ip instanceof ShortProcessor) || (ip instanceof FloatProcessor)) {
561  ip.resetMinAndMax();
562  defaultMin = ip.getMin();
563  defaultMax = ip.getMax();
564  plot.defaultMin = defaultMin;
565  plot.defaultMax = defaultMax;
566  }
567  min = defaultMin;
568  max = defaultMax;
569  setMinAndMax(ip, min, max);
570  updateScrollBars(null);
571  plotHistogram(imp);
572  autoThreshold = 0;
573  if (Recorder.record)
574  Recorder.record("resetMinAndMax");
575 
576  }//end reset()
577 
578 
585  void update(ImagePlus imp, ImageProcessor ip)
586  {
587  if (previousMin==0.0 && previousMax==0.0 || imp.getType()!=previousType)
588  IJ.beep();
589  else {
590  min = previousMin;
591  max = previousMax;
592  setMinAndMax(ip, min, max);
593  updateScrollBars(null);
594  plotHistogram(imp);
595  }
596 
597  }//end update()
598 
599 
606  void plotHistogram(ImagePlus imp)
607  {
608  ImageStatistics stats;
609  if (balance && (channels==4 || channels==2 || channels==1) && imp.getType()==ImagePlus.COLOR_RGB) {
610  int w = imp.getWidth();
611  int h = imp.getHeight();
612  byte[] r = new byte[w*h];
613  byte[] g = new byte[w*h];
614  byte[] b = new byte[w*h];
615  ((ColorProcessor)imp.getProcessor()).getRGB(r,g,b);
616  byte[] pixels=null;
617  if (channels==4)
618  pixels = r;
619  else if (channels==2)
620  pixels = g;
621  else if (channels==1)
622  pixels = b;
623  ImageProcessor ip = new ByteProcessor(w, h, pixels, null);
624  stats = ImageStatistics.getStatistics(ip, 0, imp.getCalibration());
625  } else
626  stats = imp.getStatistics();
627  plot.setHistogram(stats);
628 
629  }//end plotHistogram()
630 
631 
638  void apply(ImagePlus imp, ImageProcessor ip) {
639  if (RGBImage)
640  imp.unlock();
641  if (!imp.lock())
642  return;
643  if (imp.getType()==ImagePlus.COLOR_RGB) {
644  ip.snapshot();
645  reset(imp, ip);
646  imp.changes = true;
647  imp.unlock();
648  return;
649  }
650  if (imp.getType()!=ImagePlus.GRAY8) {
651  IJ.beep();
652  IJ.showStatus("Apply requires an 8-bit grayscale image");
653  imp.unlock();
654  return;
655  }
656  int[] table = new int[256];
657  int min = (int)ip.getMin();
658  int max = (int)ip.getMax();
659  for (int i=0; i<256; i++) {
660  if (i<=min)
661  table[i] = 0;
662  else if (i>=max)
663  table[i] = 255;
664  else
665  table[i] = (int)(((double)(i-min)/(max-min))*255);
666  }
667  ip.setRoi(imp.getRoi());
668  if (ip.getMask()!=null) ip.snapshot();
669  ip.applyTable(table);
670  ip.reset(ip.getMask());
671  reset(imp, ip);
672  imp.changes = true;
673  imp.unlock();
674 
675  }//end apply()
676 
677 
684  void threshold(ImagePlus imp, ImageProcessor ip)
685  {
686  int threshold = (int)((defaultMax-defaultMin)/2.0);
687  min = threshold;
688  max = threshold;
689  setMinAndMax(ip, min, max);
690  setThreshold(ip);
691  updateScrollBars(null);
692 
693  }//end threshold()
694 
695 
702  void setThreshold(ImageProcessor ip)
703  {
704  if (!(ip instanceof ByteProcessor))
705  return;
706  if (((ByteProcessor)ip).isInvertedLut())
707  ip.setThreshold(max, 255, ImageProcessor.NO_LUT_UPDATE);
708  else
709  ip.setThreshold(0, max, ImageProcessor.NO_LUT_UPDATE);
710 
711  }//end setThreshold()
712 
713 
720  void autoAdjust(ImagePlus imp, ImageProcessor ip)
721  {
722  if (RGBImage)
723  ip.reset();
724  Calibration cal = imp.getCalibration();
725  imp.setCalibration(null);
726  ImageStatistics stats = imp.getStatistics(); // get uncalibrated stats
727  imp.setCalibration(cal);
728  int[] histogram = stats.histogram;
729  if (autoThreshold<10)
730  autoThreshold = AUTO_THRESHOLD;
731  else
732  autoThreshold /= 2;
733  int threshold = stats.pixelCount/autoThreshold;
734  int i = -1;
735  boolean found = false;
736  do {
737  i++;
738  found = histogram[i] > threshold;
739  } while (!found && i<255);
740  int hmin = i;
741  i = 256;
742  do {
743  i--;
744  found = histogram[i] > threshold;
745  } while (!found && i>0);
746  int hmax = i;
747  if (hmax>=hmin) {
748  imp.killRoi();
749  min = stats.histMin+hmin*stats.binSize;
750  max = stats.histMin+hmax*stats.binSize;
751  if (min==max)
752  {min=stats.min; max=stats.max;}
753  setMinAndMax(ip, min, max);
754  } else {
755  reset(imp, ip);
756  return;
757  }
758  updateScrollBars(null);
759  Roi roi = imp.getRoi();
760  if (roi!=null) {
761  ImageProcessor mask = roi.getMask();
762  if (mask!=null)
763  ip.reset(mask);
764  }
765 
766  }//end autoAdjust()
767 
768 
775  void setMinAndMax(ImagePlus imp, ImageProcessor ip)
776  {
777  min = ip.getMin();
778  max = ip.getMax();
779  Calibration cal = imp.getCalibration();
780  int digits = (ip instanceof FloatProcessor)||cal.calibrated()?2:0;
781  double minValue = cal.getCValue(min);
782  double maxValue = cal.getCValue(max);
783  GenericDialog gd = new GenericDialog("Set Min and Max");
784  gd.addNumericField("Minimum Displayed Value: ", minValue, digits);
785  gd.addNumericField("Maximum Displayed Value: ", maxValue, digits);
786  gd.showDialog();
787  if (gd.wasCanceled())
788  return;
789  minValue = gd.getNextNumber();
790  maxValue = gd.getNextNumber();
791  minValue = cal.getRawValue(minValue);
792  maxValue = cal.getRawValue(maxValue);
793  if (maxValue>=minValue) {
794  min = minValue;
795  max = maxValue;
796  setMinAndMax(ip, min, max);
797  updateScrollBars(null);
798  if (RGBImage) doMasking(imp, ip);
799  if (Recorder.record)
800  Recorder.record("setMinAndMax", (int)min, (int)max);
801  }
802 
803  }//end setMinAndMax()
804 
805 
812  void setWindowLevel(ImagePlus imp, ImageProcessor ip)
813  {
814  min = ip.getMin();
815  max = ip.getMax();
816  Calibration cal = imp.getCalibration();
817  int digits = (ip instanceof FloatProcessor)||cal.calibrated()?2:0;
818  double minValue = cal.getCValue(min);
819  double maxValue = cal.getCValue(max);
820  //IJ.log("setWindowLevel: "+min+" "+max);
821  double windowValue = maxValue - minValue;
822  double levelValue = minValue + windowValue/2.0;
823  GenericDialog gd = new GenericDialog("Set W&L");
824  gd.addNumericField("Window Center (Level): ", levelValue, digits);
825  gd.addNumericField("Window Width: ", windowValue, digits);
826  gd.showDialog();
827  if (gd.wasCanceled())
828  return;
829  levelValue = gd.getNextNumber();
830  windowValue = gd.getNextNumber();
831  minValue = levelValue-(windowValue/2.0);
832  maxValue = levelValue+(windowValue/2.0);
833  minValue = cal.getRawValue(minValue);
834  maxValue = cal.getRawValue(maxValue);
835  if (maxValue>=minValue) {
836  min = minValue;
837  max = maxValue;
838  setMinAndMax(ip, minValue, maxValue);
839  updateScrollBars(null);
840  if (RGBImage) doMasking(imp, ip);
841  if (Recorder.record)
842  Recorder.record("setMinAndMax", (int)min, (int)max);
843  }
844 
845  }//end setWindowLevel()
846 
847  // Operation flags
848  static final int RESET=0, AUTO=1, SET=2, APPLY=3, THRESHOLD=4, MIN=5, MAX=6,
849  BRIGHTNESS=7, CONTRAST=8, UPDATE=9;
850 
851 
858  public void run()
859  {
860  while (!done) {
861  synchronized(this) {
862  try {wait();}
863  catch(InterruptedException e) {}
864  }
865  doUpdate();
866  }
867 
868  }//end run()
869 
870 
877  void doUpdate()
878  {
879  ImagePlus imp;
880  ImageProcessor ip;
881  int action;
882  int minvalue = minSliderValue;
883  int maxvalue = maxSliderValue;
884  int bvalue = brightnessValue;
885  int cvalue = contrastValue;
886  if (doReset) action = RESET;
887  else if (doAutoAdjust) action = AUTO;
888  else if (doSet) action = SET;
889  else if (doApplyLut) action = APPLY;
890  else if (doThreshold) action = THRESHOLD;
891  else if (doUpdate) action = UPDATE;
892  else if (minSliderValue>=0) action = MIN;
893  else if (maxSliderValue>=0) action = MAX;
894  else if (brightnessValue>=0) action = BRIGHTNESS;
895  else if (contrastValue>=0) action = CONTRAST;
896  else return;
897  minSliderValue = maxSliderValue = brightnessValue = contrastValue = -1;
898  doReset = doAutoAdjust = doSet = doApplyLut = doThreshold = doUpdate = false;
899  imp = ij.getImagePlus();
900  if (imp==null) {
901  IJ.beep();
902  IJ.showStatus("No image");
903  return;
904  }
905  if (action!=UPDATE)
906  ip = setup(imp);
907  else
908  ip = imp.getProcessor();
909  if (RGBImage && !imp.lock())
910  {imp=null; return;}
911  //IJ.write("setup: "+(imp==null?"null":imp.getTitle()));
912  switch (action) {
913  case RESET: reset(imp, ip); break;
914  case AUTO: autoAdjust(imp, ip); break;
915  case SET: if (windowLevel) setWindowLevel(imp, ip); else setMinAndMax(imp, ip); break;
916  case APPLY: apply(imp, ip); break;
917  case THRESHOLD: threshold(imp, ip); break;
918  case UPDATE: update(imp, ip); break;
919  case MIN: adjustMin(imp, ip, minvalue); break;
920  case MAX: adjustMax(imp, ip, maxvalue); break;
921  case BRIGHTNESS: adjustBrightness(imp, ip, bvalue); break;
922  case CONTRAST: adjustContrast(imp, ip, cvalue); break;
923  }
924  updatePlot();
925  updateLabels(imp, ip);
926  imp.updateAndDraw();
927  if (RGBImage)
928  imp.unlock();
929 
930  }//end doUpdate()
931 
932 
939  public void windowClosing(WindowEvent e)
940  {
941  close();
942  }
943 
950  public void close()
951  {
952  super.close();
953  instance = null;
954  done = true;
955  synchronized(this) {
956  notify();
957  }
958  }
959 
960 
967  public void windowActivated(WindowEvent e)
968  {
969  //windowActivated(e);
970  setup();
971 
972  }//end windowActivated()
973 
974 
981  public synchronized void itemStateChanged(ItemEvent e)
982  {
983  channels = channelConstants[choice.getSelectedIndex()];
984  doReset = true;
985  notify();
986 
987  }//end itemStateChanged()
988 
989 
996  public void updateAndDraw()
997  {
998  previousImageID = 0;
999  toFront();
1000 
1001  }//end updateAndDraw()
1002 
1003 
1004 } // ContrastAdjuster class
1005 
1006 
1007 class ContrastPlot extends Canvas implements MouseListener
1008 {
1009 
1010  static final int WIDTH = 200, HEIGHT=64;
1011  double defaultMin = 0;
1012  double defaultMax = 255;
1013  double min = 0;
1014  double max = 255;
1015  int[] histogram;
1016  int hmax;
1017  Image os;
1018  Graphics osg;
1019 
1020  public ContrastPlot() {
1021  addMouseListener(this);
1022  setSize(WIDTH+1, HEIGHT+1);
1023  }
1024 
1027  public Dimension getPreferredSize() {
1028  return new Dimension(WIDTH+1, HEIGHT+1);
1029  }
1030 
1031  void setHistogram(ImageStatistics stats) {
1032  long startTime = System.currentTimeMillis();
1033  histogram = stats.histogram;
1034  if (histogram.length!=256)
1035  {histogram=null; return;}
1036  for (int i=0; i<128; i++)
1037  histogram[i] = (histogram[2*i]+histogram[2*i+1])/2;
1038  int maxCount = 0;
1039  int mode = 0;
1040  for (int i=0; i<128; i++) {
1041  if (histogram[i]>maxCount) {
1042  maxCount = histogram[i];
1043  mode = i;
1044  }
1045  }
1046  int maxCount2 = 0;
1047  for (int i=0; i<128; i++) {
1048  if ((histogram[i]>maxCount2) && (i!=mode))
1049  maxCount2 = histogram[i];
1050  }
1051  hmax = stats.maxCount;
1052  if ((hmax>(maxCount2*2)) && (maxCount2!=0)) {
1053  hmax = (int)(maxCount2*1.5);
1054  histogram[mode] = hmax;
1055  }
1056  os = null;
1057 
1058  }
1059 
1060  public void update(Graphics g) {
1061  paint(g);
1062  }
1063 
1064  public void paint(Graphics g) {
1065  int x1, y1, x2, y2;
1066  double scale = (double)WIDTH/(defaultMax-defaultMin);
1067  double slope = 0.0;
1068  if (max!=min)
1069  slope = HEIGHT/(max-min);
1070  if (min>=defaultMin) {
1071  x1 = (int)(scale*(min-defaultMin));
1072  y1 = HEIGHT;
1073  } else {
1074  x1 = 0;
1075  if (max>min)
1076  y1 = HEIGHT-(int)((defaultMin-min)*slope);
1077  else
1078  y1 = HEIGHT;
1079  }
1080  if (max<=defaultMax) {
1081  x2 = (int)(scale*(max-defaultMin));
1082  y2 = 0;
1083  } else {
1084  x2 = WIDTH;
1085  if (max>min)
1086  y2 = HEIGHT-(int)((defaultMax-min)*slope);
1087  else
1088  y2 = 0;
1089  }
1090  if (histogram!=null) {
1091  if (os==null && hmax!=0) {
1092  os = createImage(WIDTH,HEIGHT);
1093  osg = os.getGraphics();
1094  osg.setColor(Color.white);
1095  osg.fillRect(0, 0, WIDTH, HEIGHT);
1096  osg.setColor(Color.gray);
1097  for (int i = 0; i < WIDTH; i++)
1098  osg.drawLine(i, HEIGHT, i, HEIGHT - ((int)(HEIGHT * histogram[i])/hmax));
1099  osg.dispose();
1100  }
1101  g.drawImage(os, 0, 0, this);
1102  } else {
1103  g.setColor(Color.white);
1104  g.fillRect(0, 0, WIDTH, HEIGHT);
1105  }
1106  g.setColor(Color.black);
1107  g.drawLine(x1, y1, x2, y2);
1108  g.drawLine(x2, HEIGHT-5, x2, HEIGHT);
1109  g.drawRect(0, 0, WIDTH, HEIGHT);
1110  }
1111 
1112  public void mousePressed(MouseEvent e) {}
1113  public void mouseReleased(MouseEvent e) {}
1114  public void mouseExited(MouseEvent e) {}
1115  public void mouseClicked(MouseEvent e) {}
1116  public void mouseEntered(MouseEvent e) {}
1117 
1118 } // ContrastPlot class
1119 
1120 
1121 class TrimmedLabel extends JLabel {
1122  int trim = IJ.isMacOSX() && IJ.isJava14()?0:6;
1123 
1124  public TrimmedLabel(String title) {
1125  super(title);
1126  }
1127 
1128  public Dimension getMinimumSize() {
1129  return new Dimension(super.getMinimumSize().width, super.getMinimumSize().height-trim);
1130  }
1131 
1132  public Dimension getPreferredSize() {
1133  return getMinimumSize();
1134  }
1135 
1136 } // TrimmedLabel class
1137 
1138 class SliderPane extends JPanel
1139 {
1140  SliderPane(Scrollbar slider, String labelText)
1141  {
1142  setLayout(new GridLayout(1, 2));
1143  add(new JLabel(labelText));
1144  add(slider);
1145  }
1146 }
1147