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 Ch03_Point_Operations;
010
011import ij.IJ;
012import ij.ImagePlus;
013import ij.gui.GenericDialog;
014import ij.plugin.filter.PlugInFilter;
015import ij.process.ImageProcessor;
016import imagingbook.common.histogram.HistogramPlot;
017import imagingbook.common.histogram.HistogramUtils;
018import imagingbook.common.histogram.PiecewiseLinearCdf;
019import imagingbook.common.ij.DialogUtils;
020import imagingbook.core.jdoc.JavaDocHelp;
021import imagingbook.sampleimages.GeneralSampleImage;
022
023import static imagingbook.common.ij.IjUtils.noCurrentImage;
024
025/**
026 * <p>
027 * Adapts image intensities to match a reference histogram that is piecewise-linear. See Sec. 3.6.4 (Fig. 3.14) of [1]
028 * for additional details.
029 * </p>
030 * <p>
031 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing &ndash; An Algorithmic Introduction</em>, 3rd ed, Springer
032 * (2022).
033 * </p>
034 *
035 * @author WB
036 * @see HistogramUtils
037 * @see HistogramPlot
038 * @see PiecewiseLinearCdf
039 */
040public class Match_Piecewise_Linear_Histogram implements PlugInFilter, JavaDocHelp {
041
042        private static int[]    a = {28, 75, 150, 210};                 // a_k (brightness values)
043        private static double[] P = {.05, .25, .75, .95};               // P_k (cum. probabilities)
044
045        private static boolean ShowOriginalHistograms = true;
046        private static boolean ShowCumulativeHistograms = true;
047        private static boolean ShowFinalHistogram = true;
048        private static boolean ListMappingFunction = false;
049
050        /** Constructor, asks to open a predefined sample image if no other image is currently open. */
051        public Match_Piecewise_Linear_Histogram() {
052                if (noCurrentImage()) {
053                        DialogUtils.askForSampleImage(GeneralSampleImage.IrishManor);
054                }
055        }
056
057        @Override
058        public int setup(String arg0, ImagePlus im) {
059                return DOES_8G;
060        }
061        
062        @Override
063        public void run(ImageProcessor ipA) {
064                if (!runDialog()) {
065                        return;
066                }
067
068                // get histogram of original image
069                int[] hA = ipA.getHistogram();
070
071                // -------------------------
072                PiecewiseLinearCdf plCdf = new PiecewiseLinearCdf(256, a, P);
073                // -------------------------
074
075                double[] nhB = plCdf.getPdf();
076                nhB = HistogramUtils.normalizeMax(nhB);
077
078                if (ShowOriginalHistograms) {
079                        new HistogramPlot(hA, "Original Histogram").show();
080                        new HistogramPlot(nhB, "Reference Histogram").show();
081                }
082                if (ShowCumulativeHistograms) {
083                        new HistogramPlot(HistogramUtils.cdf(hA), "Original Cumulative Histogram").show();
084                        new HistogramPlot(plCdf.getCdf(), "Reference Cumulative Histogram").show();
085                }
086
087                int[] F = HistogramUtils.matchHistograms(hA, plCdf);
088
089                ipA.applyTable(F);
090                
091                int[] hAm = ipA.getHistogram();
092
093                if (ShowFinalHistogram) {
094                        new HistogramPlot(hAm, "Final Histogram").show();
095                        new HistogramPlot(HistogramUtils.cdf(hAm), "Final Cumulative Histogram").show();
096                }
097
098                if (ListMappingFunction) {
099                        for (int i = 0; i < F.length; i++) {
100                                IJ.log(i + " -> " + F[i]);
101                        }
102                }
103        }
104
105        private boolean runDialog() {
106                GenericDialog gd = new GenericDialog(this.getClass().getSimpleName());
107                gd.addHelp(getJavaDocUrl());
108                gd.addCheckbox("Show original histograms", ShowOriginalHistograms);
109                gd.addCheckbox("Show cumulative histograms", ShowCumulativeHistograms);
110                gd.addCheckbox("Show final histogram", ShowFinalHistogram);
111                gd.addCheckbox("List mapping function", ListMappingFunction);
112
113                gd.showDialog();
114                if(gd.wasCanceled())
115                        return false;
116
117                ShowOriginalHistograms = gd.getNextBoolean();
118                ShowCumulativeHistograms = gd.getNextBoolean();
119                ShowFinalHistogram = gd.getNextBoolean();
120                ListMappingFunction = gd.getNextBoolean();
121                return true;
122        }
123}
124