content: /*
   * Copyright (C) 2019 Adaptech s.r.o., Robert Pösel
   * Copyright (C) 2010 Google Inc.
   *
   * Licensed under the Apache License, Version 2.0 (the "License"); you may not
   * use this file except in compliance with the License. You may obtain a copy of
   * the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   * License for the specific language governing permissions and limitations under
   * the License.
   */
  
  package com.googlecode.leptonica.android;
  
  import androidx.annotation.FloatRange;
  
  /**
   * Image binarization methods.
   *
   * @author alanv@google.com (Alan Viverette)
   */
  @SuppressWarnings("WeakerAccess")
  public class Binarize {
  	static {
  		System.loadLibrary("jpeg");
  		System.loadLibrary("pngx");
  		System.loadLibrary("leptonica");
  	}
  
  	// Otsu thresholding constants
  
  	/**
  	 * Desired tile X dimension; actual size may vary
  	 */
  	public final static int OTSU_SIZE_X = 32;
  
  	/**
  	 * Desired tile Y dimension; actual size may vary
  	 */
  	public final static int OTSU_SIZE_Y = 32;
  
  	/**
  	 * Desired X smoothing value
  	 */
  	public final static int OTSU_SMOOTH_X = 2;
  
  	/**
  	 * Desired Y smoothing value
  	 */
  	public final static int OTSU_SMOOTH_Y = 2;
  
  	/**
  	 * Fraction of the max Otsu score, typically 0.1
  	 */
  	public final static float OTSU_SCORE_FRACTION = 0.1f;
  
  	// Sauvola thresholding constants
  
  	public final static int SAUVOLA_DEFAULT_WINDOW_HALFWIDTH = 8;
  
  	public final static float SAUVOLA_DEFAULT_REDUCTION_FACTOR = 0.35f;
  
  	public final static int SAUVOLA_DEFAULT_NUM_TILES_X = 1;
  
  	public final static int SAUVOLA_DEFAULT_NUM_TILES_Y = 1;
  
  
  	/**
  	 * Performs locally-adaptive Otsu threshold binarization with default
  	 * parameters.
  	 *
  	 * @param pixs An 8 bpp PIX source image.
  	 * @return A 1 bpp thresholded PIX image.
  	 */
  	public static Pix otsuAdaptiveThreshold(Pix pixs) {
  		return otsuAdaptiveThreshold(
  				pixs, OTSU_SIZE_X, OTSU_SIZE_Y, OTSU_SMOOTH_X, OTSU_SMOOTH_Y, OTSU_SCORE_FRACTION);
  	}
  
  	/**
  	 * Performs locally-adaptive Otsu threshold binarization.
  	 * <p>
  	 * Notes:
  	 * <ol>
  	 * <li>The Otsu method finds a single global threshold for an image. This
  	 * function allows a locally adapted threshold to be found for each tile
  	 * into which the image is broken up.
  	 * <li>The array of threshold values, one for each tile, constitutes a
  	 * highly downscaled image. This array is optionally smoothed using a
  	 * convolution. The full width and height of the convolution kernel are (2 *
  	 * smoothX + 1) and (2 * smoothY + 1).
  	 * <li>The minimum tile dimension allowed is 16. If such small tiles are
  	 * used, it is recommended to use smoothing, because without smoothing, each
  	 * small tile determines the splitting threshold independently. A tile that
  	 * is entirely in the image bg will then hallucinate fg, resulting in a very
  	 * noisy binarization. The smoothing should be large enough that no tile is
  	 * only influenced by one type (fg or bg) of pixels, because it will force a
  	 * split of its pixels.
  	 * <li>To get a single global threshold for the entire image, use input
  	 * values of sizeX and sizeY that are larger than the image. For this
  	 * situation, the smoothing parameters are ignored.
  	 * <li>The threshold values partition the image pixels into two classes: one
  	 * whose values are less than the threshold and another whose values are
  	 * greater than or equal to the threshold. This is the same use of
  	 * 'threshold' as in pixThresholdToBinary().
  	 * <li>The scorefract is the fraction of the maximum Otsu score, which is
  	 * used to determine the range over which the histogram minimum is searched.
  	 * See numaSplitDistribution() for details on the underlying method of
  	 * choosing a threshold.
  	 * <li>This uses enables a modified version of the Otsu criterion for
  	 * splitting the distribution of pixels in each tile into a fg and bg part.
  	 * The modification consists of searching for a minimum in the histogram
  	 * over a range of pixel values where the Otsu score is within a defined
  	 * fraction, scoreFraction, of the max score. To get the original Otsu
  	 * algorithm, set scoreFraction == 0.
  	 * </ol>
  	 *
  	 * @param pixs          An 8 bpp PIX source image.
  	 * @param sizeX         Desired tile X dimension; actual size may vary.
  	 * @param sizeY         Desired tile Y dimension; actual size may vary.
  	 * @param smoothX       Half-width of convolution kernel applied to threshold
  	 *                      array: use 0 for no smoothing.
  	 * @param smoothY       Half-height of convolution kernel applied to threshold
  	 *                      array: use 0 for no smoothing.
  	 * @param scoreFraction Fraction of the max Otsu score; typ. 0.1 (use 0.0
  	 *                      for standard Otsu).
  	 * @return A 1 bpp thresholded PIX image.
  	 */
  	public static Pix otsuAdaptiveThreshold(Pix pixs, int sizeX, int sizeY, int smoothX, int smoothY,
  											@FloatRange(from = 0.0, to = 1.0) float scoreFraction) {
  		if (pixs == null)
  			throw new IllegalArgumentException("Source pix must be non-null");
  		if (pixs.getDepth() != 8)
  			throw new IllegalArgumentException("Source pix depth must be 8bpp");
  
  		long nativePix = nativeOtsuAdaptiveThreshold(pixs.getNativePix(),
  				sizeX, sizeY, smoothX, smoothY, scoreFraction);
  
  		if (nativePix == 0)
  			throw new RuntimeException("Failed to perform Otsu adaptive threshold on image");
  
  		return new Pix(nativePix);
  	}
  
  	/**
  	 * Performs Sauvola binarization using default values.
  	 *
  	 * @param pixs An 8 bpp PIX source image.
  	 * @return A 1 bpp thresholded PIX image.
  	 * @see #sauvolaBinarizeTiled(Pix, int, float, int, int)
  	 */
  	public static Pix sauvolaBinarizeTiled(Pix pixs) {
  		return sauvolaBinarizeTiled(pixs, SAUVOLA_DEFAULT_WINDOW_HALFWIDTH,
  				SAUVOLA_DEFAULT_REDUCTION_FACTOR, SAUVOLA_DEFAULT_NUM_TILES_X,
  				SAUVOLA_DEFAULT_NUM_TILES_Y);
  	}
  
  	/**
  	 * Performs Sauvola binarization.
  	 * <p>
  	 * Notes:
  	 * <ol>
  	 * <li> The window width and height are 2 * whsize + 1.  The minimum
  	 * value for whsize is 2; typically it is &gt;= 7.
  	 * <li> For nx == ny == 1, this defaults to pixSauvolaBinarize().
  	 * <li> Why a tiled version?
  	 * (a) Because the mean value accumulator is a uint32, overflow
  	 * can occur for an image with more than 16M pixels.
  	 * (b) The mean value accumulator array for 16M pixels is 64 MB.
  	 * The mean square accumulator array for 16M pixels is 128 MB.
  	 * Using tiles reduces the size of these arrays.
  	 * (c) Each tile can be processed independently, in parallel,
  	 * on a multicore processor.
  	 * <li> The Sauvola threshold is determined from the formula:
  	 * t = m * (1 - k * (1 - s / 128))
  	 * where:
  	 * t = local threshold
  	 * m = local mean
  	 * k = @factor (&gt;= 0)   [ typ. 0.35 ]
  	 * s = local standard deviation, which is maximized at
  	 * 127.5 when half the samples are 0 and half are 255.
  	 * <li> The basic idea of Niblack and Sauvola binarization is that
  	 * the local threshold should be less than the median value, and the larger
  	 * the variance, the closer to the median it should be chosen.  Typical
  	 * values for k are between 0.2 and 0.5.
  	 * </ol>
  	 *
  	 * @param pixs   An 8 bpp PIX source image.
  	 * @param whsize Window half-width for measuring local statistics
  	 * @param factor Factor for reducing threshold due to variance; &gt;= 0
  	 * @param nx     Subdivision into tiles; &gt;= 1
  	 * @param ny     Subdivision into tiles; &gt;= 1
  	 * @return A 1 bpp thresholded PIX image.
  	 */
  	public static Pix sauvolaBinarizeTiled(Pix pixs, int whsize, @FloatRange(from = 0.0) float factor,
  										   int nx, int ny) {
  		if (pixs == null)
  			throw new IllegalArgumentException("Source pix must be non-null");
  		if (pixs.getDepth() != 8)
  			throw new IllegalArgumentException("Source pix depth must be 8bpp");
  
  		long nativePix = nativeSauvolaBinarizeTiled(pixs.getNativePix(),
  				whsize, factor, nx, ny);
  
  		if (nativePix == 0)
  			throw new RuntimeException("Failed to perform Sauvola binarization on image");
  
  		return new Pix(nativePix);
  	}
  
  	// ***************
  	// * NATIVE CODE *
  	// ***************
  
  	private static native long nativeOtsuAdaptiveThreshold(
  			long nativePix, int sizeX, int sizeY, int smoothX, int smoothY, float scoreFract);
  
  	private static native long nativeSauvolaBinarizeTiled(
  			long nativePix, int whsize, float factor, int nx, int ny);
  }
  