Change JFreeChart Histogram colors dynamically?
Asked Answered
T

1

5

I'm trying to show the histogram of an image and show only some colors. I've already done that with JFreeChart and createXYLineChart and getting all the data by iterating over all pixels.

To speed it up I'm trying to do it with "createHistogram". I've followed this code.

To update the chart with the new values I've used this two methods:

XYPlot plot = (XYPlot) chart.getPlot();
plot.setDataset(setHistogram(red, green, blue));

Being setHistogram a method which returns a HistogramDataset depending on which checkbox are activated (boolean red, green and blue).

That works properly as intended.

The second thing I must do when I do that is update the colors of each series, as otherwise they get the default value. I did it following more or less the same procedure as with the values:

private void setHistogramColors(boolean red, boolean green, boolean blue) {
    XYPlot plot = (XYPlot) chart.getPlot();
    XYBarRenderer renderer = (XYBarRenderer) plot.getRenderer();
    renderer.setBarPainter(new StandardXYBarPainter());
    Paint[] paintArray = null;
    if (red) {
        if (green) {
            if (blue) {
                paintArray = new Paint[3];
                paintArray[0] = new Color(0x80ff0000, true);// translucent red, green & blue
                paintArray[1] = new Color(0x8000ff00, true);
                paintArray[2] = new Color(0x800000ff, true);
            } else {
                paintArray = new Paint[2];
                paintArray[0] = new Color(0x80ff0000, true);
                paintArray[1] = new Color(0x8000ff00, true);
            }
        } else {
            paintArray = new Paint[1];
            paintArray[0] = new Color(0x80ff0000, true);
        }
    } else if (green) {
        if (blue) {
            paintArray = new Paint[2];
            paintArray[0] = new Color(0x8000ff00, true);
            paintArray[1] = new Color(0x800000ff, true);
        } else {
            paintArray = new Paint[1];
            paintArray[0] = new Color(0x8000ff00, true);
        }
    } else if (blue){
        paintArray = new Paint[1];
        paintArray[0] = new Color(0x800000ff, true);
    }
    else {
        return;
    }
    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));
}

But for what I can see this only works when I plot the code for the very first time, and the next plots of different series will take the sames colors. Here is an example, RGB are the color they are supposed to be: [2] When I update with the red color goes to the green histogram and green's to the blue one:

Is there anyway to fix this?

Tannenberg answered 10/11, 2016 at 21:25 Comment(0)
T
5

Based on your fragment and this comment, it looks like you are trying to change the display by adding and removing series from the dataset in your checkbox handlers. Instead of altering the model, change the visibility of a series in the view. In this case you can give each checkbox a suitable Action, as shown here and below. Note in particular that VisibleAction uses the series index that matches the corresponding color in the custom DrawingSupplier.

red

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://mcmap.net/q/1021794/-change-jfreechart-histogram-colors-dynamically/230513
 * @see https://mcmap.net/q/429255/-combineddomainxyplot-not-rescaling-domain-axis/230513
 * @see https://mcmap.net/q/1021780/-displaying-a-histogram-of-image-data
 */
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(
                "http://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();
        });
    }
}
Toms answered 11/11, 2016 at 10:13 Comment(2)
I can't thank you enough, yesterday when I saw your comment about using VisibleAction I spent some hours trying to do it, looking all the methods in the JFreeChart, XYPload and ChartPanels trying to get a way to hide the series but couldn't get a solution. Today you just made my day. Thank's a lot.Tannenberg
@nck: Glad to help; this approach seemed faster for your use case.Toms

© 2022 - 2024 — McMap. All rights reserved.