/* * Lut_Panel.java * * Created on 3 mars 2003, 16:04 Copyright (C) 2003 Patrick Pirrotte * ImageJ plugin * Version : 2.1 * Author : Patrick Pirrotte * written for the IBMP-CNRS Strasbourg(France) * Email : patrick.pirrotte@gmx.net, jerome.mutterer@ibmp-ulp.u-strasbg.fr * Description : Lut_Panel extends the LUT capabilities of ImageJ. It is especially useful * for microscopists: a red, green or blue gradient palette can be applied * with a click. Gradient LUTs can be defined with a starting and an ending color * and very easily pasted into the actual picture. Lut_Panel also implements a simple * Palette editor. * Please send bug reports and wishes to the above email address. * * Release History : * * * 19.3.2003 : v2.1 Preview functions for gradients (on request) and Lut Editor * 15.3.2003 : v2.0 gradient indexes can now be defined * 9.3.2003 : v1.0 import, export, fast R/G/B-gradient , user defined gradient (evened) * * * 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 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ // specific of WholeNumberField import java.awt.Toolkit; import java.text.NumberFormat; import java.text.ParseException; import java.util.Locale; import javax.swing.text.*; // specific end import java.io.*; import java.awt.*; import java.awt.image.*; import java.awt.event.*; import java.awt.Color.*; import java.awt.geom.Rectangle2D; import java.util.Arrays; //for sorting import java.lang.Object; import ij.text.TextWindow; import javax.swing.*; import javax.swing.event.*; import javax.swing.colorchooser.*; import ij.*; import ij.io.*; import ij.process.*; import ij.plugin.*; import ij.plugin.frame.*; import ij.plugin.PlugIn; import ij.text.TextWindow; /* Lut_Panel class start * Main plugin class, (entry point) */ public class Lut_Panel implements PlugIn { public GUI theGUI; public Color_Chooser color_picker; public Lut_Edit lut_edit; public int initial_tooltip_delay; /* ImageJ plugin entry point of Lut_Panel */ public void run(String args) { if (args.equals("about")){ showAbout(); } else { IJ.showStatus("Plugin Lut_Panel started."); theGUI = new GUI(); theGUI.init(); color_picker = new Color_Chooser(); color_picker.colorFrame.setModal(true); lut_edit = new Lut_Edit(); lut_edit.editFrame.setModal(true); initial_tooltip_delay = ToolTipManager.sharedInstance().getInitialDelay(); } } void showAbout() { IJ.showMessage("About Lut_Panel", "Lut_Panel v.2.1, Copyright (C) 2003 Patrick PIRROTTE\n"+ "written for the IBMP-CNRS Strasbourg(France)\n"+ "Email : patrick.pirrotte@gmx.net, jerome.mutterer@ibmp-ulp.u-strasbg.fr\n"+ "Lut_Panel comes with ABSOLUTELY NO WARRANTY; It is licensed\n"+ "under the GNU General Public License. You should have received\n"+ "a copy of the GNU General Public License along with this program.\n"+ "If not, write to the Free Software Foundation, Inc., 59 Temple Place\n"+ "- Suite 330, Boston, MA 02111-1307, USA.\n"+ "\n"+ "Lut_Panel extends the LUT capabilities of ImageJ. It is especially useful\n"+ "for microscopists: a red, green or blue gradient palette can be applied \n"+ "with a click. Gradient LUTs can be defined with a starting and an ending color\n"+ "and very easily pasted into the actual picture. Lut_Panel also implements a simple\n"+ "Palette editor.\n\n"+ "Please send bug reports and wishes to the above email address.\n" ); } /* class GUI start * GUI creates a panel with buttons */ public class GUI { public JFrame baseFrame; private JPanel pan; private GridLayout baseGridLayout = new GridLayout(8,1,10,2); private Dimension ScreenDimension = Toolkit.getDefaultToolkit().getScreenSize(); private int ScreenX = (int) ScreenDimension.getWidth(); private int ScreenY = (int) ScreenDimension.getHeight(); private int baseFrameXsize = (int)(ScreenX/8); private int baseFrameYsize = (int)(ScreenY/4); private int baseFrameXlocation = (int)((11*ScreenX/12) - baseFrameXsize); private int baseFrameYlocation = (int)((8.5*ScreenY/12) - baseFrameYsize); private smallButton red_button = new smallButton("R","replace LUT with a generic red LUT","red"); private smallButton green_button = new smallButton("G","replace LUT with a generic green LUT","green"); private smallButton blue_button = new smallButton("B","replace LUT with a generic blue LUT","blue"); private smallButton import_lut_button = new smallButton("Import LUT","Apply LUT from imported .txt",""); private smallButton export_lut_button = new smallButton("Export LUT","Export LUT to .txt",""); private smallButton gradient_lut_button = new smallButton("Gradient LUT","Apply gradient LUT",""); private smallButton edit_lut_button = new smallButton("Edit LUT","Check/Edit LUT",""); private smallButton exit_button = new smallButton("Exit","Exit LUT Panel",""); /** Initiates the GUI, setting its layout*/ public void init() { baseFrame = new JFrame(); baseFrame.setTitle("LUT Panel 2.1"); baseFrame.setSize(baseFrameXsize,baseFrameYsize); baseFrame.setResizable(false); baseFrame.setLocation(baseFrameXlocation,baseFrameYlocation); baseFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); pan = new JPanel(); pan.setSize(baseFrameXsize, baseFrameYsize); pan.setForeground(SystemColor.window); pan.setLayout(baseGridLayout); pan.add(red_button); pan.add(green_button); pan.add(blue_button); pan.add(import_lut_button); pan.add(export_lut_button); pan.add(gradient_lut_button); pan.add(edit_lut_button); pan.add(exit_button); baseFrame.getContentPane().add(pan); addRedListener(red_button, baseFrame); addGreenListener(green_button, baseFrame); addBlueListener(blue_button, baseFrame); addImportListener(import_lut_button, baseFrame); addExportListener(export_lut_button, baseFrame); addGradientListener(gradient_lut_button, baseFrame); addEditListener(edit_lut_button, baseFrame); addExitListener(exit_button, baseFrame); baseFrame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { lut_edit.editFrame.dispose(); color_picker.colorFrame.dispose(); baseFrame.dispose(); } } ); baseFrame.show(); } /* Action listeners for the buttons*/ private void addRedListener(final JButton button, final JFrame parent) { button.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { IJ.runPlugIn("ij.plugin.LutLoader", "red"); } }); } private void addGreenListener(final JButton button, final JFrame parent) { button.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { IJ.runPlugIn("ij.plugin.LutLoader", "green"); } }); } private void addBlueListener(final JButton button, final JFrame parent) { button.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { IJ.runPlugIn("ij.plugin.LutLoader", "blue"); } }); } private void addImportListener(final JButton button, final JFrame parent) { button.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { read_lut(); } }); } private void addExportListener(final JButton button, final JFrame parent) { button.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { String fname = WindowManager.getCurrentImage().getTitle(); SaveDialog sd = new SaveDialog("Save LUT to...",fname,".lut"); if (fname!=null) { String directory = sd.getDirectory(); String path = directory+fname+".txt"; File file = new File(path); ImagePlus imp = WindowManager.getCurrentImage(); imp.startTiming(); export_lut(file); } } }); } private void addGradientListener(final JButton button, final JFrame parent) { button.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { if (WindowManager.getCurrentImage().getFileInfo().getBytesPerPixel()!=1){ IJ.error("Only works on 8 bit images"); return; } if (color_picker.colorFrame.isShowing()== false) color_picker.colorFrame.show(); else color_picker.colorFrame.setVisible(false); } }); } private void addEditListener(final JButton button, final JFrame parent) { button.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { if (WindowManager.getCurrentImage().getFileInfo().getBytesPerPixel()!=1){ IJ.error("Only works on 8 bit images"); return; } if (lut_edit.editFrame.isShowing()== false){ lut_edit.update_lut_edit(); lut_edit.editFrame.show(); } else lut_edit.editFrame.setVisible(false); } }); } private void addExitListener(final JButton button, final JFrame parent) { button.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { color_picker.colorFrame.dispose(); baseFrame.dispose(); } }); } /* code written by Wayne Rasband, reads a lut with 3 or 4 columns, first line is * a header. */ public void read_lut() { if (WindowManager.getCurrentImage().getFileInfo().getBytesPerPixel()!=1){ IJ.error("Only works on 8 bit images"); return; } if (IJ.versionLessThan("1.26f")) return; TextReader tr = new TextReader(); ImageProcessor ip = tr.open(); if (ip==null) return; int width = ip.getWidth(); int height = ip.getHeight(); if (!((width==3||width==4)&&(height==256||height==257))) { IJ.showMessage("LUT Importer", "3 or 4 column text file required.\n" +"This file has "+width+" columns."); return; } int x = width==4?1:0; int y = 0; ip.setRoi(x, y, 3, 256); ip = ip.crop(); //new ImagePlus("ip", ip).show(); byte[] reds = new byte[256]; byte[] greens = new byte[256]; byte[] blues = new byte[256]; for (int i=0; i<256; i++) { reds[i] = (byte)ip.getPixelValue(0,i); greens[i] = (byte)ip.getPixelValue(1,i); blues[i] = (byte)ip.getPixelValue(2,i); } IndexColorModel cm = new IndexColorModel(8, 256, reds, greens, blues); ImagePlus imp = WindowManager.getCurrentImage(); if (imp==null) makeImage(tr.getName(), cm); else { imp.getProcessor().setColorModel(cm); imp.updateAndDraw(); } } public void makeImage(String title, IndexColorModel cm) { int width = 256; int height = 32; byte[] pixels = new byte[width*height]; ByteProcessor bp = new ByteProcessor(width, height, pixels, cm); int[] ramp = new int[width]; for (int i=0; i255) under256 = false; if (!under256) str= "Index values must be under 256 (8 bit LUT)!\n"; else str=""; if (!Arrays.equals(interval,temp)) { ascendent = false; str += "No increasing index values!"; } else{ str +=""; } if (!under256 || !ascendent) { IJ.error(str); return; } } Lut_Apply LA = new Lut_Apply(); if(check.isSelected()) LA.apply(color,selCol); else LA.apply(color,selCol,interval); } /* kind of a push and pop LUT functions, used for the Preview of the gradient */ public FileInfo getLUT(){ FileInfo f = WindowManager.getCurrentImage().getFileInfo(); return f; } public void setLUT(FileInfo fi){ Lut_Apply LAP = new Lut_Apply(); LAP.showLut(fi,true); } } /* Lut_Apply class start * applies gradients to Lut */ public class Lut_Apply extends ImagePlus{ /* function apply calculates gradients and applies it by calling showlut * gc[] is the color array, i the number of colours * Gradient is done evenly */ public void apply(Color[] gc, int i){ FileInfo fi = new FileInfo(); fi.reds = new byte[256]; fi.greens = new byte[256]; fi.blues = new byte[256]; fi.lutSize = 256; float nColorsfl = 256; float interval = nColorsfl / (float)i; for (int j=0;j0) { if (nColorsfl<256) interpolate(fi.reds, fi.greens, fi.blues, (int)nColorsfl); //fi.fileName = arg; showLut(fi,true); return; } } /* overload of the previous gradient apply function * gc[] is the color array, i the number of colours, idx the index values * gradient milestones are set at idx values */ public void apply(Color[] gc, int i,float[] idx){ FileInfo fi = new FileInfo(); fi.reds = new byte[256]; fi.greens = new byte[256]; fi.blues = new byte[256]; fi.lutSize = 256; float nColorsfl = 256; float iv; String str = new String(); for (int j=0;j0) { if (nColorsfl<256) interpolate(fi.reds, fi.greens, fi.blues, (int)nColorsfl); //fi.fileName = arg; showLut(fi,true); return; } } /* applies the new Lut on the actual image*/ void showLut(FileInfo fi, boolean showImage) { ImagePlus imp = WindowManager.getCurrentImage(); if (imp!=null) { if (imp.getType()==ImagePlus.COLOR_RGB) IJ.error("Color tables cannot be assiged to RGB Images."); else { ImageProcessor ip = imp.getProcessor(); ColorModel cm = new IndexColorModel(8, 256, fi.reds, fi.greens, fi.blues); ip.setColorModel(cm); if (imp.getStackSize()>1) imp.getStack().setColorModel(cm); imp.updateAndDraw(); } } /*else imp.createImage(fi, showImage);*/ } /* function interpolate taken out from the orignal ImageJ project from Wayne Rasband * Couldn't find a way to have straight access to this method so I had * to add it manually. */ void interpolate(byte[] reds, byte[] greens, byte[] blues, int nColors) { byte[] r = new byte[nColors]; byte[] g = new byte[nColors]; byte[] b = new byte[nColors]; System.arraycopy(reds, 0, r, 0, nColors); System.arraycopy(greens, 0, g, 0, nColors); System.arraycopy(blues, 0, b, 0, nColors); double scale = nColors/256.0; int i1, i2; double fraction; for (int i=0; i<256; i++) { i1 = (int)(i*scale); i2 = i1+1; if (i2==nColors) i2 = nColors-1; fraction = i*scale - i1; reds[i] = (byte)((1.0-fraction)*(r[i1]&255) + fraction*(r[i2]&255)); greens[i] = (byte)((1.0-fraction)*(g[i1]&255) + fraction*(g[i2]&255)); blues[i] = (byte)((1.0-fraction)*(b[i1]&255) + fraction*(b[i2]&255)); } } /* Lut_Apply class end*/ } /* Color_Chooser end*/ /* Button class start * new Button class, sets color and tooltip text */ class smallButton extends JButton { public smallButton(String buttonText, String tooltipText, String button_color) { Font dafont = new Font(null); float fontsize = 11; dafont = dafont.deriveFont(fontsize); dafont = dafont.deriveFont(Font.BOLD); this.setFont(dafont); this.setText(buttonText); if (button_color=="red") this.setForeground(java.awt.Color.red); if (button_color=="green") this.setForeground(java.awt.Color.green); if (button_color=="blue") this.setForeground(java.awt.Color.blue); this.setToolTipText(tooltipText); } } /* Button class end*/ /* DrawRect class start * draws a rectangle at a set position with a preset color, color can be modified * later on */ class DrawRect extends JPanel{ private Rectangle2D rect; private Graphics2D g2; private int x,y; private Color fromc; //constructor public DrawRect(int posx,int posy){ x = posx; y = posy; this.setSize(20,20); fromc = Color.white; } //draws a rectangle public void paintComponent(Graphics g){ super.paintComponent(g); g2 = (Graphics2D)g; double width = 20; double height = 20; rect = new Rectangle2D.Double(x,y,width,height); g2.setPaint(fromc); g2.fill(rect); g2.setPaint(Color.black); g2.draw(rect); } //changes rectangle color*/ public void fillFace(Color fromgrad){ fromc = fromgrad; repaint(); } //returns color public Color getColor(){ return fromc; } } /* DrawRect class end*/ /* ActiveDrawRect class start extends DrawRect * implements mouse event listening for color swatches */ public class ActiveDrawRect extends DrawRect implements MouseListener{ public ActiveDrawRect(int posx, int posy, Color c){ super(posx,posy); addMouseListener(this); } public void mouseClicked(MouseEvent e) { JColorChooser cc = new JColorChooser(); Color c = cc.showDialog(lut_edit.editFrame,"Color Chooser", Color.red); for (int i=0;i<256;i++){ if (e.getSource()==lut_edit.rect[i]){ if (c!=null ) { lut_edit.rect[i].fillFace(c); } break; } } } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { for (int i=0;i<256;i++){ if (e.getSource()==lut_edit.rect[i]){ String str = "LUT position:"+i+" ,R:"+lut_edit.rect[i].getColor().getRed()+ " ,G:"+lut_edit.rect[i].getColor().getGreen()+" ,B:"+lut_edit.rect[i].getColor().getBlue(); lut_edit.rect[i].setToolTipText(str); ToolTipManager.sharedInstance().setInitialDelay(0); return; } } } public void mouseExited(MouseEvent e) { for (int i=0;i<256;i++){ if (e.getSource()==lut_edit.rect[i]){ lut_edit.rect[i].setToolTipText(""); ToolTipManager.sharedInstance().setInitialDelay(initial_tooltip_delay); } } } } /* ActiveDrawRect class end*/ /* WholeNumberField class start * from Sun; creates a Textfield that does only allow integers */ public class WholeNumberField extends JTextField { private Toolkit toolkit; private NumberFormat integerFormatter; public WholeNumberField(int value, int columns) { super(columns); toolkit = Toolkit.getDefaultToolkit(); integerFormatter = NumberFormat.getNumberInstance(Locale.US); integerFormatter.setParseIntegerOnly(true); setValue(value); } public int getValue() { int retVal = 0; try { retVal = integerFormatter.parse(getText()).intValue(); } catch (ParseException e) { // This should never happen because insertString allows // only properly formatted data to get in the field. toolkit.beep(); } return retVal; } public void setValue(int value) { setText(integerFormatter.format(value)); } protected Document createDefaultModel() { return new WholeNumberDocument(); } protected class WholeNumberDocument extends PlainDocument { public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { char[] source = str.toCharArray(); char[] result = new char[source.length]; int j = 0; for (int i = 0; i < result.length; i++) { if (Character.isDigit(source[i])) result[j++] = source[i]; else { toolkit.beep(); System.err.println("insertString: " + source[i]); } } super.insertString(offs, new String(result, 0, j), a); } } } /* Wholenumberfield class end*/ /* Lut_Edit class start*/ class Lut_Edit extends JDialog implements WindowListener{ public JDialog editFrame; private smallButton Apply_button = new smallButton("Apply","Apply LUT",""); private smallButton Preview_button = new smallButton("Preview","Preview LUT",""); private smallButton Cancel_button = new smallButton("Cancel","Cancel LUT edition",""); private Dimension ScreenDimension = Toolkit.getDefaultToolkit().getScreenSize(); private int ScreenX = (int) ScreenDimension.getWidth(); private int ScreenY = (int) ScreenDimension.getHeight(); private int colorFrameXlocation = (int)((ScreenX/12)); private int colorFrameYlocation = (int)(8*(ScreenY/12)); private ActiveDrawRect[] rect = new ActiveDrawRect[256]; private int selCol; private JCheckBox check = new JCheckBox(); private float[] interval; private ButtonGroup cbg = new ButtonGroup(); private JButton[] Define_button = new JButton[5]; private JRadioButton[] cb = new JRadioButton[5]; private WholeNumberField[] tf = new WholeNumberField[5]; private FileInfo fi; public Lut_Edit() { editFrame = new JDialog(); editFrame.setTitle("Edit LUT"); editFrame.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); editFrame.setResizable(false); editFrame.setLocation(colorFrameXlocation,colorFrameYlocation); editFrame.getContentPane().setLayout(new BorderLayout()); JPanel panelEdit = new JPanel(); panelEdit.setLayout(new BorderLayout()); JPanel panelButton = new JPanel(); JPanel panel = new JPanel(new GridLayout(8,32)); panelButton.add(Apply_button); panelButton.add(Preview_button); panelButton.add(Cancel_button); panelEdit.add(panel,BorderLayout.NORTH); panelEdit.add(panelButton,BorderLayout.SOUTH); for (int i = 0;i < 256; i++) { rect[i] = new ActiveDrawRect(0,0,Color.white); panel.add(rect[i]); } addApplyListener(Apply_button,editFrame); addPreviewListener(Preview_button,editFrame); addCancelListener(Cancel_button,editFrame); editFrame.getContentPane().add(panelEdit); editFrame.addWindowListener(this); editFrame.pack(); } public void update_lut_edit(){ ImagePlus imp = WindowManager.getCurrentImage(); if (imp!=null) { IndexColorModel icm = (IndexColorModel)imp.getProcessor().getColorModel(); byte[] r = new byte[256]; byte[] g = new byte[256]; byte[] b = new byte[256]; icm.getReds(r); icm.getGreens(g); icm.getBlues(b); Color c; for (int i = 0;i < 256; i++) { c = new Color(r[i]&255,g[i]&255,b[i]&255); rect[i].fillFace(c); } } else IJ.error("8 bit image must be opened!"); } // Window listeneres; essentially used for the Preview function, LUT is "saved" public void windowActivated(WindowEvent e) { fi = getLUT(); } public void windowClosing(WindowEvent e) { } public void windowClosed(WindowEvent e) { } public void windowOpened(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowDeactivated(WindowEvent e) { } private void addApplyListener(final JButton button, final JDialog parent) { button.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { apply_swatch_lut(); editFrame.setVisible(false); } }); } private void addPreviewListener(final JButton button, final JDialog parent) { button.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { apply_swatch_lut(); } }); } private void addCancelListener(final JButton button, final JDialog parent) { button.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { if (fi!=null) setLUT(fi); editFrame.setVisible(false); } }); } private void apply_swatch_lut(){ if (IJ.versionLessThan("1.26f")) return; byte[] reds = new byte[256]; byte[] greens = new byte[256]; byte[] blues = new byte[256]; for (int i=0; i<256; i++) { reds[i] = (byte)rect[i].getColor().getRed(); greens[i] = (byte)rect[i].getColor().getGreen(); blues[i] = (byte)rect[i].getColor().getBlue(); } IndexColorModel cm = new IndexColorModel(8, 256, reds, greens, blues); ImagePlus imp = WindowManager.getCurrentImage(); if (imp==null) makeImage("Untitled LUT", cm); else { imp.getProcessor().setColorModel(cm); imp.updateAndDraw(); } } public void makeImage(String title, IndexColorModel cm) { int width = 256; int height = 32; byte[] pixels = new byte[width*height]; ByteProcessor bp = new ByteProcessor(width, height, pixels, cm); int[] ramp = new int[width]; for (int i=0; i