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.*;
import ij.plugin.filter.ThresholdToSelection;
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");
if (instance!=null) {
WindowManager.toFront(instance);
return;
}
thread = new Thread(this, "BandAdjuster");
WindowManager.addWindow(this);
instance = this;
ij = IJ.getInstance();
Font font = IJ.font10;
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);
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);
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);
minSlider = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, sliderRange);
GUI.fixScrollbar(minSlider);
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);
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);
maxSlider = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, sliderRange);
GUI.fixScrollbar(maxSlider);
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);
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);
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);
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);
minSlider2 = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, sliderRange);
GUI.fixScrollbar(minSlider2);
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);
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);
maxSlider2 = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, sliderRange);
GUI.fixScrollbar(maxSlider2);
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);
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);
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);
minSlider3 = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, sliderRange);
GUI.fixScrollbar(minSlider3);
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);
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);
maxSlider3 = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, sliderRange);
GUI.fixScrollbar(maxSlider3);
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);
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);
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++;
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++;
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);
int trim = IJ.isMacOSX()?10:0;
panel = new Panel();
panel.setLayout(new GridLayout(0, 4, 0, 0));
originalB = new TrimmedButton("Original", trim);
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); GUI.scale(this);
pack();
GUI.centerOnImageJScreen(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) { 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());
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;
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(); 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");
}
}
void createSelection() {
ImagePlus imp = WindowManager.getCurrentImage();
if (imp==null) return;
int saveMode = mode;
mode = BLACK_AND_WHITE;
apply(imp);
mode = saveMode;
ImageProcessor ip = imp.getProcessor().convertToByte(false);
int fg = Prefs.blackBackground?255:0;
ip.setThreshold(fg, fg, ImageProcessor.NO_LUT_UPDATE);
Roi roi = (new ThresholdToSelection()).convert(ip);
reset(imp);
imp.setRoi(roi);
}
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");
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();
ImageProcessor ip = imp.getProcessor();
if (mask==null) { 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];
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) { pixs[counter++] = ip.getPixel(i+r.x, j+r.y);
}
}
}
}
imp.deleteRoi();
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);
}
if(colorSpace==HSB){ 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++){
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);
}
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.deleteRoi();
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; 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);
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("");
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);
ByteProcessor bpHue = new ByteProcessor(width,height,hSource,cm);
ImagePlus impHue = new ImagePlus("Hue",bpHue);
ByteProcessor bpSat = new ByteProcessor(width,height,sSource,cm);
ImagePlus impSat = new ImagePlus("Sat",bpSat);
ByteProcessor bpBri = new ByteProcessor(width,height,bSource,cm);
ImagePlus impBri = new ImagePlus("Bri",bpBri);
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()){ 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()){ 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()){ 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()){ 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()){ 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()){ 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()){ 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()){ 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;
}
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);
}
if (originalImage.getBitDepth()==24) {
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);
filteredB.setEnabled(true);
}
}
public void close() {
super.close();
instance = null;
done = true;
Prefs.set("cthresholder.dark", darkBackground.getState());
synchronized(this) {
notify();
}
}
public static void getLab(ImageProcessor ip, byte[] L, byte[] a, byte[] b) {
ColorSpaceConverter converter = new ColorSpaceConverter();
int[] pixels = (int[])ip.getPixels();
for (int i=0; i<pixels.length; i++) {
double[] values = converter.RGBtoLAB(pixels[i]);
int L1 = (int) (values[0] * 2.55);
int a1 = (int) (Math.floor((1.0625 * values[1] + 128) + 0.5));
int b1 = (int) (Math.floor((1.0625 * values[2] + 128) + 0.5));
L[i] = (byte)((int)(L1<0?0:(L1>255?255:L1)) & 0xff);
a[i] = (byte)((int)(a1<0?0:(a1>255?255:a1)) & 0xff);
b[i] = (byte)((int)(b1<0?0:(b1>255?255:b1)) & 0xff);
}
}
public void getYUV(ImageProcessor ip, byte[] Y, byte[] U, byte[] V) {
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); g = ((c&0x00ff00)>>8); b = ( c&0x0000ff);
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));
i++;
}
}
}
public static void RGBtoLab() {
ImagePlus imp = IJ.getImage();
if (imp.getBitDepth()==24)
imp.setProcessor(RGBtoLab(imp.getProcessor()));
}
private static ImageProcessor RGBtoLab(ImageProcessor ip) {
int n = ip.getPixelCount();
byte[] L = new byte[n];
byte[] a = new byte[n];
byte[] b = new byte[n];
ColorThresholder.getLab(ip, L, a, b);
ColorProcessor cp = new ColorProcessor(ip.getWidth(),ip.getHeight());
cp.setRGB(L,a,b);
return cp;
}
public static void RGBtoYUV() {
ImagePlus imp = IJ.getImage();
if (imp.getBitDepth()==24) {
RGBtoYUV(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); g = ((c&0x00ff00)>>8); b = ( c&0x0000ff);
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);
}
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];
hmax = (int)(maxCount2 * 1.15); 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);
}
else if (j==2){
for (int i=0; i<256; i++)
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(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) {}
}
}