Saturday, July 25, 2015

Display image's Histogram (RGB) on JavaFX LineChart


Example to display Histogram of on-line images on JavaFX LineChart


remark: In the video, photos from http://www.ultimate-photo-tips.com/histogram-examples.html are used as sample to generate histogram.

Example code:
package javafx_imagehistogram;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelReader;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

/**
 * @web http://java-buddy.blogspot.com
 */
public class JavaFX_ImageHistogram extends Application {

    String defaultImage = "http://goo.gl/kYEQl";

    @Override
    public void start(Stage primaryStage) {

        Label labelInfo = new Label();
        labelInfo.setText(
                "java.version: " + System.getProperty("java.version") + "\n"
                + "javafx.runtime.version: " + System.getProperty("javafx.runtime.version")
        );

        TextField textSrc = new TextField();
        textSrc.setText(defaultImage);
        Button btnDo = new Button("Do Histogram");
        ImageView imageView = new ImageView();

        final CategoryAxis xAxis = new CategoryAxis();
        final NumberAxis yAxis = new NumberAxis();
        final LineChart<String, Number> chartHistogram
                = new LineChart<>(xAxis, yAxis);
        chartHistogram.setCreateSymbols(false);

        btnDo.setOnAction((ActionEvent event) -> {

            String imageSrc = textSrc.getText();
            Image image = new Image(imageSrc);
            imageView.setImage(image);
            chartHistogram.getData().clear();

            ImageHistogram imageHistogram = new ImageHistogram(image);
            if(imageHistogram.isSuccess()){
                chartHistogram.getData().addAll(
                    //imageHistogram.getSeriesAlpha(),
                    imageHistogram.getSeriesRed(),
                    imageHistogram.getSeriesGreen(),
                    imageHistogram.getSeriesBlue());
            }
        });

        HBox hBox = new HBox();
        hBox.getChildren().addAll(imageView, chartHistogram);

        VBox vBox = new VBox();
        vBox.getChildren().addAll(labelInfo, textSrc, btnDo, hBox);

        StackPane root = new StackPane();
        root.getChildren().add(vBox);

        Scene scene = new Scene(root, 1000, 500);

        primaryStage.setTitle("java-buddy.blogspot.com");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

    class ImageHistogram {

        private Image image;

        private long alpha[] = new long[256];
        private long red[] = new long[256];
        private long green[] = new long[256];
        private long blue[] = new long[256];

        XYChart.Series seriesAlpha;
        XYChart.Series seriesRed;
        XYChart.Series seriesGreen;
        XYChart.Series seriesBlue;

        private boolean success;

        ImageHistogram(Image src) {
            image = src;
            success = false;

            //init
            for (int i = 0; i < 256; i++) {
                alpha[i] = red[i] = green[i] = blue[i] = 0;
            }

            PixelReader pixelReader = image.getPixelReader();
            if (pixelReader == null) {
                return;
            }

            //count pixels
            for (int y = 0; y < image.getHeight(); y++) {
                for (int x = 0; x < image.getWidth(); x++) {
                    int argb = pixelReader.getArgb(x, y);
                    int a = (0xff & (argb >> 24));
                    int r = (0xff & (argb >> 16));
                    int g = (0xff & (argb >> 8));
                    int b = (0xff & argb);

                    alpha[a]++;
                    red[r]++;
                    green[g]++;
                    blue[b]++;

                }
            }

            seriesAlpha = new XYChart.Series();
            seriesRed = new XYChart.Series();
            seriesGreen = new XYChart.Series();
            seriesBlue = new XYChart.Series();
            seriesAlpha.setName("alpha");
            seriesRed.setName("red");
            seriesGreen.setName("green");
            seriesBlue.setName("blue");

            //copy alpha[], red[], green[], blue[]
            //to seriesAlpha, seriesRed, seriesGreen, seriesBlue
            for (int i = 0; i < 256; i++) {
                seriesAlpha.getData().add(new XYChart.Data(String.valueOf(i), alpha[i]));
                seriesRed.getData().add(new XYChart.Data(String.valueOf(i), red[i]));
                seriesGreen.getData().add(new XYChart.Data(String.valueOf(i), green[i]));
                seriesBlue.getData().add(new XYChart.Data(String.valueOf(i), blue[i]));
            }

            success = true;
        }

        public boolean isSuccess() {
            return success;
        }

        public XYChart.Series getSeriesAlpha() {
            return seriesAlpha;
        }

        public XYChart.Series getSeriesRed() {
            return seriesRed;
        }

        public XYChart.Series getSeriesGreen() {
            return seriesGreen;
        }

        public XYChart.Series getSeriesBlue() {
            return seriesBlue;
        }
    }

}



- Display brightness histogram on JavaFX LineChart

JavaFX animating BarChart and PieChart


JavaFX example to show both animating BarChart and PieChart:


package javafx_histogram;

import java.util.Random;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.PieChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

/**
 * @web http://java-buddy.blogspot.com
 */
public class JavaFX_Histogram extends Application {

