001/*******************************************************************************
002 * This software is provided as a supplement to the authors' textbooks on digital
003 * image processing published by Springer-Verlag in various languages and editions.
004 * Permission to use and distribute this software is granted under the BSD 2-Clause
005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause).
006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved.
007 * Visit https://imagingbook.com for additional details.
008 ******************************************************************************/
009package Ch02_Histograms_Statistics;
010
011import Ch03_Point_Operations.Equalize_Histogram;
012import ij.IJ;
013import ij.ImagePlus;
014import ij.gui.GenericDialog;
015import ij.plugin.filter.PlugInFilter;
016import ij.process.ByteProcessor;
017import ij.process.ImageProcessor;
018import imagingbook.common.histogram.HistogramPlot;
019import imagingbook.common.histogram.HistogramUtils;
020import imagingbook.common.ij.DialogUtils;
021import imagingbook.core.jdoc.JavaDocHelp;
022import imagingbook.sampleimages.GeneralSampleImage;
023
024import static imagingbook.common.ij.IjUtils.noCurrentImage;
025
026/**
027 * <p>
028 * ImageJ plugin which calculates and displays (lists) the histogram of a 8-bit grayscale image. See Sec. 2.3 of [1] for
029 * additional details. Alternatively the histogram could be obtained with ImageJs built-in method (see also
030 * {@link Equalize_Histogram}):
031 * </p>
032 * <pre>
033 * int[] h = ip.getHistogram();</pre>
034 * <p>
035 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing &ndash; An Algorithmic Introduction</em>, 3rd ed, Springer
036 * (2022).
037 * </p>
038 *
039 * @author WB
040 * @see HistogramPlot
041 * @see HistogramUtils
042 * @see Show_Histogram
043 */
044public class Compute_Histogram implements PlugInFilter, JavaDocHelp {
045
046        private static boolean ShowHistogramPlot = true;
047        private static boolean ListHistogramEntries = false;
048        private static int HISTOGRAM_HEIGHT = 128;
049
050        private ImagePlus im;
051
052        /** Constructor, asks to open a predefined sample image if no other image is currently open. */
053        public Compute_Histogram() {
054                if (noCurrentImage()) {
055                        DialogUtils.askForSampleImage(GeneralSampleImage.IrishManor);
056                }
057        }
058
059        @Override
060        public int setup(String arg, ImagePlus im) {
061                this.im = im;
062                return DOES_8G + NO_CHANGES;
063        }
064    
065        @Override
066        public void run(ImageProcessor ip) {
067                if (!runDialog()) {
068                        return;
069                }
070
071                int[] h = new int[256];         // histogram array (initialized to zero values)
072                final int M  = ip.getWidth();
073                final int N = ip.getHeight();
074
075                for (int v = 0; v < N; v++) {
076                        for (int u = 0; u < M; u++) {
077                                int i = ip.getPixel(u, v);
078                                h[i] = h[i] + 1;
079                        }
080                }
081                
082                // alternative to the above:
083                // int[] h = ip.getHistogram();
084
085                // ... histogram h[] may now be used
086
087                if (ShowHistogramPlot) {
088                        showHistogram(h, "Histogram of " + im.getShortTitle());
089                }
090                
091                // listing histogram values:
092                if (ListHistogramEntries) {
093                        IJ.log("  i       h[i]");
094                        for (int i = 0; i < h.length; i++) {
095                                IJ.log(String.format("%3d: %8d", i, h[i]));
096                        }
097                }
098        }
099
100        void showHistogram(int[] h, String title) {
101                int height = HISTOGRAM_HEIGHT;
102                int hmax = 0;
103                for (int i = 0; i < h.length; i++) {
104                        hmax = Math.max(hmax,  h[i]);
105                }
106                ByteProcessor hIp = new ByteProcessor(h.length, height);        // create a new image
107                hIp.setColor(255);
108                hIp.fill();
109                hIp.setColor(0);
110                for (int i = 0; i < h.length; i++) {
111                        int len = (int) Math.round(height * h[i] / hmax);       // scale the max value to window height
112                        hIp.drawLine(i, height, i, height - len);
113                }
114                new ImagePlus(title, hIp).show();       // show the new image on screen
115        }
116
117        private boolean runDialog() {
118                GenericDialog gd = new GenericDialog(this.getClass().getSimpleName());
119                gd.addHelp(getJavaDocUrl());
120                gd.addCheckbox("Show histogram plot", ShowHistogramPlot);
121                gd.addCheckbox("List histogram entries", ListHistogramEntries);
122
123                gd.showDialog();
124                if(gd.wasCanceled())
125                        return false;
126
127                ShowHistogramPlot = gd.getNextBoolean();
128                ListHistogramEntries = gd.getNextBoolean();
129                return true;
130        }
131
132}