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.threshold.global; 011 012import imagingbook.common.histogram.HistogramUtils; 013 014/** 015 * <p> 016 * This is an implementation of the global "quantile" thresholder, described in Sec. 9.1 (Alg. 9.1) of [1]. Requires the 017 * quantile (p) to be specified at instantiation. Method {@link #getThreshold(int[])} returns the minimal threshold that 018 * will put AT LEAST the p-fraction of pixels (but not all pixels) in the background. If the underlying image is flat 019 * (i.e., contains only a single pixel value), {@link GlobalThresholder#NoThreshold} is returned to indicate an invalid 020 * threshold. Similarly there is no valid threshold if it takes the pixels from all brightness levels to fill the 021 * p-quantile. 022 * </p> 023 * <p> 024 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing – An Algorithmic Introduction</em>, 3rd ed, Springer 025 * (2022). Also see the Errata p. 245! 026 * </p> 027 * 028 * @author WB 029 * @version 2022/08/22 030 */ 031public class QuantileThresholder implements GlobalThresholder { 032 033 private final double p; // quantile of expected background pixels 034 035 public QuantileThresholder(double p) { 036 if (p <= 0 || p >= 1) { 037 throw new IllegalArgumentException("quantile p must be 0 < p < 1"); 038 } 039 this.p = p; 040 } 041 042 @Override 043 public float getThreshold(int[] h) { 044 int K = h.length; 045 int N = HistogramUtils.count(h); // total number of pixels 046 double np = N * p; // number of pixels in quantile 047 int i = 0; 048 int c = h[0]; // c = cumulative histogram [i] 049 while (i < K && c < np) { // quantile calculation 050 i = i + 1; 051 c = c + h[i]; 052 } 053 return (c < N) ? i : NoThreshold; // return i if level i does not include all pixels 054 } 055 056}