Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
LUT_Editor.java
1 package ij.plugin;
2 import ij.*;
3 import ij.process.*;
4 import ij.gui.*;
5 //import ij.plugin.*;
6 import java.awt.*;
7 import java.awt.image.*;
8 import ij.util.*;
9 import ij.measure.*;
10 import java.util.Vector;
11 import java.awt.event.*;
12 
13 public class LUT_Editor implements PlugIn, ActionListener{
14  Vector colors;
15  private ImagePlus imp;
16  Button openButton, saveButton, resizeButton, invertButton;
17  ColorPanel colorPanel;
18 
19  public void run(String args) {
20  ImagePlus imp = IJ.getInstance().getImagePlus();
21  if (imp==null) {
22  IJ.showMessage("LUT Editor", "No images are open");
23  return;
24  }
25  if (imp.getBitDepth()==24) {
26  IJ.showMessage("LUT Editor", "RGB images do not use LUTs");
27  return;
28  }
29  colorPanel = new ColorPanel(imp);
30  if (colorPanel.getMapSize()!=256) {
31  IJ.showMessage("LUT Editor", "LUT must have 256 entries");
32  return;
33  }
34  int red=0, green=0, blue=0;
35  GenericDialog gd = new GenericDialog("LUT Editor");
36  Panel buttonPanel = new Panel(new GridLayout(4, 1, 0, 5));
37  openButton = new Button("Open...");
38  openButton.addActionListener(this);
39  buttonPanel.add(openButton);
40  saveButton = new Button("Save...");
41  saveButton.addActionListener(this);
42  buttonPanel.add(saveButton);
43  resizeButton = new Button("Set...");
44  resizeButton.addActionListener(this);
45  buttonPanel.add(resizeButton);
46  invertButton = new Button("Invert...");
47  invertButton.addActionListener(this);
48  buttonPanel.add(invertButton);
49  Panel panel = new Panel();
50  panel.add(colorPanel);
51  panel.add(buttonPanel);
52  gd.addPanel(panel, GridBagConstraints.CENTER, new Insets(10, 0, 0, 0));
53  gd.showDialog();
54  if (gd.wasCanceled()){
55  colorPanel.cancelLUT();
56  return;
57  } else
58  colorPanel.applyLUT();
59  }
60 
61  void save() {
62  IJ.run("LUT...");
63  }
64 
65  public void actionPerformed(ActionEvent e) {
66  Object source = e.getSource();
67  if (source==openButton)
68  colorPanel.open();
69  else if (source==saveButton)
70  save();
71  else if (source==resizeButton)
72  colorPanel.resize();
73  else if (source==invertButton)
74  colorPanel.invert();
75  }
76 }
77 
78 
79 class ColorPanel extends Panel implements MouseListener, MouseMotionListener{
80  static final int entryWidth=12, entryHeight=12;
81  int rows = 16;
82  int columns = 16;
83  Color c[] = new Color[256];
84  Color b;
85  ColorProcessor cp;
86  IndexColorModel origin;
87  private ImagePlus imp;
88  private int[] xSize = new int[256], redY, greenY, blueY;
89  private int mapSize, x, y, initialC = -1, finalC = -1;
90  private byte[] reds, greens, blues;
91  private boolean updateLut;
92  private static String[] choices = {"Replication","Interpolation", "Spline Fitting"};
93  private static String scaleMethod = choices[1];
94 
95  ColorPanel(ImagePlus imp) {
96  setup(imp);
97  }
98 
99  public void setup(ImagePlus imp) {
100  if (imp==null) {
101  IJ.noImage();
102  return;
103  }
104  this.imp = imp;
105  IndexColorModel cm = (IndexColorModel)imp.getProcessor().getColorModel();
106  origin = cm;
107  mapSize = cm.getMapSize();
108  reds = new byte[256];
109  greens = new byte[256];
110  blues = new byte[256];
111  cm.getReds(reds);
112  cm.getGreens(greens);
113  cm.getBlues(blues);
114  addMouseListener(this);
115  addMouseMotionListener(this);
116  for(int index = 0; index < mapSize; index++)
117  c[index] = new Color(reds[index]&255, greens[index]&255, blues[index]&255);
118  }
119  public Dimension getPreferredSize() {
120  return new Dimension(columns*entryWidth, rows*entryHeight);
121  }
122  public Dimension getMinimumSize() {
123  return new Dimension(columns*entryWidth, rows*entryHeight);
124  }
125  int getMouseZone(int x, int y){
126  int horizontal = (int)x/entryWidth;
127  int vertical = (int)y/entryHeight;
128  int index = (columns*vertical + horizontal);
129  return index;
130  }
131  public void colorRamp() {
132  if (initialC>finalC) {
133  int tmp = initialC;
134  initialC = finalC;
135  finalC = tmp;
136  }
137  float difference = finalC - initialC+1;
138  int start = (byte)c[initialC].getRed()&255;
139  int end = (byte)c[finalC].getRed()&255;
140  float rstep = (end-start)/difference;
141  for(int index = initialC; index <= finalC; index++)
142  reds[index] = (byte)(start+ (index-initialC)*rstep);
143 
144  start = (byte)c[initialC].getGreen()&255;
145  end = (byte)c[finalC].getGreen()&255;
146  float gstep = (end-start)/difference;
147  for(int index = initialC; index <= finalC; index++)
148  greens[index] = (byte)(start + (index-initialC)*gstep);
149 
150  start = (byte)c[initialC].getBlue()&255;
151  end = (byte)c[finalC].getBlue()&255;
152  float bstep = (end-start)/difference;
153  for(int index = initialC; index <= finalC; index++)
154  blues[index] = (byte)(start + (index-initialC)*bstep);
155  for (int index = initialC; index <= finalC; index++)
156  c[index] = new Color(reds[index]&255, greens[index]&255, blues[index]&255);
157  repaint();
158  }
159 
160  public void mousePressed(MouseEvent e){
161  x = (e.getX());
162  y = (e.getY());
163  initialC = getMouseZone(x,y);
164  }
165 
166  public void mouseReleased(MouseEvent e){
167  x = (e.getX());
168  y = (e.getY());
169  finalC = getMouseZone(x,y);
170  if(initialC>=mapSize&&finalC>=mapSize) {
171  initialC = finalC = -1;
172  return;
173  }
174  if(initialC>=mapSize)
175  initialC = mapSize-1;
176  if(finalC>=mapSize)
177  finalC = mapSize-1;
178  if(finalC<0)
179  finalC = 0;
180  if (initialC == finalC) {
181  b = c[finalC];
182  ColorChooser cc = new ColorChooser("Color at Entry " + (finalC) , c[finalC] , false);
183  c[finalC] = cc.getColor();
184  if (c[finalC]==null){
185  c[finalC] = b;
186  }
187  colorRamp();
188  } else {
189  b = c[initialC];
190  ColorChooser icc = new ColorChooser("Initial Entry (" + (initialC)+")" , c[initialC] , false);
191  c[initialC] = icc.getColor();
192  if (c[initialC]==null){
193  c[initialC] = b;
194  initialC = finalC = -1;
195  return;
196  }
197  b = c[finalC];
198  ColorChooser fcc = new ColorChooser("Final Entry (" + (finalC)+")" , c[finalC] , false);
199  c[finalC] = fcc.getColor();
200  if (c[finalC]==null){
201  c[finalC] = b;
202  initialC = finalC = -1;
203  return;
204  }
205  colorRamp();
206  }
207  initialC = finalC = -1;
208  applyLUT();
209  }
210 
211  public void mouseClicked(MouseEvent e){}
212  public void mouseEntered(MouseEvent e){}
213  public void mouseExited(MouseEvent e){}
214 
215  public void mouseDragged(MouseEvent e){
216  x = (e.getX());
217  y = (e.getY());
218  finalC = getMouseZone(x,y);
219  repaint();
220  }
221 
222  public void mouseMoved(MouseEvent e) {
223  x = (e.getX());
224  y = (e.getY());
225  int entry = getMouseZone(x,y);
226  if (entry<mapSize) {
227  int red = reds[entry]&255;
228  int green = greens[entry]&255;
229  int blue = blues[entry]&255;
230  IJ.showStatus("index=" + entry + ", color=" + red + "," + green + "," + blue);
231  } else
232  IJ.showStatus("");
233  }
234 
235  void open() {
236  IJ.run("LUT... ");
237  updateLut = true;
238  repaint();
239  }
240 
241  void updateLut() {
242  IndexColorModel cm = (IndexColorModel)imp.getProcessor().getColorModel();
243  if (mapSize == 0)
244  return;
245  cm.getReds(reds);
246  cm.getGreens(greens);
247  cm.getBlues(blues);
248  for(int i=0; i<mapSize; i++)
249  c[i] = new Color(reds[i]&255, greens[i]&255, blues[i]&255);
250  }
251 
252  void invert() {
253  byte[] reds2 = new byte[mapSize];
254  byte[] greens2 = new byte[mapSize];
255  byte[] blues2 = new byte[mapSize];
256  for (int i=0; i<mapSize; i++) {
257  reds2[i] = (byte)(reds[mapSize-i-1]&255);
258  greens2[i] = (byte)(greens[mapSize-i-1]&255);
259  blues2[i] = (byte)(blues[mapSize-i-1]&255);
260  }
261  reds=reds2; greens=greens2; blues=blues2;
262  for(int i=0; i<mapSize; i++)
263  c[i] = new Color(reds[i]&255, greens[i]&255, blues[i]&255);
264  applyLUT();
265  repaint();
266  }
267 
268  void resize() {
269  GenericDialog sgd = new GenericDialog("LUT Editor");
270  sgd.addNumericField("Number of Colors:", mapSize, 0);
271  sgd.addChoice("Scale Using:", choices, scaleMethod);
272  sgd.showDialog();
273  if (sgd.wasCanceled()){
274  cancelLUT();
275  return;
276  }
277  int newSize = (int)sgd.getNextNumber();
278  if (newSize<2) newSize = 2;
279  if (newSize>256) newSize =256;
280  scaleMethod = sgd.getNextChoice();
281  scale(reds, greens, blues, newSize);
282  mapSize = newSize;
283  for(int i=0; i<mapSize; i++)
284  c[i] = new Color(reds[i]&255, greens[i]&255, blues[i]&255);
285  applyLUT();
286  repaint();
287  }
288 
289  void scale(byte[] reds, byte[] greens, byte[] blues, int newSize) {
290  if (newSize==mapSize)
291  return;
292  else if (newSize<mapSize || scaleMethod.equals(choices[0]))
293  scaleUsingReplication(reds, greens, blues, newSize);
294  else if (scaleMethod.equals(choices[1]))
295  scaleUsingInterpolation(reds, greens, blues, newSize);
296  else
297  scaleUsingSplineFitting(reds, greens, blues, newSize);
298  }
299 
300  void scaleUsingReplication(byte[] reds, byte[] greens, byte[] blues, int newSize) {
301  byte[] reds2 = new byte[256];
302  byte[] greens2 = new byte[256];
303  byte[] blues2 = new byte[256];
304  for(int i = 0; i < mapSize; i++) {
305  reds2[i] = reds[i];
306  greens2[i] = greens[i];
307  blues2[i] = blues[i];
308  }
309  for(int i = 0; i < newSize; i++) {
310  int index =(int)( i*((double)mapSize/newSize));
311  reds[i] = reds2[index];
312  greens[i] = greens2[index];
313  blues[i] = blues2[index];
314  }
315  }
316 
317  void scaleUsingInterpolation(byte[] reds, byte[] greens, byte[] blues, int newSize) {
318  int[] r = new int[mapSize];
319  int[] g = new int[mapSize];
320  int[] b = new int[mapSize];
321  for(int i = 0; i<mapSize; i++) {
322  r[i] = reds[i]&255;
323  g[i] = greens[i]&255;
324  b[i] = blues[i]&255;
325  }
326  double scale = (double)(mapSize-1)/(newSize-1);
327  int i1, i2;
328  double fraction;
329  for (int i=0; i<newSize; i++) {
330  i1 = (int)(i*scale);
331  i2 = i1+1;
332  if (i2==mapSize) i2 = mapSize-1;
333  fraction = i*scale - i1;
334  //IJ.log(i+" "+i1+" "+i2+" "+fraction+" "+mapSize+" "+newSize);
335  reds[i] = (byte)((1.0-fraction)*r[i1] + fraction*r[i2]);
336  greens[i] = (byte)((1.0-fraction)*g[i1] + fraction*g[i2]);
337  blues[i] = (byte)((1.0-fraction)*b[i1] + fraction*b[i2]);
338  }
339  }
340 
341  void scaleUsingSplineFitting(byte[] reds, byte[] greens, byte[] blues, int newSize) {
342  //IJ.log("scaleUsingSplineFitting: "+mapSize+" "+newSize);
343  int[] reds2 = new int[mapSize];
344  int[] greens2 = new int[mapSize];
345  int[] blues2 = new int[mapSize];
346  for(int i=0; i<mapSize; i++) {
347  reds2[i] = reds[i]&255;
348  greens2[i] = greens[i]&255;
349  blues2[i] = blues[i]&255;
350  }
351  int[] xValues = new int[mapSize];
352  for(int i = 0; i < mapSize; i++) {
353  xValues[i] = (int)(i*(double)newSize/(mapSize-1));
354  //IJ.log(i+" "+xValues[i]+" "+reds2[i]);
355  }
356  SplineFitter sfReds = new SplineFitter(xValues, reds2, mapSize);
357  SplineFitter sfGreens = new SplineFitter(xValues, greens2, mapSize);
358  SplineFitter sfBlues = new SplineFitter(xValues, blues2, mapSize);
359  for(int i = 0; i < newSize; i++) {
360  double v = Math.round(sfReds.evalSpline(xValues, reds2, mapSize, i));
361  if (v<0.0) v=0.0; if (v>255.0) v=255.0; reds[i] = (byte)v;
362  v = Math.round(sfGreens.evalSpline(xValues, greens2, mapSize, i));
363  if (v<0.0) v=0.0; if (v>255.0) v=255.0; greens[i] = (byte)v;
364  v = Math.round(sfBlues.evalSpline(xValues, blues2, mapSize, i));
365  if (v<0.0) v=0.0; if (v>255.0) v=255.0; blues[i] = (byte)v;
366  }
367  }
368 
369  public void cancelLUT() {
370  if (mapSize == 0)
371  return;
372  origin.getReds(reds);
373  origin.getGreens(greens);
374  origin.getBlues(blues);
375  mapSize = 256;
376  applyLUT();
377  }
378 
379  public void applyLUT() {
380  byte[] reds2=reds, greens2=greens, blues2=blues;
381  if (mapSize<256) {
382  reds2 = new byte[256];
383  greens2 = new byte[256];
384  blues2 = new byte[256];
385  for(int i = 0; i < mapSize; i++) {
386  reds2[i] = reds[i];
387  greens2[i] = greens[i];
388  blues2[i] = blues[i];
389  }
390  scale(reds2, greens2, blues2, 256);
391  }
392  ColorModel cm = new IndexColorModel(8, 256, reds2, greens2, blues2);
393  ImageProcessor ip = imp.getProcessor();
394  ip.setColorModel(cm);
395  imp.updateAndDraw();
396  }
397 
398  public void update(Graphics g) {
399  paint(g);
400  }
401 
402  public void paint(Graphics g) {
403  if (updateLut) {
404  updateLut();
405  updateLut = false;
406  }
407  int index = 0;
408  for (int y=0; y<rows; y++) {
409  for (int x=0; x<columns; x++) {
410  if(index>=mapSize) {
411  g.setColor(Color.lightGray);
412  g.fillRect(x*entryWidth, y*entryHeight, entryWidth, entryHeight);
413  } else if (((index <= finalC) && (index >= initialC)) || ((index >= finalC) && (index <= initialC))){
414  g.setColor(c[index].brighter());
415  g.fillRect(x*entryWidth, y*entryHeight, entryWidth, entryHeight);
416  g.setColor(Color.white);
417  g.drawRect((x*entryWidth), (y*entryHeight), entryWidth, entryHeight);
418  g.setColor(Color.black);
419  g.drawLine((x*entryWidth)+entryWidth-1, (y*entryHeight), (x*entryWidth)+entryWidth-1, (y*entryWidth)+entryHeight);
420  g.drawLine((x*entryWidth), (y*entryHeight)+entryHeight-1, (x*entryWidth)+entryWidth-1, (y*entryHeight)+entryHeight-1);
421  g.setColor(Color.white);
422  } else {
423  g.setColor(c[index]);
424  g.fillRect(x*entryWidth, y*entryHeight, entryWidth, entryHeight);
425  g.setColor(Color.white);
426  g.drawRect((x*entryWidth), (y*entryHeight), entryWidth-1, entryHeight-1);
427  g.setColor(Color.black);
428  g.drawLine((x*entryWidth), (y*entryHeight), (x*entryWidth)+entryWidth-1, (y*entryWidth));
429  g.drawLine((x*entryWidth), (y*entryHeight), (x*entryWidth), (y*entryHeight)+entryHeight-1);
430  }
431  index++;
432  }
433  }
434  }
435 
436  int getMapSize() {
437  return mapSize;
438  }
439 
440 }