    int group[] = new int[10];

    @Override
    public void start(Stage primaryStage) {

        prepareData();

        Label labelInfo = new Label();
        labelInfo.setText(
                "java.version: " + System.getProperty("java.version") + "\n"
                + "javafx.runtime.version: " + System.getProperty("javafx.runtime.version")
        );

        //barChart1 with setAnimated(false)
        VBox vBoxBarChart1 = new VBox();
        final CategoryAxis xAxis1 = new CategoryAxis();
        final NumberAxis yAxis1 = new NumberAxis();
        final BarChart<String, Number> barChart1
                = new BarChart<>(xAxis1, yAxis1);
        barChart1.setCategoryGap(0);
        barChart1.setBarGap(0);
        barChart1.setMaxSize(500, 450);

        XYChart.Series series1 = new XYChart.Series();
        series1.getData().add(new XYChart.Data("0-10", group[0]));
        series1.getData().add(new XYChart.Data("10-20", group[1]));
        series1.getData().add(new XYChart.Data("20-30", group[2]));
        series1.getData().add(new XYChart.Data("30-40", group[3]));
        series1.getData().add(new XYChart.Data("40-50", group[4]));
        series1.getData().add(new XYChart.Data("50-60", group[5]));
        series1.getData().add(new XYChart.Data("60-70", group[6]));
        series1.getData().add(new XYChart.Data("70-80", group[7]));
        series1.getData().add(new XYChart.Data("80-90", group[8]));
        series1.getData().add(new XYChart.Data("90-100", group[9]));

        barChart1.getData().addAll(series1);
        Label labelAnimated1 = new Label();
        barChart1.setAnimated(false);
        vBoxBarChart1.getChildren().addAll(barChart1, labelAnimated1);
        //- End of barChart1

        //PieChart2 with setAnimated(false)
        VBox vBoxPieChart2 = new VBox();

        ObservableList<PieChart.Data> pieChartData
                = FXCollections.observableArrayList(
                        new PieChart.Data("0-10", group[0]),
                        new PieChart.Data("10-20", group[1]),
                        new PieChart.Data("20-30", group[2]),
                        new PieChart.Data("30-40", group[3]),
                        new PieChart.Data("40-50", group[4]),
                        new PieChart.Data("50-60", group[5]),
                        new PieChart.Data("60-70", group[6]),
                        new PieChart.Data("70-80", group[7]),
                        new PieChart.Data("80-90", group[8]),
                        new PieChart.Data("90-100", group[9])
                );
        
        final PieChart pieChart2
                = new PieChart(pieChartData);
        pieChart2.setMaxSize(500, 450);

        Label labelAnimated2 = new Label();
        pieChart2.setAnimated(false);
        vBoxPieChart2.getChildren().addAll(pieChart2, labelAnimated2);
        //- End of pieChart2

        HBox hBoxCharts = new HBox();
        hBoxCharts.getChildren().addAll(vBoxBarChart1, vBoxPieChart2);

        Label labelCnt = new Label();

        VBox vBox = new VBox();
        vBox.getChildren().addAll(labelInfo, hBoxCharts, labelCnt);

        StackPane root = new StackPane();
        root.getChildren().add(vBox);

        Scene scene = new Scene(root, 1000, 500);

        primaryStage.setTitle("java-buddy.blogspot.com");
        primaryStage.setScene(scene);
        primaryStage.show();

        Random random = new Random();

        //Apply Animating Data in Charts
        //ref: http://docs.oracle.com/javafx/2/charts/bar-chart.htm
        //"Animating Data in Charts" section
        Timeline timeline = new Timeline();
        timeline.getKeyFrames().add(
                new KeyFrame(Duration.millis(200), (ActionEvent actionEvent) -> {

                    int data = random.nextInt(100);
                    /*
                    final int mean = 50;
                    final int standardDeviation = 10;
                    int data = mean + (int) (random.nextGaussian() * standardDeviation);
                    */

                    if (data <= 10) {
                        group[0]++;
                        series1.getData().set(0, new XYChart.Data("0-10", group[0]));
                        pieChartData.set(0, new PieChart.Data("0-10", group[0]));
                    } else if (data <= 20) {
                        group[1]++;
                        series1.getData().set(1, new XYChart.Data("10-20", group[1]));
                        pieChartData.set(1, new PieChart.Data("10-20", group[1]));
                    } else if (data <= 30) {
                        group[2]++;
                        series1.getData().set(2, new XYChart.Data("20-30", group[2]));
                        pieChartData.set(2, new PieChart.Data("20-30", group[2]));
                    } else if (data <= 40) {
                        group[3]++;
                        series1.getData().set(3, new XYChart.Data("30-40", group[3]));
                        pieChartData.set(3, new PieChart.Data("30-40", group[3]));
                    } else if (data <= 50) {
                        group[4]++;
                        series1.getData().set(4, new XYChart.Data("40-50", group[4]));
                        pieChartData.set(4, new PieChart.Data("40-50", group[4]));
                    } else if (data <= 60) {
                        group[5]++;
                        series1.getData().set(5, new XYChart.Data("50-60", group[5]));
                        pieChartData.set(5, new PieChart.Data("50-60", group[5]));
                    } else if (data <= 70) {
                        group[6]++;
                        series1.getData().set(6, new XYChart.Data("60-70", group[6]));
                        pieChartData.set(6, new PieChart.Data("60-70", group[6]));
                    } else if (data <= 80) {
                        group[7]++;
                        series1.getData().set(7, new XYChart.Data("70-80", group[7]));
                        pieChartData.set(7, new PieChart.Data("70-80", group[7]));
                    } else if (data <= 90) {
                        group[8]++;
                        series1.getData().set(8, new XYChart.Data("80-90", group[8]));
                        pieChartData.set(8, new PieChart.Data("80-90", group[8]));
                    } else if (data <= 100) {
                        group[9]++;
                        series1.getData().set(9, new XYChart.Data("10-100", group[9]));
                        pieChartData.set(9, new PieChart.Data("10-100", group[9]));
                    }

                    labelAnimated1.setText(
                            "barChart1.getAnimated() = " + barChart1.getAnimated());
                    labelAnimated2.setText(
                            "barChart2.getAnimated() = " + pieChart2.getAnimated());

                    String s = "";
                    for (int i = 0; i < 10; i++) {
                        s += " " + group[i];
                    }
                    labelCnt.setText(s);
                }));

        timeline.setCycleCount(1000);
        timeline.setAutoReverse(true);  //!?
        timeline.play();

    }

