package ij.plugin.frame; import ij.*; import ij.process.*; import ij.gui.*; import java.awt.*; import java.awt.image.*; import java.util.*; import java.awt.event.*; import ij.measure.*; import ij.plugin.*; /* This plugin isolates pixels in an RGB image or stack according to a range of Hue. Original PassBand2 by Bob Dougherty. Some code borrowed from ThresholdAdjuster by Wayne Rasband. Version 0 5/12/2002. Version 1 5/13 Filtered pixels set to foreground color. Speed improved. Version 2 5/13. Fixed a bug in setting the restore pixels that was causing problems with stacks. Explicitly get the foreground color from the toolbar in apply. Modifications by G. Landini. 17/Feb/2004. The changes are seen as the sliders/checkboxes are adjusted. Added hue strip to histogram window, changed histogram scale factor 19/Feb/2004. Added Saturation and Brightness histograms, Added Pass/Stop checkboxes for each HSB channel. Added threshold, added inversion of threshold Cleaned some variables. Changed name to Threshold_HSB 22/Feb/2004 Threshold in RGB or HSB space Changed name to Threshold_Colour, changed button names. Added thresholding by "sampling". Hue band sampled selection may not' always work if there are 0 valued histograms. Thread now finishes properly 23/Feb/2004 Java 1.4 on Mac OS X bug (thanks Wayne) 25/Feb/2004 Any type of ROI supported for [Sample] 26/Feb/2004 Modified ROI handling (thanks Wayne) 28/Feb/2004 Improved Hue sampling, changed histogram background colour 29/Feb/2004 Added CIE Lab colour space 6/Mar/2004 v1.4 Requires ImageJ 1.32c (thanks Wayne) 8/Mar/2004 v1.5 ColourProcessor bug (thanks Wayne), filter checkboxes detect a new image 23/Jun/2004 v1.6 Added YUV colour space 1/May/2006 v1.7 Minor changes to Lab coding, added macro recorder button 5/Jan/2007 v1.8 added warning and commented lines for back/foreground colours 2/Feb/2008 v1.9 closing does not apply the filter if Original was being displayed. Thanks for the hint Bob! */ /** Selects pixels according to hsb or rgb components. */ public class ColorThresholder extends PlugInFrame implements PlugIn, Measurements, ActionListener, AdjustmentListener, FocusListener, ItemListener, Runnable{ private static final int HSB=0, RGB=1, LAB=2, YUV=3; private static final String[] colorSpaces = {"HSB", "RGB", "Lab", "YUV"}; private boolean flag = false; private int colorSpace = HSB; private Thread thread; private static Frame instance; private BandPlot plot = new BandPlot(); private BandPlot splot = new BandPlot(); private BandPlot bplot = new BandPlot(); private int sliderRange = 256; private Panel panel, panelt; private Button originalB, filteredB, stackB, helpB, sampleB, resetallB, newB, macroB, selectB; private Checkbox bandPassH, bandPassS, bandPassB, darkBackground; private CheckboxGroup colourMode; private Choice colorSpaceChoice, methodChoice, modeChoice; private int previousImageID = -1; private int previousSlice = -1; private ImageJ ij; private int minHue = 0, minSat = 0, minBri = 0; private int maxHue = 255, maxSat = 255, maxBri = 255; private Scrollbar minSlider, maxSlider, minSlider2, maxSlider2, minSlider3, maxSlider3; private Label label1, label2, label3, label4, label5, label6, labelh, labels, labelb, labelf; private boolean done; private byte[] hSource, sSource, bSource; private boolean applyingStack; private static final int DEFAULT = 0; private static String[] methodNames = AutoThresholder.getMethods(); private static String method = methodNames[DEFAULT]; private static AutoThresholder thresholder = new AutoThresholder(); private static final int RED=0, WHITE=1, BLACK=2, BLACK_AND_WHITE=3; private static final String[] modes = {"Red", "White", "Black", "B&W"}; private static int mode = RED; private int numSlices; private ImageStack stack; private int width, height, numPixels; public ColorThresholder() { super("Threshold Color (experimental)"); if (instance!=null) { WindowManager.toFront(instance); return; } thread = new Thread(this, "BandAdjuster"); WindowManager.addWindow(this); instance = this; ij = IJ.getInstance(); Font font = new Font("SansSerif", Font.PLAIN, 10); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); setLayout(gridbag); int y = 0; c.gridx = 0; c.gridy = y; c.gridwidth = 1; c.weightx = 0; c.insets = new Insets(5, 0, 0, 0); labelh = new Label("Hue", Label.CENTER); add(labelh, c); c.gridx = 1; c.gridy = y++; c.gridwidth = 1; c.weightx = 0; c.insets = new Insets(7, 0, 0, 0); labelf = new Label("", Label.RIGHT); add(labelf, c); // plot c.gridx = 0; c.gridy = y; c.gridwidth = 1; c.fill = c.BOTH; c.anchor = c.CENTER; c.insets = new Insets(0, 5, 0, 0); add(plot, c); // checkboxes bandPassH = new Checkbox("Pass"); bandPassH.addItemListener(this); bandPassH.setState(true); c.gridx = 1; c.gridy = y++; c.gridwidth = 2; c.insets = new Insets(5, 5, 0, 5); add(bandPassH, c); // minHue slider minSlider = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, sliderRange); c.gridx = 0; c.gridy = y++; c.gridwidth = 1; c.weightx = IJ.isMacintosh()?90:100; c.fill = c.HORIZONTAL; c.insets = new Insets(5, 5, 0, 0); add(minSlider, c); minSlider.addAdjustmentListener(this); minSlider.setUnitIncrement(1); // minHue slider label c.gridx = 1; c.gridwidth = 1; c.weightx = IJ.isMacintosh()?10:0; c.insets = new Insets(5, 0, 0, 0); label1 = new Label(" ", Label.LEFT); label1.setFont(font); add(label1, c); // maxHue sliderHue maxSlider = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, sliderRange); c.gridx = 0; c.gridy = y; c.gridwidth = 1; c.weightx = 100; c.insets = new Insets(5, 5, 0, 0); add(maxSlider, c); maxSlider.addAdjustmentListener(this); maxSlider.setUnitIncrement(1); // maxHue slider label c.gridx = 1; c.gridwidth = 1; c.gridy = y++; c.weightx = 0; c.insets = new Insets(5, 0, 0, 0); label2 = new Label(" ", Label.LEFT); label2.setFont(font); add(label2, c); //===== c.gridx = 0; c.gridy = y++; c.gridwidth = 1; c.weightx = 0; c.insets = new Insets(10, 0, 0, 0); labels = new Label("Saturation", Label.CENTER); add(labels, c); // plot c.gridx = 0; c.gridy = y; c.gridwidth = 1; c.fill = c.BOTH; c.anchor = c.CENTER; c.insets = new Insets(0, 5, 0, 0); add(splot, c); // checkboxes bandPassS = new Checkbox("Pass"); bandPassS.addItemListener(this); bandPassS.setState(true); c.gridx = 1; c.gridy = y++; c.gridwidth = 2; c.insets = new Insets(5, 5, 0, 5); add(bandPassS, c); // minSat slider minSlider2 = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, sliderRange); c.gridx = 0; c.gridy = y++; c.gridwidth = 1; c.weightx = IJ.isMacintosh()?90:100; c.fill = c.HORIZONTAL; c.insets = new Insets(5, 5, 0, 0); add(minSlider2, c); minSlider2.addAdjustmentListener(this); minSlider2.setUnitIncrement(1); // minSat slider label c.gridx = 1; c.gridwidth = 1; c.weightx = IJ.isMacintosh()?10:0; c.insets = new Insets(5, 0, 0, 0); label3 = new Label(" ", Label.LEFT); label3.setFont(font); add(label3, c); // maxSat slider maxSlider2 = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, sliderRange); c.gridx = 0; c.gridy = y++; c.gridwidth = 1; c.weightx = 100; c.insets = new Insets(5, 5, 0, 0); add(maxSlider2, c); maxSlider2.addAdjustmentListener(this); maxSlider2.setUnitIncrement(1); // maxSat slider label c.gridx = 1; c.gridwidth = 1; c.weightx = 0; c.insets = new Insets(5, 0, 0, 0); label4 = new Label(" ", Label.LEFT); label4.setFont(font); add(label4, c); //===== c.gridx = 0; c.gridwidth = 1; c.gridy = y++; c.weightx = 0; c.insets = new Insets(10, 0, 0, 0); labelb = new Label("Brightness", Label.CENTER); add(labelb, c); c.gridx = 0; c.gridwidth = 1; c.gridy = y; c.fill = c.BOTH; c.anchor = c.CENTER; c.insets = new Insets(0, 5, 0, 0); add(bplot, c); // checkboxes bandPassB = new Checkbox("Pass"); bandPassB.addItemListener(this); bandPassB.setState(true); c.gridx = 1; c.gridy = y++; c.gridwidth = 2; c.insets = new Insets(5, 5, 0, 5); add(bandPassB, c); // minBri slider minSlider3 = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, sliderRange); c.gridx = 0; c.gridy = y++; c.gridwidth = 1; c.weightx = IJ.isMacintosh()?90:100; c.fill = c.HORIZONTAL; c.insets = new Insets(5, 5, 0, 0); add(minSlider3, c); minSlider3.addAdjustmentListener(this); minSlider3.setUnitIncrement(1); // minBri slider label c.gridx = 1; c.gridwidth = 1; c.weightx = IJ.isMacintosh()?10:0; c.insets = new Insets(5, 0, 0, 0); label5 = new Label(" ", Label.LEFT); label5.setFont(font); add(label5, c); // maxBri slider maxSlider3 = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, sliderRange); c.gridx = 0; c.gridy = y++; c.gridwidth = 1; c.weightx = 100; c.insets = new Insets(5, 5, 0, 0); add(maxSlider3, c); maxSlider3.addAdjustmentListener(this); maxSlider3.setUnitIncrement(1); // maxBri slider label c.gridx = 1; c.gridwidth = 1; c.weightx = 0; c.insets = new Insets(5, 0, 0, 0); label6 = new Label(" ", Label.LEFT); label6.setFont(font); add(label6, c); GridBagLayout gridbag2 = new GridBagLayout(); GridBagConstraints c2 = new GridBagConstraints(); int y2 = 0; Panel panel = new Panel(); panel.setLayout(gridbag2); // threshoding method choice c2.gridx = 0; c2.gridy = y2; c2.anchor = GridBagConstraints.EAST; c2.gridwidth = 1; c2.insets = new Insets(5, 0, 0, 0); Label theLabel = new Label("Thresholding method:"); gridbag2.setConstraints(theLabel, c2); panel.add(theLabel); methodChoice = new Choice(); for (int i=0; i<methodNames.length; i++) methodChoice.addItem(methodNames[i]); methodChoice.select(method); methodChoice.addItemListener(this); c2.gridx = 1; c2.gridy = y2; c2.anchor = GridBagConstraints.WEST; gridbag2.setConstraints(methodChoice, c2); panel.add(methodChoice); y2++; // display mode choice c2.gridx = 0; c2.gridy = y2; c2.anchor = GridBagConstraints.EAST; c2.insets = new Insets(0, 0, 0, 0); theLabel = new Label("Threshold color:"); gridbag2.setConstraints(theLabel, c2); panel.add(theLabel); modeChoice = new Choice(); for (int i=0; i<modes.length; i++) modeChoice.addItem(modes[i]); modeChoice.select(mode); modeChoice.addItemListener(this); c2.gridx = 1; c2.gridy = y2; c2.anchor = GridBagConstraints.WEST; gridbag2.setConstraints(modeChoice, c2); panel.add(modeChoice); y2++; // color space choice c2.gridx = 0; c2.gridy = y2; c2.anchor = GridBagConstraints.EAST; theLabel = new Label("Color space:"); gridbag2.setConstraints(theLabel, c2); panel.add(theLabel); colorSpaceChoice = new Choice(); for (int i=0; i<colorSpaces.length; i++) colorSpaceChoice.addItem(colorSpaces[i]); colorSpaceChoice.select(HSB); colorSpaceChoice.addItemListener(this); c2.gridx = 1; c2.gridy = y2; c2.anchor = GridBagConstraints.WEST; gridbag2.setConstraints(colorSpaceChoice, c2); panel.add(colorSpaceChoice); y2++; c.gridx = 0; c.gridy = y++; c.gridwidth = 2; c.insets = new Insets(5, 0, 0, 0); c.anchor = GridBagConstraints.CENTER; c.fill = GridBagConstraints.NONE; add(panel, c); //===== panelt = new Panel(); boolean db = Prefs.get("cthresholder.dark", true); darkBackground = new Checkbox("Dark background", db); darkBackground.addItemListener(this); panelt.add(darkBackground); c.gridx = 0; c.gridy = y++; c.gridwidth = 2; c.insets = new Insets(0, 0, 0, 0); add(panelt, c); // buttons int trim = IJ.isMacOSX()?10:0; panel = new Panel(); panel.setLayout(new GridLayout(0, 4, 0, 0)); originalB = new TrimmedButton("Original", trim); //originalB.setEnabled(false); originalB.addActionListener(this); originalB.addKeyListener(ij); panel.add(originalB); filteredB = new TrimmedButton("Filtered", trim); filteredB.setEnabled(false); filteredB.addActionListener(this); filteredB.addKeyListener(ij); panel.add(filteredB); selectB = new TrimmedButton("Select", trim); selectB.addActionListener(this); selectB.addKeyListener(ij); panel.add(selectB); sampleB = new TrimmedButton("Sample", trim); sampleB.addActionListener(this); sampleB.addKeyListener(ij); panel.add(sampleB); stackB = new TrimmedButton("Stack", trim); stackB.addActionListener(this); stackB.addKeyListener(ij); panel.add(stackB); macroB = new TrimmedButton("Macro", trim); macroB.addActionListener(this); macroB.addKeyListener(ij); panel.add(macroB); helpB = new TrimmedButton("Help", trim); helpB.addActionListener(this); helpB.addKeyListener(ij); panel.add(helpB); c.gridx = 0; c.gridy = y++; c.gridwidth = 2; c.insets = new Insets(5, 5, 10, 5); gridbag.setConstraints(panel, c); add(panel); addKeyListener(ij); // ImageJ handles keyboard shortcuts pack(); GUI.center(this); setVisible(true); thread.start(); if (!checkImage()) return; synchronized(this) { notify(); } } public void run() { while (!done) { synchronized(this) { try {wait();} catch(InterruptedException e) {} if (!done) {//RPD ImagePlus imp = WindowManager.getCurrentImage(); if (imp!=null) { reset(imp); apply(imp); imp.updateAndDraw(); } } } } } public synchronized void adjustmentValueChanged(AdjustmentEvent e) { if (IJ.debugMode) IJ.log("ColorThresholder.adjustmentValueChanged "); if (!checkImage()) return; if (e.getSource() == minSlider) adjustMinHue((int) minSlider.getValue()); else if (e.getSource() == maxSlider) adjustMaxHue((int) maxSlider.getValue()); else if (e.getSource() == minSlider2) adjustMinSat((int) minSlider2.getValue()); else if (e.getSource() == maxSlider2) adjustMaxSat((int) maxSlider2.getValue()); else if (e.getSource() == minSlider3) adjustMinBri((int) minSlider3.getValue()); else if (e.getSource() == maxSlider3) adjustMaxBri((int) maxSlider3.getValue()); //originalB.setEnabled(true); updateLabels(); updatePlot(); notify(); } public synchronized void itemStateChanged(ItemEvent e) { if (IJ.debugMode) IJ.log("ColorThresolder.itemStateChanged"); ImagePlus imp = WindowManager.getCurrentImage(); if (imp==null) return; Object source = e.getSource(); if (source==methodChoice) { method = methodChoice.getSelectedItem(); } else if (source==modeChoice) { mode = modeChoice.getSelectedIndex(); } else if (source==colorSpaceChoice) { colorSpace = ((Choice)source).getSelectedIndex(); flag = true; //originalB.setEnabled(false); filteredB.setEnabled(false); minHue=minSat=minBri=0; maxHue=maxSat=maxBri=255; bandPassH.setState(true); bandPassS.setState(true); bandPassB.setState(true); } else if (source==darkBackground) { } reset(imp); if (source==methodChoice || source==colorSpaceChoice || source==darkBackground) autoSetThreshold(); checkImage(); //new updateNames(); notify(); } public void focusGained(FocusEvent e){ if (IJ.debugMode) IJ.log("ColorThresolder.focusGained"); checkImage(); } public void focusLost(FocusEvent e){} public void actionPerformed(ActionEvent e) { if (IJ.debugMode) IJ.log("ColorThresholder.actionPerformed"); Button b = (Button)e.getSource(); if (b==null) return; boolean imageThere = b==sampleB || checkImage(); if (imageThere) { ImagePlus imp = WindowManager.getCurrentImage(); if (imp==null) return; if (b==originalB) { reset(imp); imp.setProperty("OriginalImage", null); filteredB.setEnabled(true); } else if (b==filteredB) { reset(imp); apply(imp); } else if (b==sampleB) { sample(); apply(imp); } else if (b==selectB) { createSelection(); } else if (b==stackB) { applyStack(); } else if (b==macroB) { generateMacro(); return; } else if (b==helpB) { showHelp(); return; } updatePlot(); updateLabels(); imp.updateAndDraw(); } else { IJ.beep(); IJ.showStatus("No Image"); } //notify(); } //Select button pressed void createSelection() { ImagePlus imp = WindowManager.getCurrentImage(); if (imp==null) return; imp.killRoi(); mode = BLACK_AND_WHITE; apply(imp); IJ.run(imp, "8-bit", ""); //if (Prefs.blackBackground) // IJ.run(imp, "Invert", ""); IJ.run(imp, "Create Selection", ""); IJ.run(imp, "RGB Color", ""); reset(imp); } void generateMacro() { if (!Recorder.record) { IJ.error("Threshold Color", "Command recorder is not running"); return; } Recorder.recordString("// Color Thresholder "+IJ.getVersion()+"\n"); Recorder.recordString("// Autogenerated macro, single images only!\n"); Recorder.recordString("min=newArray(3);\n"); Recorder.recordString("max=newArray(3);\n"); Recorder.recordString("filter=newArray(3);\n"); Recorder.recordString("a=getTitle();\n"); if (colorSpace==HSB) { Recorder.recordString("run(\"HSB Stack\");\n"); Recorder.recordString("run(\"Convert Stack to Images\");\n"); Recorder.recordString("selectWindow(\"Hue\");\n"); Recorder.recordString("rename(\"0\");\n"); Recorder.recordString("selectWindow(\"Saturation\");\n"); Recorder.recordString("rename(\"1\");\n"); Recorder.recordString("selectWindow(\"Brightness\");\n"); Recorder.recordString("rename(\"2\");\n"); } else { if (colorSpace==LAB) Recorder.recordString("call(\"ij.plugin.frame.ColorThresholder.RGBtoLab\");\n"); if (colorSpace==YUV) Recorder.recordString("call(\"ij.plugin.frame.ColorThresholder.RGBtoYUV\");\n"); Recorder.recordString("run(\"RGB Stack\");\n"); Recorder.recordString("run(\"Convert Stack to Images\");\n"); Recorder.recordString("selectWindow(\"Red\");\n"); Recorder.recordString("rename(\"0\");\n"); Recorder.recordString("selectWindow(\"Green\");\n"); Recorder.recordString("rename(\"1\");\n"); Recorder.recordString("selectWindow(\"Blue\");\n"); Recorder.recordString("rename(\"2\");\n"); } Recorder.recordString("min[0]="+minSlider.getValue()+";\n"); Recorder.recordString("max[0]="+maxSlider.getValue()+";\n"); if (bandPassH.getState()) Recorder.recordString("filter[0]=\"pass\";\n"); else Recorder.recordString("filter[0]=\"stop\";\n"); Recorder.recordString("min[1]="+minSlider2.getValue()+";\n"); Recorder.recordString("max[1]="+maxSlider2.getValue()+";\n"); if (bandPassS.getState()) Recorder.recordString("filter[1]=\"pass\";\n"); else Recorder.recordString("filter[1]=\"stop\";\n"); Recorder.recordString("min[2]="+minSlider3.getValue()+";\n"); Recorder.recordString("max[2]="+maxSlider3.getValue()+";\n"); if (bandPassB.getState()) Recorder.recordString("filter[2]=\"pass\";\n"); else Recorder.recordString("filter[2]=\"stop\";\n"); Recorder.recordString("for (i=0;i<3;i++){\n"); Recorder.recordString(" selectWindow(\"\"+i);\n"); Recorder.recordString(" setThreshold(min[i], max[i]);\n"); Recorder.recordString(" run(\"Convert to Mask\");\n"); Recorder.recordString(" if (filter[i]==\"stop\") run(\"Invert\");\n"); Recorder.recordString("}\n"); Recorder.recordString("imageCalculator(\"AND create\", \"0\",\"1\");\n"); Recorder.recordString("imageCalculator(\"AND create\", \"Result of 0\",\"2\");\n"); Recorder.recordString("for (i=0;i<3;i++){\n"); Recorder.recordString(" selectWindow(\"\"+i);\n"); Recorder.recordString(" close();\n"); Recorder.recordString("}\n"); Recorder.recordString("selectWindow(\"Result of 0\");\n"); Recorder.recordString("close();\n"); Recorder.recordString("selectWindow(\"Result of Result of 0\");\n"); //if(invert.getState()) // Recorder.recordString("run(\"Invert\");\n"); Recorder.recordString("rename(a);\n"); Recorder.recordString("// Colour Thresholding-------------\n"); } void showHelp() { IJ.showMessage("Help","Color Thresholder\n \n"+ "Modification of Bob Dougherty's BandPass2 plugin by G.Landini\n"+ "to threshold 24 bit RGB images based on HSB, RGB, CIE Lab \n"+ "or YUV components.\n \n"+ "[Pass]: Everything within range is thresholded, otherwise,\n"+ "everything outside range is thresholded.\n \n"+ "[Default] [Huang] [Intermodes] [etc.]: Selects the automatic\n"+ "thresholding method.\n \n"+ "[Red] [White] [Black] [B&W]: Selects the threshold color.\n \n"+ "[Dark background]: Auto-thresholding methods assume\n"+ "light features and dark background.\n \n"+ "[Original]: Shows the original image and updates the buffer\n"+ "when switching to another image.\n \n"+ "[Filtered]: Shows the filtered image.\n \n"+ "[Stack]: Processes the rest of the slices in the stack (if any)\n"+ "using the current settings.\n \n"+ "[Macro]: Creates a macro based on the current settings which\n"+ "is sent to the macro Recorder window, if open.\n \n"+ "[Sample]: (experimental) Sets the ranges of the filters based\n"+ "on the pixel value components in a user-defined ROI.\n \n"+ "[HSB] [RGB] [CIE Lab] [YUV]: Selects the color space.\n \n"+ ""); } void sample() { ImagePlus imp = WindowManager.getCurrentImage(); if (imp==null || imp.getBitDepth()!=24) return; reset(imp); byte[] hsSource,ssSource,bsSource; int [] bin = new int[256]; int counter=0, pi=0, rangePassH = 0, rangeStopH = 0, rangePassL = 0, rangeStopL = 0, i, j; int snumPixels=0; Roi roi = imp.getRoi(); if (roi==null) { IJ.error("Selection required"); return; } ImageProcessor mask = roi.getMask(); Rectangle r = roi.getBoundingRect(); //new ImagePlus("Mask", ipm).show(); // display the mask ImageProcessor ip = imp.getProcessor(); // ROI size if (mask==null) { //rectangular snumPixels = r.width*r.height; } else{ snumPixels=0; for (j=0; j<r.height; j++) { for (i=0; i<r.width; i++) { if (mask.getPixel(i,j)!=0) { snumPixels++; } } } } hsSource = new byte[snumPixels]; ssSource = new byte[snumPixels]; bsSource = new byte[snumPixels]; int [] pixs = new int[snumPixels]; // get pixel in ROI if (mask==null) { for (j=0; j<r.height; j++) { for (i=0; i<r.width; i++) { pixs[counter++] = ip.getPixel(i+r.x, j+r.y); } } } else{ for (j=0; j<r.height; j++) { for (i=0; i<r.width; i++) { if (mask.getPixel(i,j)!=0) { //v1.32c onwards pixs[counter++] = ip.getPixel(i+r.x, j+r.y); } } } } imp.killRoi(); //Get hsb or rgb from roi. //1pixel wide to fit all pixels of a non-square ROI in a ColorProcessor: ColorProcessor cp2 = new ColorProcessor(1, snumPixels, pixs); int iminhue=256, imaxhue=-1, iminsat=256, imaxsat=-1, iminbri=256, imaxbri=-1; int iminred=256, imaxred=-1, imingre=256, imaxgre=-1, iminblu=256, imaxblu=-1; if(colorSpace==RGB) cp2.getRGB(hsSource,ssSource,bsSource); else if(colorSpace==HSB) cp2.getHSB(hsSource,ssSource,bsSource); else if(colorSpace==LAB) getLab(cp2, hsSource,ssSource,bsSource); else if(colorSpace==YUV) getYUV(cp2, hsSource,ssSource,bsSource); for (i = 0; i < snumPixels; i++){ bin[hsSource[i]&255]=1; if ((hsSource[i]&255)>imaxhue) imaxhue=(hsSource[i]&255); if ((hsSource[i]&255)<iminhue) iminhue=(hsSource[i]&255); if ((ssSource[i]&255)>imaxsat) imaxsat=(ssSource[i]&255); if ((ssSource[i]&255)<iminsat) iminsat=(ssSource[i]&255); if ((bsSource[i]&255)>imaxbri) imaxbri=(bsSource[i]&255); if ((bsSource[i]&255)<iminbri) iminbri=(bsSource[i]&255); //IJ.showMessage("h:"+iminhue+"H:"+imaxhue+"s:"+iminsat+"S:"+imaxsat+"b:"+iminbri+"B:"+imaxbri); } if(colorSpace==HSB){ // get pass or stop filter whichever has a narrower range int gap=0, maxgap=0, maxgapst=-1, maxgapen=-1, gapst=0 ; if (bin[0]==0){ gapst=0; gap=1; } for (i = 1; i < 256; i++){ //System.out.println("i:"+i+" bin:"+bin[i]); if (bin[i]==0){ if (bin[i-1]>0){ gap=1; gapst=i; } else { gap++; } if (gap>maxgap){ maxgap=gap; maxgapst=gapst; maxgapen=i; } } } for (i = 0; i < 256; i++){ if (bin[i]>0){ rangePassL = i; break; } } for (i = 255; i >= 0; i--){ if (bin[i]>0){ rangePassH = i; break; } } if ((rangePassH-rangePassL)<maxgap){ bandPassH.setState(true); iminhue=rangePassL; imaxhue=rangePassH; } else{ bandPassH.setState(false); iminhue=maxgapst; imaxhue=maxgapen; } } else { bandPassH.setState(true); } adjustMinHue(iminhue); minSlider.setValue(iminhue); adjustMaxHue(imaxhue); maxSlider.setValue(imaxhue); adjustMinSat(iminsat); minSlider2.setValue(iminsat); adjustMaxSat(imaxsat); maxSlider2.setValue(imaxsat); adjustMinBri(iminbri); minSlider3.setValue(iminbri); adjustMaxBri(imaxbri); maxSlider3.setValue(imaxbri); //originalB.setEnabled(true); //IJ.showStatus("done"); } private boolean checkImage() { if (IJ.debugMode) IJ.log("ColorThresholder.checkImage"); ImagePlus imp = WindowManager.getCurrentImage(); if (imp==null || imp.getBitDepth()!=24) { IJ.beep(); IJ.showStatus("No RGB image"); return false; } imp.killRoi(); boolean ok = setup(imp); return ok; } boolean setup(ImagePlus imp) { if (IJ.debugMode) IJ.log("ColorThresholder.setup"); ImageProcessor ip; int type = imp.getType(); if (type!=ImagePlus.COLOR_RGB) return false; ip = imp.getProcessor(); int id = imp.getID(); int slice = imp.getCurrentSlice(); if ((id!=previousImageID)||(slice!=previousSlice)||(flag) ) { Undo.reset(); flag = false; //if true, flags a change of colour model numSlices = imp.getStackSize(); stack = imp.getStack(); width = stack.getWidth(); height = stack.getHeight(); numPixels = width*height; hSource = new byte[numPixels]; sSource = new byte[numPixels]; bSource = new byte[numPixels]; ImageProcessor mask = new ByteProcessor(width, height); imp.setProperty("Mask", mask); //Get hsb or rgb from image. ColorProcessor cp = (ColorProcessor)ip; IJ.showStatus("Converting colour space..."); if(colorSpace==RGB) cp.getRGB(hSource,sSource,bSource); else if(colorSpace==HSB) cp.getHSB(hSource,sSource,bSource); else if(colorSpace==LAB) getLab(cp, hSource,sSource,bSource); else if(colorSpace==YUV) getYUV(cp, hSource,sSource,bSource); IJ.showStatus(""); //Create a spectrum ColorModel for the Hue histogram plot. Color c; byte[] reds = new byte[256]; byte[] greens = new byte[256]; byte[] blues = new byte[256]; for (int i=0; i<256; i++) { c = Color.getHSBColor(i/255f, 1f, 1f); reds[i] = (byte)c.getRed(); greens[i] = (byte)c.getGreen(); blues[i] = (byte)c.getBlue(); } ColorModel cm = new IndexColorModel(8, 256, reds, greens, blues); //Make an image with just the hue from the RGB image and the spectrum LUT. //This is just for a hue histogram for the plot. Do not show it. //ByteProcessor bpHue = new ByteProcessor(width,height,h,cm); ByteProcessor bpHue = new ByteProcessor(width,height,hSource,cm); ImagePlus impHue = new ImagePlus("Hue",bpHue); //impHue.show(); ByteProcessor bpSat = new ByteProcessor(width,height,sSource,cm); ImagePlus impSat = new ImagePlus("Sat",bpSat); //impSat.show(); ByteProcessor bpBri = new ByteProcessor(width,height,bSource,cm); ImagePlus impBri = new ImagePlus("Bri",bpBri); //impBri.show(); plot.setHistogram(impHue, 0); splot.setHistogram(impSat, 1); bplot.setHistogram(impBri, 2); if (!applyingStack) autoSetThreshold(); imp.updateAndDraw(); } previousImageID = id; previousSlice = slice; return ip!=null; } void autoSetThreshold() { if (IJ.debugMode) IJ.log("ColorThresholder.autoSetThreshold"); boolean darkb = darkBackground!=null && darkBackground.getState(); switch (colorSpace) { case HSB: int[] histogram = bplot.getHistogram(); if (histogram==null) return; int threshold = thresholder.getThreshold(method, histogram); if (darkb) { minBri = threshold+1; maxBri = 255; } else { minBri = 0; maxBri = threshold; } break; case RGB: int[] rhistogram = plot.getHistogram(); threshold = thresholder.getThreshold(method, rhistogram); if (darkb) { minHue = threshold+1; maxHue = 255; } else { minHue = 0; maxHue = threshold; } int[] ghistogram = splot.getHistogram(); threshold = thresholder.getThreshold(method, ghistogram); if (darkb) { minSat = threshold+1; maxSat = 255; } else { minSat = 0; maxSat = threshold; } int[] bhistogram = bplot.getHistogram(); threshold = thresholder.getThreshold(method, bhistogram); if (darkb) { minBri = threshold+1; maxBri = 255; } else { minBri = 0; maxBri = threshold; } break; case LAB: case YUV: histogram = plot.getHistogram(); threshold = thresholder.getThreshold(method, histogram); if (darkb) { minHue = threshold+1; maxHue = 255; } else { minHue = 0; maxHue = threshold; } break; } updateScrollBars(); updateLabels(); updatePlot(); } void updatePlot() { plot.minHue = minHue; plot.maxHue = maxHue; plot.repaint(); splot.minHue = minSat; splot.maxHue = maxSat; splot.repaint(); bplot.minHue = minBri; bplot.maxHue = maxBri; bplot.repaint(); } void updateLabels() { label1.setText(""+((int)minHue)); label2.setText(""+((int)maxHue)); label3.setText(""+((int)minSat)); label4.setText(""+((int)maxSat)); label5.setText(""+((int)minBri)); label6.setText(""+((int)maxBri)); } void updateNames() { if (colorSpace==RGB){ labelh.setText("Red"); labels.setText("Green"); labelb.setText("Blue"); } else if(colorSpace==HSB){ labelh.setText("Hue"); labels.setText("Saturation"); labelb.setText("Brightness"); } else if(colorSpace==LAB){ labelh.setText("L*"); labels.setText("a*"); labelb.setText("b*"); } else if(colorSpace==YUV){ labelh.setText("Y"); labels.setText("U"); labelb.setText("V"); } } void updateScrollBars() { minSlider.setValue((int)minHue); maxSlider.setValue((int)maxHue); minSlider2.setValue((int)minSat); maxSlider2.setValue((int)maxSat); minSlider3.setValue((int)minBri); maxSlider3.setValue((int)maxBri); } void adjustMinHue(int value) { minHue = value; if (maxHue<minHue) { maxHue = minHue; maxSlider.setValue((int)maxHue); } } void adjustMaxHue(int value) { maxHue = value; if (minHue>maxHue) { minHue = maxHue; minSlider.setValue((int)minHue); } } void adjustMinSat(int value) { minSat = value; if (maxSat<minSat) { maxSat = minSat; maxSlider2.setValue((int)maxSat); } } void adjustMaxSat(int value) { maxSat = value; if (minSat>maxSat) { minSat = maxSat; minSlider2.setValue((int)minSat); } } void adjustMinBri(int value) { minBri = value; if (maxBri<minBri) { maxBri = minBri; maxSlider3.setValue((int)maxBri); } } void adjustMaxBri(int value) { maxBri = value; if (minBri>maxBri) { minBri = maxBri; minSlider3.setValue((int)minBri); } } void apply(ImagePlus imp) { if (IJ.debugMode) IJ.log("ColorThresholder.apply"); ImageProcessor fillMaskIP = (ImageProcessor)imp.getProperty("Mask"); if (fillMaskIP==null) return; byte[] fillMask = (byte[])fillMaskIP.getPixels(); byte fill = (byte)255; byte keep = (byte)0; if (bandPassH.getState() && bandPassS.getState() && bandPassB.getState()){ //PPP All pass for (int j = 0; j < numPixels; j++){ int hue = hSource[j]&0xff; int sat = sSource[j]&0xff; int bri = bSource[j]&0xff; if (((hue < minHue)||(hue > maxHue)) || ((sat < minSat)||(sat > maxSat)) || ((bri < minBri)||(bri > maxBri))) fillMask[j] = keep; else fillMask[j] = fill; } } else if(!bandPassH.getState() && !bandPassS.getState() && !bandPassB.getState()){ //SSS All stop for (int j = 0; j < numPixels; j++){ int hue = hSource[j]&0xff; int sat = sSource[j]&0xff; int bri = bSource[j]&0xff; if (((hue >= minHue)&&(hue <= maxHue)) || ((sat >= minSat)&&(sat <= maxSat)) || ((bri >= minBri)&&(bri <= maxBri))) fillMask[j] = keep; else fillMask[j] = fill; } } else if(bandPassH.getState() && bandPassS.getState() && !bandPassB.getState()){ //PPS for (int j = 0; j < numPixels; j++){ int hue = hSource[j]&0xff; int sat = sSource[j]&0xff; int bri = bSource[j]&0xff; if (((hue < minHue)||(hue > maxHue)) || ((sat < minSat)||(sat > maxSat)) || ((bri >= minBri) && (bri <= maxBri))) fillMask[j] = keep; else fillMask[j] = fill; } } else if(!bandPassH.getState() && !bandPassS.getState() && bandPassB.getState()){ //SSP for (int j = 0; j < numPixels; j++){ int hue = hSource[j]&0xff; int sat = sSource[j]&0xff; int bri = bSource[j]&0xff; if (((hue >= minHue) && (hue <= maxHue)) || ((sat >= minSat) && (sat <= maxSat)) || ((bri < minBri) || (bri > maxBri))) fillMask[j] = keep; else fillMask[j] = fill; } } else if (bandPassH.getState() && !bandPassS.getState() && !bandPassB.getState()){ //PSS for (int j = 0; j < numPixels; j++){ int hue = hSource[j]&0xff; int sat = sSource[j]&0xff; int bri = bSource[j]&0xff; if (((hue < minHue) || (hue > maxHue)) || ((sat >= minSat) && (sat <= maxSat)) || ((bri >= minBri) && (bri <= maxBri))) fillMask[j] = keep; else fillMask[j] = fill; } } else if(!bandPassH.getState() && bandPassS.getState() && bandPassB.getState()){ //SPP for (int j = 0; j < numPixels; j++){ int hue = hSource[j]&0xff; int sat = sSource[j]&0xff; int bri = bSource[j]&0xff; if (((hue >= minHue) && (hue <= maxHue))|| ((sat < minSat) || (sat > maxSat)) || ((bri < minBri) || (bri > maxBri))) fillMask[j] = keep; else fillMask[j] = fill; } } else if (!bandPassH.getState() && bandPassS.getState() && !bandPassB.getState()){ //SPS for (int j = 0; j < numPixels; j++){ int hue = hSource[j]&0xff; int sat = sSource[j]&0xff; int bri = bSource[j]&0xff; if (((hue >= minHue)&& (hue <= maxHue)) || ((sat < minSat)||(sat > maxSat)) || ((bri >= minBri) && (bri <= maxBri))) fillMask[j] = keep; else fillMask[j] = fill; } } else if(bandPassH.getState() && !bandPassS.getState() && bandPassB.getState()){ //PSP for (int j = 0; j < numPixels; j++){ int hue = hSource[j]&0xff; int sat = sSource[j]&0xff; int bri = bSource[j]&0xff; if (((hue < minHue) || (hue > maxHue)) || ((sat >= minSat)&&(sat <= maxSat)) || ((bri < minBri) || (bri > maxBri))) fillMask[j] = keep; else fillMask[j] = fill; } } ImageProcessor ip = imp.getProcessor(); if (ip==null) return; if (mode==BLACK_AND_WHITE) { int[] pixels = (int[])ip.getPixels(); int fcolor = Prefs.blackBackground?0xffffffff:0xff000000; int bcolor = Prefs.blackBackground?0xff000000:0xffffffff; for (int i=0; i<numPixels; i++) { if (fillMask[i]!=0) pixels[i] = fcolor; else pixels[i]= bcolor; } } else { ip.setColor(thresholdColor()); ip.fill(fillMaskIP); } } Color thresholdColor() { Color color = null; switch (mode) { case RED: color=Color.red; break; case WHITE: color=Color.white; break; case BLACK: color=Color.black; break; case BLACK_AND_WHITE: color=Color.black; break; } return color; } void applyStack() { ImagePlus imp = WindowManager.getCurrentImage(); if (imp==null) return; applyingStack = true; for (int i = 1; i <= numSlices; i++){ imp.setSlice(i); if (!checkImage()) return; apply(imp); } applyingStack = false; } // Restores the original pixel data void reset(ImagePlus imp) { if (IJ.debugMode) IJ.log("ColorThresholder.reset"); ImageProcessor ip = imp.getProcessor(); ImagePlus originalImage = (ImagePlus)imp.getProperty("OriginalImage"); if (originalImage==null) { originalImage = imp.createImagePlus(); originalImage.setTitle(imp.getTitle()+" (Original)"); originalImage.setProcessor(ip.duplicate()); imp.setProperty("OriginalImage", originalImage); } int[] restore = (int[])originalImage.getProcessor().getPixels(); int[] pixels = (int[])ip.getPixels(); for (int i=0; i<numPixels; i++) pixels[i] = restore[i]; } public void windowActivated(WindowEvent e) { if (IJ.debugMode) IJ.log("ColorThresholder.windowActivated "); ImagePlus imp = WindowManager.getCurrentImage(); if (imp==null || imp.getBitDepth()!=24) { IJ.beep(); IJ.showStatus("No RGB image"); } else { setup(imp); //reset(imp); filteredB.setEnabled(true); } } public void close() { super.close(); instance = null; done = true; Prefs.set("cthresholder.dark", darkBackground.getState()); synchronized(this) { notify(); } } public void getLab(ImageProcessor ip, byte[] L, byte[] a, byte[] b) { // Returns Lab in 3 byte arrays. //http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html#Specifications //http://www.easyrgb.com/math.php?MATH=M7#text7 int c, x, y, i=0; double rf, gf, bf; double X, Y, Z, fX, fY, fZ; double La, aa, bb; double ot=1/3.0, cont = 16/116.0; int width=ip.getWidth(); int height=ip.getHeight(); for(y=0;y<height; y++) { for (x=0; x< width;x++){ c = ip.getPixel(x,y); // RGB to XYZ rf = ((c&0xff0000)>>16)/255.0; //R 0..1 gf = ((c&0x00ff00)>>8)/255.0; //G 0..1 bf = ( c&0x0000ff)/255.0; //B 0..1 // gamma = 1.0? //white reference D65 PAL/SECAM X = 0.430587 * rf + 0.341545 * gf + 0.178336 * bf; Y = 0.222021 * rf + 0.706645 * gf + 0.0713342* bf; Z = 0.0201837* rf + 0.129551 * gf + 0.939234 * bf; // XYZ to Lab if ( X > 0.008856 ) fX = Math.pow(X, ot); else fX = ( 7.78707 * X ) + cont; //7.7870689655172 if ( Y > 0.008856 ) fY = Math.pow(Y, ot); else fY = ( 7.78707 * Y ) + cont; if ( Z > 0.008856 ) fZ = Math.pow(Z, ot); else fZ = ( 7.78707 * Z ) + cont; La = ( 116 * fY ) - 16; aa = 500 * ( fX - fY ); bb = 200 * ( fY - fZ ); // rescale La = (int) (La * 2.55); aa = (int) (Math.floor((1.0625 * aa + 128) + 0.5)); bb = (int) (Math.floor((1.0625 * bb + 128) + 0.5)); //hsb = Color.RGBtoHSB(r, g, b, hsb); // a* and b* range from -120 to 120 in the 8 bit space //L[i] = (byte)((int)(La*2.55) & 0xff); //a[i] = (byte)((int)(Math.floor((1.0625 * aa + 128) + 0.5)) & 0xff); //b[i] = (byte)((int)(Math.floor((1.0625 * bb + 128) + 0.5)) & 0xff); L[i] = (byte)((int)(La<0?0:(La>255?255:La)) & 0xff); a[i] = (byte)((int)(aa<0?0:(aa>255?255:aa)) & 0xff); b[i] = (byte)((int)(bb<0?0:(bb>255?255:bb)) & 0xff); i++; } } } public void getYUV(ImageProcessor ip, byte[] Y, byte[] U, byte[] V) { // Returns YUV in 3 byte arrays. //RGB <--> YUV Conversion Formulas from http://www.cse.msu.edu/~cbowen/docs/yuvtorgb.html //R = Y + (1.4075 * (V - 128)); //G = Y - (0.3455 * (U - 128) - (0.7169 * (V - 128)); //B = Y + (1.7790 * (U - 128); // //Y = R * .299 + G * .587 + B * .114; //U = R * -.169 + G * -.332 + B * .500 + 128.; //V = R * .500 + G * -.419 + B * -.0813 + 128.; int c, x, y, i=0, r, g, b; double yf; int width=ip.getWidth(); int height=ip.getHeight(); for(y=0;y<height; y++) { for (x=0; x< width;x++){ c = ip.getPixel(x,y); r = ((c&0xff0000)>>16);//R g = ((c&0x00ff00)>>8);//G b = ( c&0x0000ff); //B // Kai's plugin yf = (0.299 * r + 0.587 * g + 0.114 * b); Y[i] = (byte)((int)Math.floor(yf + 0.5)) ; U[i] = (byte)(128+(int)Math.floor((0.493 *(b - yf))+ 0.5)); V[i] = (byte)(128+(int)Math.floor((0.877 *(r - yf))+ 0.5)); //Y[i] = (byte) (Math.floor( 0.299 * r + 0.587 * g + 0.114 * b)+.5); //U[i] = (byte) (Math.floor(-0.169 * r - 0.332 * g + 0.500 * b + 128.0)+.5); //V[i] = (byte) (Math.floor( 0.500 * r - 0.419 * g - 0.0813 * b + 128.0)+.5); i++; } } } /** Converts the current image from RGB to CIE L*a*b* and stores the results * in the same RGB image R=L*, G=a*, B=b*. Values are therfore offset and rescaled. * see: * http://www.brucelindbloom.com/index.html?WorkingSpaceInfo.html#Specifications * http://www.easyrgb.com/math.php?MATH=M7#text7 * @author Gabriel Landini, G.Landini@bham.ac.uk */ public static void RGBtoLab() { ImagePlus imp = IJ.getImage(); if (imp.getBitDepth()==24) { RGBtoLab(imp.getProcessor()); imp.updateAndDraw(); } } static void RGBtoLab(ImageProcessor ip) { int xe = ip.getWidth(); int ye = ip.getHeight(); int c, x, y, i=0; double rf, gf, bf; double X, Y, Z, fX, fY, fZ; double La, aa, bb; double ot=1/3.0, cont = 16/116.0; int Li, ai, bi; ImagePlus imp = WindowManager.getCurrentImage(); for(y=0;y<ye;y++){ for (x=0;x<xe;x++){ c=ip.getPixel(x,y); // RGB to XYZ rf = ((c&0xff0000)>>16)/255.0; //R 0..1 gf = ((c&0x00ff00)>>8)/255.0; //G 0..1 bf = ( c&0x0000ff)/255.0; //B 0..1 //white reference D65 PAL/SECAM X = 0.430587 * rf + 0.341545 * gf + 0.178336 * bf; Y = 0.222021 * rf + 0.706645 * gf + 0.0713342* bf; Z = 0.0201837* rf + 0.129551 * gf + 0.939234 * bf; // XYZ to Lab if ( X > 0.008856 ) fX = Math.pow(X, ot); else fX = ( 7.78707 * X ) + cont;//7.7870689655172 if ( Y > 0.008856 ) fY = Math.pow(Y, ot); else fY = ( 7.78707 * Y ) + cont; if ( Z > 0.008856 ) fZ = Math.pow(Z, ot); else fZ = ( 7.78707 * Z ) + cont; La = ( 116 * fY ) - 16; aa = 500 * ( fX - fY ); bb = 200 * ( fY - fZ ); // Lab rescaled to the 0..255 range // a* and b* range from -120 to 120 in the 8 bit space La = La * 2.55; aa = Math.floor((1.0625 * aa + 128) + 0.5); bb = Math.floor((1.0625 * bb + 128) + 0.5); // bracketing Li = (int)(La<0?0:(La>255?255:La)); ai = (int)(aa<0?0:(aa>255?255:aa)); bi = (int)(bb<0?0:(bb>255?255:bb)); ip.putPixel(x,y, ((Li&0xff)<<16)+((ai&0xff)<<8)+(bi&0xff)); } } } /** Converts the current image from RGB to YUV and stores * the results in the same RGB image R=Y, G=U, B=V. * @author Gabriel Landini, G.Landini@bham.ac.uk */ public static void RGBtoYUV() { ImagePlus imp = IJ.getImage(); if (imp.getBitDepth()==24) { RGBtoLab(imp.getProcessor()); imp.updateAndDraw(); } } static void RGBtoYUV(ImageProcessor ip) { int xe = ip.getWidth(); int ye = ip.getHeight(); int c, x, y, i=0, Y, U, V, r, g, b; double yf; ImagePlus imp = WindowManager.getCurrentImage(); for(y=0;y<ye;y++){ for (x=0;x<xe;x++){ c=ip.getPixel(x,y); r = ((c&0xff0000)>>16);//R g = ((c&0x00ff00)>>8);//G b = ( c&0x0000ff); //B // Kai's plugin yf = (0.299 * r + 0.587 * g + 0.114 * b); Y = ((int)Math.floor(yf + 0.5)) ; U = (128+(int)Math.floor((0.493 *(b - yf))+ 0.5)); V = (128+(int)Math.floor((0.877 *(r - yf))+ 0.5)); ip.putPixel(x,y, (((Y<0?0:Y>255?255:Y) & 0xff) << 16)+ (((U<0?0:U>255?255:U) & 0xff) << 8) + ((V<0?0:V>255?255:V) & 0xff)); ip.putPixel(x,y, ((Y & 0xff) <<16) + ((U & 0xff) << 8) + ( V & 0xff)); } } } class BandPlot extends Canvas implements Measurements, MouseListener { final int WIDTH = 256, HEIGHT=64; double minHue = 0, minSat=0, minBri=0; double maxHue = 255, maxSat= 255, maxBri=255; int[] histogram; Color[] hColors; int hmax; Image os; Graphics osg; public BandPlot() { addMouseListener(this); setSize(WIDTH+1, HEIGHT+1); } /** Overrides Component getPreferredSize(). Added to work around a bug in Java 1.4 on Mac OS X.*/ public Dimension getPreferredSize() { return new Dimension(WIDTH+1, HEIGHT+1); } void setHistogram(ImagePlus imp, int j) { ImageProcessor ip = imp.getProcessor(); ImageStatistics stats = ImageStatistics.getStatistics(ip, AREA+MODE, null); int maxCount2 = 0; histogram = stats.histogram; for (int i = 0; i < stats.nBins; i++) if ((histogram[i] > maxCount2) ) maxCount2 = histogram[i]; //if ((histogram[i] > maxCount2) && (i != stats.mode)) maxCount2 = histogram[i]; hmax = (int)(maxCount2 * 1.15);//GL was 1.5 os = null; ColorModel cm = ip.getColorModel(); if (!(cm instanceof IndexColorModel)) return; IndexColorModel icm = (IndexColorModel)cm; int mapSize = icm.getMapSize(); if (mapSize!=256) return; byte[] r = new byte[256]; byte[] g = new byte[256]; byte[] b = new byte[256]; icm.getReds(r); icm.getGreens(g); icm.getBlues(b); hColors = new Color[256]; if (colorSpace==RGB){ if (j==0){ for (int i=0; i<256; i++) hColors[i] = new Color(i&255, 0&255, 0&255); } else if (j==1){ for (int i=0; i<256; i++) hColors[i] = new Color(0&255, i&255, 0&255); } else if (j==2){ for (int i=0; i<256; i++) hColors[i] = new Color(0&255, 0&255, i&255); } } else if (colorSpace==HSB){ if (j==0){ for (int i=0; i<256; i++) hColors[i] = new Color(r[i]&255, g[i]&255, b[i]&255); } else if (j==1){ for (int i=0; i<256; i++) hColors[i] = new Color(255&255, 255-i&255, 255-i&255); //hColors[i] = new Color(192-i/4&255, 192+i/4&255, 192-i/4&255); } else if (j==2){ for (int i=0; i<256; i++) //hColors[i] = new Color(i&255, i&255, 0&255); hColors[i] = new Color(i&255, i&255, i&255); } } else if (colorSpace==LAB){ if (j==0){ for (int i=0; i<256; i++) hColors[i] = new Color(i&255, i&255, i&255); } else if (j==1){ for (int i=0; i<256; i++) hColors[i] = new Color(i&255, 255-i&255, 0&255); } else if (j==2){ for (int i=0; i<256; i++) hColors[i] = new Color(i&255, i&255, 255-i&255); } } else if (colorSpace==YUV){ if (j==0){ for (int i=0; i<256; i++) hColors[i] = new Color(i&255, i&255, i&255); } else if (j==1){ for (int i=0; i<256; i++) hColors[i] = new Color((int)(36+(255-i)/1.4)&255, 255-i&255, i&255); } else if (j==2){ for (int i=0; i<256; i++) hColors[i] = new Color(i&255, 255-i&255, (int)(83+(255-i)/2.87)&255); } } } int[] getHistogram() { return histogram; } public void update(Graphics g) { paint(g); } public void paint(Graphics g ) { int hHist=0; if (histogram!=null) { if (os==null) { os = createImage(WIDTH,HEIGHT); osg = os.getGraphics(); //osg.setColor(Color.white); osg.setColor(new Color(140,152,144)); osg.fillRect(0, 0, WIDTH, HEIGHT); for (int i = 0; i < WIDTH; i++) { if (hColors!=null) osg.setColor(hColors[i]); hHist=HEIGHT - ((int)(HEIGHT * histogram[i])/hmax)-6; osg.drawLine(i, HEIGHT, i, hHist); osg.setColor(Color.black); osg.drawLine(i, hHist, i, hHist); } osg.dispose(); } if (os!=null) g.drawImage(os, 0, 0, this); } else { g.setColor(Color.white); g.fillRect(0, 0, WIDTH, HEIGHT); } g.setColor(Color.black); g.drawLine(0, HEIGHT -6, 256, HEIGHT-6); g.drawRect(0, 0, WIDTH, HEIGHT); g.drawRect((int)minHue, 1, (int)(maxHue-minHue), HEIGHT-7); } public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mouseClicked(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} } // BandPlot class } // BandAdjuster class