package gdsc.utils; /*----------------------------------------------------------------------------- * GDSC Plugins for ImageJ * * Copyright (C) 2011 Alex Herbert * Genome Damage and Stability Centre * University of Sussex, UK * * 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. *---------------------------------------------------------------------------*/ import ij.IJ; import ij.ImagePlus; import ij.Prefs; import ij.WindowManager; import ij.gui.GUI; import ij.gui.Toolbar; import ij.macro.MacroRunner; import ij.plugin.MacroInstaller; import ij.plugin.frame.PlugInFrame; import ij.process.ImageProcessor; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Color; import java.awt.Component; import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Label; import java.awt.Point; import java.awt.Scrollbar; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import javax.swing.JPanel; import org.apache.commons.math3.stat.descriptive.SummaryStatistics; import gdsc.UsageTracker; /** * Alows an RGB image to be filtered using HSB limits. */ public class HSB_Picker extends PlugInFrame { private static final long serialVersionUID = 5755638798388461612L; private static final String TITLE = "HSB Picker"; private static final String OPT_LOCATION = "HSB_Picker.location"; private static final double SCALE = 100; private static HSB_Picker instance = null; private Scrollbar sampleSlider; @SuppressWarnings("unused") private Label sampleLabel; private Label nLabel; private Label[] statsLabel; private Scrollbar scaleSlider; @SuppressWarnings("unused") private Label scaleLabel; private Button clearButton, filterButton, okButton, helpButton; private SummaryStatistics[] stats; /** * Constructor */ public HSB_Picker() { super(TITLE); stats = new SummaryStatistics[3]; statsLabel = new Label[3]; for (int i = 0; i < stats.length; i++) { stats[i] = new SummaryStatistics(); } } /* * (non-Javadoc) * * @see ij.plugin.frame.PlugInFrame#run(java.lang.String) */ public void run(String arg) { UsageTracker.recordPlugin(this.getClass(), arg); if (WindowManager.getImageCount() == 0) { IJ.showMessage("No images opened."); return; } if (instance != null) { if (!(instance.getTitle().equals(getTitle()))) { HSB_Picker oldInstance = (HSB_Picker) instance; Prefs.saveLocation(OPT_LOCATION, oldInstance.getLocation()); oldInstance.close(); } else { instance.toFront(); return; } } instance = this; IJ.register(HSB_Picker.class); WindowManager.addWindow(this); createFrame(); addKeyListener(IJ.getInstance()); pack(); Point loc = Prefs.getLocation(OPT_LOCATION); if (loc != null) setLocation(loc); else { GUI.center(this); } if (IJ.isMacOSX()) setResizable(false); setVisible(true); // Install the macro that is called when the image is clicked installTool(); } private void installTool() { StringBuffer sb = new StringBuffer(); sb.append("macro 'HSB Picker Tool - C00fT0610HC0f0T5910SCf00Tac10L' {\n"); sb.append(" call('").append(this.getClass().getName()).append(".run');\n"); sb.append("};\n"); new MacroInstaller().install(sb.toString()); Toolbar.getInstance().setTool(Toolbar.SPARE1); } public static void run() { if (instance != null) { instance.imageClicked(); } else { ImagePlus imp = getCurrentImage(); if (imp == null) return; // Create a new instance HSB_Picker p = new HSB_Picker(); p.run(""); } } private void imageClicked() { ImagePlus imp = getCurrentImage(); if (imp == null) return; Point p = imp.getCanvas().getCursorLoc(); if (p == null) return; ImageProcessor ip = imp.getProcessor(); addValue(ip, p); } /** * @return The current image (must be 24-bit and have an image canvas) */ private static ImagePlus getCurrentImage() { // NOTE: BUG // The ImageCanvas.mousePressed(MouseEvent e) eventually calls // Toolbar.getInstance().runMacroTool(toolID); // This runs the HSB_Picker if the tool is selected. // // This happens before WindowManager.setCurrentImage(...) is called // by the containing window that has been activated or brought to the front. // This means it is possible to click in a different image, raising the ImageCanvas event, // but have the HSL values sampled from the previous current image due to the use // of WindowManager.getCurrentImage(). // // This can be fixed by setting: // WindowManager.setCurrentWindow(win); // in the ImageCanvas.mousePressed(...) method. ImagePlus imp = WindowManager.getCurrentImage(); if (imp == null || imp.getBitDepth() != 24 || imp.getCanvas() == null) return null; return imp; } private void addValue(ImageProcessor ip, Point p) { int[] iArray = new int[3]; float[] hsbvals = new float[3]; int width = sampleSlider.getValue(); int limit = width * width; for (int y = -width; y <= width; y++) { for (int x = -width; x <= width; x++) { if (x * x + y * y > limit) continue; ip.getPixel(x + p.x, y + p.y, iArray); Color.RGBtoHSB(iArray[0], iArray[1], iArray[2], hsbvals); for (int i = 0; i < 3; i++) stats[i].addValue(hsbvals[i]); } } updateDisplayedStatistics(); } private void clear() { for (SummaryStatistics s : stats) s.clear(); updateDisplayedStatistics(); } private void runFilter() { if (stats[0].getN() < 2) { IJ.log("Not enough samples to run the filter"); return; } // Use the SD to set a 95% interval for the width double scale = scaleSlider.getValue() / SCALE; float hWidth = (float) (stats[0].getStandardDeviation() * scale); float sWidth = (float) (stats[1].getStandardDeviation() * scale); float bWidth = (float) (stats[2].getStandardDeviation() * scale); HSB_Filter.hue = clip(stats[0].getMean()); HSB_Filter.hueWidth = clip(hWidth); HSB_Filter.saturation = clip(stats[1].getMean()); HSB_Filter.saturationWidth = clip(sWidth); HSB_Filter.brightness = clip(stats[2].getMean()); HSB_Filter.brightnessWidth = clip(bWidth); IJ.doCommand("HSB Filter"); } private float clip(double d) { return (float) (Math.max(0, Math.min(d, 1))); } private void updateDisplayedStatistics() { nLabel.setText(Long.toString(stats[0].getN())); for (int i = 0; i < 3; i++) statsLabel[i].setText(summary(stats[i])); } private String summary(SummaryStatistics stats) { return String.format("%.3f +/- %.4f", stats.getMean(), stats.getStandardDeviation()); } /* * (non-Javadoc) * * @see ij.plugin.frame.PlugInFrame#close() */ public void close() { Prefs.saveLocation(OPT_LOCATION, getLocation()); instance = null; super.close(); } GridBagLayout mainGrid = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); int row = 0; private void createFrame() { setLayout(mainGrid); createSliderPanel(sampleSlider = new Scrollbar(Scrollbar.HORIZONTAL, 2, 1, 0, 15), "Sample radius", sampleLabel = new Label("0"), 1); createLabelPanel(nLabel = new Label(), "Pixels", "0"); createLabelPanel(statsLabel[0] = new Label(), "Hue", "0"); createLabelPanel(statsLabel[1] = new Label(), "Saturation", "0"); createLabelPanel(statsLabel[2] = new Label(), "Brightness", "0"); // Add the buttons clearButton = new Button("Reset"); clearButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { clear(); } }); add(clearButton, 0, 3); row++; createSliderPanel( scaleSlider = new Scrollbar(Scrollbar.HORIZONTAL, (int) (2 * SCALE), 1, 1, (int) (4 * SCALE)), "Filter scale", scaleLabel = new Label("0"), SCALE); // Add the buttons filterButton = new Button("HSB Filter"); filterButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { runFilter(); } }); okButton = new Button("Close"); okButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { close(); } }); helpButton = new Button("Help"); helpButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String macro = "run('URL...', 'url=" + gdsc.help.URL.UTILITY + "');"; new MacroRunner(macro); } }); JPanel buttonPanel = new JPanel(); FlowLayout l = new FlowLayout(); l.setVgap(0); buttonPanel.setLayout(l); buttonPanel.add(filterButton, BorderLayout.CENTER); buttonPanel.add(okButton, BorderLayout.CENTER); buttonPanel.add(helpButton, BorderLayout.CENTER); add(buttonPanel, 0, 3); row++; updateDisplayedStatistics(); } private void add(Component comp, int x, int width) { c.gridx = x; c.gridy = row; c.gridwidth = width; c.fill = GridBagConstraints.HORIZONTAL; c.anchor = GridBagConstraints.WEST; mainGrid.setConstraints(comp, c); add(comp); } private void createSliderPanel(final Scrollbar sliderField, String label, final Label sliderLabel, final double scale) { Label listLabel = new Label(label, 0); add(listLabel, 0, 1); sliderField.setSize(100, 10); c.ipadx = 75; add(sliderField, 1, 1); c.ipadx = 0; sliderField.addAdjustmentListener(new AdjustmentListener() { public void adjustmentValueChanged(AdjustmentEvent e) { setSliderLabel(sliderField, sliderLabel, scale); } }); add(sliderLabel, 2, 1); setSliderLabel(sliderField, sliderLabel, scale); row++; } private void setSliderLabel(final Scrollbar sliderField, final Label sliderLabel, double scale) { double value = sliderField.getValue() / scale; sliderLabel.setText(String.format("%.2f", value)); } private void createLabelPanel(Label labelField, String label, String value) { Label listLabel = new Label(label, 0); add(listLabel, 0, 1); if (labelField != null) { // labelField.setSize(fontWidth * 3, fontWidth); labelField.setText(value); add(labelField, 1, 2); } row++; } }