Displaying a histogram of image data
Solution 1:
The example below uses several techniques to create an RGB histogram of an arbitrary image:
-
The
Raster
methodgetSamples()
extracts the values of each color band from theBufferedImage
. -
The
HistogramDataset
methodaddSeries()
adds each band's counts to thedataset
. -
A
StandardXYBarPainter
replaces theChartFactory
default, as shown here. -
A custom
DefaultDrawingSupplier
supplies the color required for each series; it contains translucent colors. -
A variation of
VisibleAction
, discussed here, is used to control the visibility of each band; a complementary approach usingChartMouseListener
is shown here.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Paint;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.DefaultDrawingSupplier;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYBarPainter;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.statistics.HistogramDataset;
/**
* @see https://stackoverflow.com/q/40537278/230513
* @see https://stackoverflow.com/q/11870416/230513
* @see https://stackoverflow.com/a/28519356/230513
*/
public class Histogram {
private static final int BINS = 256;
private final BufferedImage image = getImage();
private HistogramDataset dataset;
private XYBarRenderer renderer;
private BufferedImage getImage() {
try {
return ImageIO.read(new URL(
"https://i.imgur.com/kxXhIH1.jpg"));
} catch (IOException e) {
e.printStackTrace(System.err);
}
return null;
}
private ChartPanel createChartPanel() {
// dataset
dataset = new HistogramDataset();
Raster raster = image.getRaster();
final int w = image.getWidth();
final int h = image.getHeight();
double[] r = new double[w * h];
r = raster.getSamples(0, 0, w, h, 0, r);
dataset.addSeries("Red", r, BINS);
r = raster.getSamples(0, 0, w, h, 1, r);
dataset.addSeries("Green", r, BINS);
r = raster.getSamples(0, 0, w, h, 2, r);
dataset.addSeries("Blue", r, BINS);
// chart
JFreeChart chart = ChartFactory.createHistogram("Histogram", "Value",
"Count", dataset, PlotOrientation.VERTICAL, true, true, false);
XYPlot plot = (XYPlot) chart.getPlot();
renderer = (XYBarRenderer) plot.getRenderer();
renderer.setBarPainter(new StandardXYBarPainter());
// translucent red, green & blue
Paint[] paintArray = {
new Color(0x80ff0000, true),
new Color(0x8000ff00, true),
new Color(0x800000ff, true)
};
plot.setDrawingSupplier(new DefaultDrawingSupplier(
paintArray,
DefaultDrawingSupplier.DEFAULT_FILL_PAINT_SEQUENCE,
DefaultDrawingSupplier.DEFAULT_OUTLINE_PAINT_SEQUENCE,
DefaultDrawingSupplier.DEFAULT_STROKE_SEQUENCE,
DefaultDrawingSupplier.DEFAULT_OUTLINE_STROKE_SEQUENCE,
DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE));
ChartPanel panel = new ChartPanel(chart);
panel.setMouseWheelEnabled(true);
return panel;
}
private JPanel createControlPanel() {
JPanel panel = new JPanel();
panel.add(new JCheckBox(new VisibleAction(0)));
panel.add(new JCheckBox(new VisibleAction(1)));
panel.add(new JCheckBox(new VisibleAction(2)));
return panel;
}
private class VisibleAction extends AbstractAction {
private final int i;
public VisibleAction(int i) {
this.i = i;
this.putValue(NAME, (String) dataset.getSeriesKey(i));
this.putValue(SELECTED_KEY, true);
renderer.setSeriesVisible(i, true);
}
@Override
public void actionPerformed(ActionEvent e) {
renderer.setSeriesVisible(i, !renderer.getSeriesVisible(i));
}
}
private void display() {
JFrame f = new JFrame("Histogram");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(createChartPanel());
f.add(createControlPanel(), BorderLayout.SOUTH);
f.add(new JLabel(new ImageIcon(image)), BorderLayout.WEST);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
new Histogram().display();
});
}
}
Solution 2:
Using the Chart2D
library, the example below illustrates some alternative approaches.
A
ColorConvertOp
is used to convert the sample image to grayscale, as shown here and here.Nested loops iterate over the pixels of the
BufferedImage
, invoking thegetRGB()
method to extract the value of each pixel; the corresponding counts are used to construct thedataset
.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import net.sourceforge.chart2d.Chart2DProperties;
import net.sourceforge.chart2d.Dataset;
import net.sourceforge.chart2d.GraphChart2DProperties;
import net.sourceforge.chart2d.GraphProperties;
import net.sourceforge.chart2d.LBChart2D;
import net.sourceforge.chart2d.LegendProperties;
import net.sourceforge.chart2d.MultiColorsProperties;
import net.sourceforge.chart2d.Object2DProperties;
/** @see https://stackoverflow.com/q/9964872/230513 */
public class Histogram extends JPanel {
private BufferedImage image = getImage("OptionPane.warningIcon");
private BufferedImage gray = getGray(image);
public Histogram() {
JPanel panel = new JPanel(new GridLayout(0, 1));
panel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
panel.add(new JLabel(new ImageIcon(image)));
panel.add(new JLabel(new ImageIcon(gray)));
this.setLayout(new BorderLayout());
this.add(panel, BorderLayout.WEST);
this.add(createChart(gray, 20), BorderLayout.CENTER);
}
private BufferedImage getImage(String name) {
Icon icon = UIManager.getIcon(name);
int w = icon.getIconWidth();
int h = icon.getIconHeight();
this.setPreferredSize(new Dimension(w, h));
BufferedImage i = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D) i.getGraphics();
g2d.setPaint(new GradientPaint(
0, 0, Color.blue, w, h, Color.green, true));
g2d.fillRect(0, 0, w, h);
icon.paintIcon(null, g2d, 0, 0);
g2d.dispose();
return i;
}
private BufferedImage getGray(BufferedImage image) {
BufferedImage g = new BufferedImage(
image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
ColorConvertOp op = new ColorConvertOp(
image.getColorModel().getColorSpace(),
g.getColorModel().getColorSpace(), null);
op.filter(image, g);
return g;
}
private LBChart2D createChart(BufferedImage gray, int buckets) {
// Chart2D configuration
Object2DProperties object2DProps = new Object2DProperties();
object2DProps.setObjectTitleText("Gray Histogram");
Chart2DProperties chart2DProps = new Chart2DProperties();
chart2DProps.setChartDataLabelsPrecision(-1);
LegendProperties legendProps = new LegendProperties();
String[] legendLabels = {"Gray"};
legendProps.setLegendLabelsTexts(legendLabels);
GraphChart2DProperties graphChart2DProps = new GraphChart2DProperties();
graphChart2DProps.setLabelsAxisTitleText("Gray");
graphChart2DProps.setNumbersAxisTitleText("Count");
// Dataset
String[] labelsAxisLabels = new String[buckets];
for (int i = 0; i < labelsAxisLabels.length; i++) {
labelsAxisLabels[i] = String.valueOf(i * 256 / buckets);
}
graphChart2DProps.setLabelsAxisLabelsTexts(labelsAxisLabels);
int[] counts = new int[buckets];
for (int r = 0; r < gray.getHeight(); r++) {
for (int c = 0; c < gray.getWidth(); c++) {
int v = (gray.getRGB(c, r) & 0xff) * buckets / 256;
counts[v]++;
}
}
Dataset dataset = new Dataset(1, counts.length, 1);
for (int i = 0; i < counts.length; i++) {
dataset.set(0, i, 0, counts[i]);
}
GraphProperties graphProps = new GraphProperties();
MultiColorsProperties multiColorsProps = new MultiColorsProperties();
LBChart2D chart2D = new LBChart2D();
chart2D.setObject2DProperties(object2DProps);
chart2D.setChart2DProperties(chart2DProps);
chart2D.setLegendProperties(legendProps);
chart2D.setGraphChart2DProperties(graphChart2DProps);
chart2D.addGraphProperties(graphProps);
chart2D.addDataset(dataset);
chart2D.addMultiColorsProperties(multiColorsProps);
//Optional validation: Prints debug messages if invalid only.
if (!chart2D.validate(false)) {
chart2D.validate(true);
}
return chart2D;
}
private void display() {
JFrame f = new JFrame("Histogram");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setSize(640, 480);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new Histogram().display();
}
});
}
}