/*
 * Copyright (c) 2011-2016, Peter Abeles. All Rights Reserved.
 *
 * This file is part of BoofCV (http://boofcv.org).
 *
 * 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 boofcv.alg.filter.binary.impl;

import boofcv.alg.filter.blur.BlurImageOps;
import boofcv.alg.misc.ImageStatistics;
import boofcv.alg.misc.PixelMath;
import boofcv.struct.image.GrayF32;
import boofcv.struct.image.GrayU8;

/**
 * 

* Intended for use as a preprocessing step in OCR it computes a binary image from an input gray image. It's * an adaptive algorithm and uses the local mean and standard deviation, as is shown in the equation below:
* T(x,y) = m(x,y) * [ 1 + k * (s(x,y)/R - 1)]
* where T(x,y) is the pixel's threshold, m(x,y) is the local mean, s(x,y) is the local deviation, * R is dynamic range of standard deviation, and k is a user specified threshold. *

* *

* There are two tuning parameters 'k' a positive number and the the 'radius' of the local region. Recommended * values are k=0.3 and radius=15. These were found by tuning against a set of text. *

* * @author Peter Abeles */ public class ThresholdSauvola { // user specified threshold float k; // size of local region int radius; // should it threshold down or up boolean down; // storage for intermediate results GrayF32 inputPow2 = new GrayF32(1,1); // I^2 GrayF32 inputMean = new GrayF32(1,1); // local mean of I GrayF32 inputMeanPow2 = new GrayF32(1,1); // pow2 of local mean of I GrayF32 inputPow2Mean = new GrayF32(1,1); // local mean of I^2 GrayF32 stdev = new GrayF32(1,1); // computed standard deviation GrayF32 tmp = new GrayF32(1,1); // work space /** * Configures the algorithm. * @param radius size of local radius. Try 15 * @param k User specified threshold adjustment factor. Must be positive. Try 0.3 * @param down Threshold down or up */ public ThresholdSauvola(int radius, float k, boolean down) { this.k = k; this.radius = radius; this.down = down; } /** * Converts the input image into a binary image. * * @param input Input image. Not modified. * @param output Output binary image. Modified. */ public void process(GrayF32 input , GrayU8 output ) { inputPow2.reshape(input.width,input.height); inputMean.reshape(input.width,input.height); inputMeanPow2.reshape(input.width,input.height); inputPow2Mean.reshape(input.width,input.height); stdev.reshape(input.width,input.height); tmp.reshape(input.width,input.height); inputPow2.reshape(input.width,input.height); // mean of input image = E[X] BlurImageOps.mean(input, inputMean, radius, tmp); // standard deviation = sqrt( E[X^2] + E[X]^2) PixelMath.pow2(input, inputPow2); BlurImageOps.mean(inputPow2,inputPow2Mean,radius,tmp); PixelMath.pow2(inputMean,inputMeanPow2); PixelMath.subtract(inputPow2Mean, inputMeanPow2, stdev); PixelMath.sqrt(stdev, stdev); float R = ImageStatistics.max(stdev); if( down ) { for (int y = 0; y < input.height; y++) { int i = y * stdev.width; int indexIn = input.startIndex + y * input.stride; int indexOut = output.startIndex + y * output.stride; for (int x = 0; x < input.width; x++, i++) { // threshold = mean.*(1 + k * ((deviation/R)-1)); float threshold = inputMean.data[i] * (1.0f + k * (stdev.data[i] / R - 1.0f)); output.data[indexOut++] = (byte) (input.data[indexIn++] <= threshold ? 1 : 0); } } } else { for (int y = 0; y < input.height; y++) { int i = y * stdev.width; int indexIn = input.startIndex + y * input.stride; int indexOut = output.startIndex + y * output.stride; for (int x = 0; x < input.width; x++, i++) { // threshold = mean.*(1 + k * ((deviation/R)-1)); float threshold = inputMean.data[i] * (1.0f + k * (stdev.data[i] / R - 1.0f)); output.data[indexOut++] = (byte) (input.data[indexIn++] >= threshold ? 1 : 0); } } } } public float getK() { return k; } public void setK(float k) { this.k = k; } public int getRadius() { return radius; } public void setRadius(int radius) { this.radius = radius; } public boolean isDown() { return down; } public void setDown(boolean down) { this.down = down; } }

Ads help maintain this website.