    public static void main(String[] args) {
        launch(args);
    }

    //generate dummy random data
    private void prepareData() {
        for (int i = 0; i < 10; i++) {
            group[i] = 0;
        }
    }

}

Tuesday, July 21, 2015

Wrong report by animating JavaFX BarChart with setAnimated(true)


Last post show "Apply Animaton in JavaFX Charts with Timeline", and mentioned the BarChart show wrong graph if setAnimated(true). In order to show the behavior, the example is modified to create two BarCharts to show the same data. The BarChart on left side with setAnimated(false) show the correct graph, on the right side setAnimated(true) show the wrong graph.


package javafx_histogram;

import java.util.Random;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

/**
 * @web http://java-buddy.blogspot.com
 */
public class JavaFX_Histogram extends Application {

    int group[] = new int[10];

    @Override
    public void start(Stage primaryStage) {

        prepareData();

        Label labelInfo = new Label();
        labelInfo.setText(
                "java.version: " + System.getProperty("java.version") + "\n"
                + "javafx.runtime.version: " + System.getProperty("javafx.runtime.version")
        );

        //barChart1 with setAnimated(false)
        VBox vBoxBarChart1 = new VBox();
        final CategoryAxis xAxis1 = new CategoryAxis();
        final NumberAxis yAxis1 = new NumberAxis();
        final BarChart<String, Number> barChart1
                = new BarChart<>(xAxis1, yAxis1);
        barChart1.setCategoryGap(0);
        barChart1.setBarGap(0);
        barChart1.setMaxSize(500, 450);

        XYChart.Series series1 = new XYChart.Series();
        series1.setName("Animated = false");
        series1.getData().add(new XYChart.Data("0-10", group[0]));
        series1.getData().add(new XYChart.Data("10-20", group[1]));
        series1.getData().add(new XYChart.Data("20-30", group[2]));
        series1.getData().add(new XYChart.Data("30-40", group[3]));
        series1.getData().add(new XYChart.Data("40-50", group[4]));

        series1.getData().add(new XYChart.Data("50-60", group[5]));
        series1.getData().add(new XYChart.Data("60-70", group[6]));
        series1.getData().add(new XYChart.Data("70-80", group[7]));
        series1.getData().add(new XYChart.Data("80-90", group[8]));
        series1.getData().add(new XYChart.Data("90-100", group[9]));

        barChart1.getData().addAll(series1);
        Label labelAnimated1 = new Label();
        barChart1.setAnimated(false);
        vBoxBarChart1.getChildren().addAll(barChart1, labelAnimated1);
        //- End of barChart1
        
        //barChart2 with setAnimated(true)
        VBox vBoxBarChart2 = new VBox();
        final CategoryAxis xAxis2 = new CategoryAxis();
        final NumberAxis yAxis2 = new NumberAxis();
        final BarChart<String, Number> barChart2
                = new BarChart<>(xAxis2, yAxis2);
        barChart2.setCategoryGap(0);
        barChart2.setBarGap(0);
        barChart2.setMaxSize(500, 450);

        XYChart.Series series2 = new XYChart.Series();
        series2.setName("Animated = true");
        series2.getData().add(new XYChart.Data("0-10", group[0]));
        series2.getData().add(new XYChart.Data("10-20", group[1]));
        series2.getData().add(new XYChart.Data("20-30", group[2]));
        series2.getData().add(new XYChart.Data("30-40", group[3]));
        series2.getData().add(new XYChart.Data("40-50", group[4]));

        series2.getData().add(new XYChart.Data("50-60", group[5]));
        series2.getData().add(new XYChart.Data("60-70", group[6]));
        series2.getData().add(new XYChart.Data("70-80", group[7]));
        series2.getData().add(new XYChart.Data("80-90", group[8]));
        series2.getData().add(new XYChart.Data("90-100", group[9]));

        barChart2.getData().addAll(series2);
        Label labelAnimated2 = new Label();
        barChart2.setAnimated(true);
        vBoxBarChart2.getChildren().addAll(barChart2, labelAnimated2);
        //- End of barChart2
        
        HBox hBoxCharts = new HBox();
        hBoxCharts.getChildren().addAll(vBoxBarChart1, vBoxBarChart2);
        
        Label labelCnt = new Label();

        VBox vBox = new VBox();
        vBox.getChildren().addAll(labelInfo, hBoxCharts, labelCnt);

        StackPane root = new StackPane();
        root.getChildren().add(vBox);

        Scene scene = new Scene(root, 1000, 500);

        primaryStage.setTitle("java-buddy.blogspot.com");
        primaryStage.setScene(scene);
        primaryStage.show();

        Random random = new Random();
        
        //Apply Animating Data in Charts
        //ref: http://docs.oracle.com/javafx/2/charts/bar-chart.htm
        //"Animating Data in Charts" section
        
        Timeline timeline = new Timeline();
        timeline.getKeyFrames().add(
                new KeyFrame(Duration.millis(200), (ActionEvent actionEvent) -> {

            //int data = random.nextInt(100);
            final int mean = 50;
            final int standardDeviation = 10;
            int data = mean + (int)(random.nextGaussian() * standardDeviation);
            
            if (data <= 10) {
                group[0]++;
                series1.getData().set(0, new XYChart.Data("0-10", group[0]));
                series2.getData().set(0, new XYChart.Data("0-10", group[0]));
            } else if (data <= 20) {
                group[1]++;
                series1.getData().set(1, new XYChart.Data("10-20", group[1]));
                series2.getData().set(1, new XYChart.Data("10-20", group[1]));
            } else if (data <= 30) {
                group[2]++;
                series1.getData().set(2, new XYChart.Data("20-30", group[2]));
                series2.getData().set(2, new XYChart.Data("20-30", group[2]));
            } else if (data <= 40) {
                group[3]++;
                series1.getData().set(3, new XYChart.Data("30-40", group[3]));
                series2.getData().set(3, new XYChart.Data("30-40", group[3]));
            } else if (data <= 50) {
                group[4]++;
                series1.getData().set(4, new XYChart.Data("40-50", group[4]));
                series2.getData().set(4, new XYChart.Data("40-50", group[4]));
            } else if (data <= 60) {
                group[5]++;
                series1.getData().set(5, new XYChart.Data("50-60", group[5]));
                series2.getData().set(5, new XYChart.Data("50-60", group[5]));
            } else if (data <= 70) {
                group[6]++;
                series1.getData().set(6, new XYChart.Data("60-70", group[6]));
                series2.getData().set(6, new XYChart.Data("60-70", group[6]));
            } else if (data <= 80) {
                group[7]++;
                series1.getData().set(7, new XYChart.Data("70-80", group[7]));
                series2.getData().set(7, new XYChart.Data("70-80", group[7]));
            } else if (data <= 90) {
                group[8]++;
                series1.getData().set(8, new XYChart.Data("80-90", group[8]));
                series2.getData().set(8, new XYChart.Data("80-90", group[8]));
            } else if (data <= 100) {
                group[9]++;
                series1.getData().set(9, new XYChart.Data("10-100", group[9]));
                series2.getData().set(9, new XYChart.Data("10-100", group[9]));
            }

            labelAnimated1.setText(
                    "barChart1.getAnimated() = " + barChart1.getAnimated());
            labelAnimated2.setText(
                    "barChart2.getAnimated() = " + barChart2.getAnimated());
            
            String s = "";
            for(int i=0; i<10; i++){
                s+= " " + group[i];
            }
            labelCnt.setText(s);
        }));

        timeline.setCycleCount(1000);
        timeline.setAutoReverse(true);  //!?
        timeline.play();

    }

    public static void main(String[] args) {
        launch(args);
    }

    //generate dummy random data
    private void prepareData() {
        for (int i = 0; i < 10; i++) {
            group[i] = 0;
        }
    }

}