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 – 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