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 ******************************************************************************/ 009 010package imagingbook.common.histogram; 011 012/** 013 * <p> 014 * This class represents a discrete "cumulative distribution function" that is piecewise linear. See Sec. 3.6.3 (Fig. 015 * 3.12) of [1] for additional details. 016 * </p> 017 * <p> 018 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing – An Algorithmic Introduction</em>, 3rd ed, Springer 019 * (2022). 020 * </p> 021 * 022 * @author WB 023 */ 024public class PiecewiseLinearCdf { 025 026 private final int K; 027 private final int[] iArr; 028 private final double[] pArr; 029 030 /** 031 * <p> 032 * Constructor, creates a {@link PiecewiseLinearCdf} from a sequence of brightness / cumulative probability pairs. 033 * See Sec. 3.6.3 (Fig. 3.12) of [1] for additional details. Usage example: 034 * </p> 035 * <pre> 036 * int[] ik = {28, 75, 150, 210}; 037 * double[] Pk = {.05, .25, .75, .95}; 038 * PiecewiseLinearCdf pLCdf = new PiecewiseLinearCdf(256, ik, Pk);</pre> 039 * <p> 040 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing – An Algorithmic Introduction</em>, 3rd ed, 041 * Springer (2022). 042 * </p> 043 * 044 * @param K number of brightness values (typ. 256) 045 * @param a a sequence of brightness values serving as control points 046 * @param b a sequence of cumulative probability values in [0,1], one for each control point 047 */ 048 public PiecewiseLinearCdf(int K, int[] a, double[] b) { 049 this.K = K; // number of intensity values (typ. 256) 050 int N = a.length; 051 iArr = new int[N + 2]; // array of intensity values 052 pArr = new double[N + 2]; // array of cum. distribution values 053 iArr[0] = -1; 054 pArr[0] = 0; 055 for (int i = 0; i < N; i++) { 056 iArr[i + 1] = a[i]; 057 pArr[i + 1] = b[i]; 058 } 059 iArr[N + 1] = K - 1; 060 pArr[N + 1] = 1; 061 } 062 063 /** 064 * Returns the cumulative probability for the specified intensity value. 065 * 066 * @param i the intensity value 067 * @return the associated cumulative probability 068 */ 069 public double getCdf(int i) { 070 if (i < 0) 071 return 0; 072 else if (i >= K - 1) 073 return 1; 074 else { 075 int s = 0, N = iArr.length - 1; 076 for (int j = 0; j <= N; j++) { // find s (segment index) 077 if (iArr[j] <= i) 078 s = j; 079 else 080 break; 081 } 082 return pArr[s] + (i - iArr[s]) 083 * ((pArr[s + 1] - pArr[s]) / (iArr[s + 1] - iArr[s])); 084 } 085 } 086 087 /** 088 * Returns the cumulative probabilities for the intensity values 0 to 255 as a {@code double[]}. 089 * 090 * @return the array of cumulative probabilities 091 */ 092 public double[] getCdf() { 093 double[] P = new double[256]; 094 for (int i = 0; i < 256; i++) { 095 P[i] = this.getCdf(i); 096 } 097 return P; 098 } 099 100 /** 101 * Returns the inverse cumulative probability function a = P<sup>-1</sup>(a), that is, the intensity value a 102 * associated with a given cum. probability P. 103 * 104 * @param P a cumulative probability 105 * @return the associated intensity 106 */ 107 public double getInverseCdf(double P) { 108 if (P < getCdf(0)) 109 return 0; 110 else if (P >= 1) 111 return K - 1; 112 else { 113 int r = 0, N = iArr.length - 1; 114 for (int j = 0; j <= N; j++) { // find r (segment index) 115 if (pArr[j] <= P) 116 r = j; 117 else 118 break; 119 } 120 return iArr[r] + (P - pArr[r]) * ((iArr[r + 1] - iArr[r]) / (pArr[r + 1] - pArr[r])); 121 } 122 } 123 124 /** 125 * Returns the probability function for this distribution as a discrete array of probabilities. 126 * 127 * @return the probability array 128 */ 129 public double[] getPdf() { 130 double[] prob = new double[K]; 131 prob[0] = getCdf(0); 132 for (int i = 1; i < K; i++) { 133 prob[i] = getCdf(i) - getCdf(i-1); 134 } 135 return prob; 136 } 137 138}