/*
* 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;
import boofcv.abst.filter.binary.LocalSquareBlockMinMaxBinaryFilter;
import boofcv.alg.filter.binary.impl.ThresholdSauvola;
import boofcv.alg.misc.GImageStatistics;
import boofcv.core.image.GConvertImage;
import boofcv.struct.image.*;
/**
* Weakly typed version of {@link ThresholdImageOps}.
*
* @author Peter Abeles
*/
public class GThresholdImageOps {
/**
*
* Computes the variance based threshold using Otsu's method from an input image. Internally it uses
* {@link #computeOtsu(int[], int, int)} and {@link boofcv.alg.misc.GImageStatistics#histogram(ImageGray, int, int[])}
*
*
* @param input Input gray-scale image
* @param minValue The minimum value of a pixel in the image. (inclusive)
* @param maxValue The maximum value of a pixel in the image. (inclusive)
* @return Selected threshold.
*/
public static int computeOtsu(ImageGray input , int minValue , int maxValue ) {
int range = 1+maxValue - minValue;
int histogram[] = new int[ range ];
GImageStatistics.histogram(input,minValue,histogram);
// Total number of pixels
int total = input.width*input.height;
return computeOtsu(histogram,range,total)+minValue;
}
/**
* Computes the variance based Otsu threshold from a histogram directly. The threshold is selected by minimizing the
* spread of both foreground and background pixel values.
*
* @param histogram Histogram of pixel intensities.
* @param length Number of elements in the histogram.
* @param totalPixels Total pixels in the image
* @return Selected threshold
*/
// original code from http://www.labbookpages.co.uk/software/imgProc/otsuThreshold.html
// Dr. Andrew Greensted
// modifications to reduce overflow
public static int computeOtsu( int histogram[] , int length , int totalPixels ) {
double dlength = length;
double sum = 0;
for (int i=0 ; i< length ; i++)
sum += (i/dlength)*histogram[i];
double sumB = 0;
int wB = 0;
double varMax = 0;
int threshold = 0;
for (int i=0 ; i varMax) {
varMax = varBetween;
threshold = i;
}
}
return threshold;
}
/**
*
* Computes a threshold which maximizes the entropy between the foreground and background regions. See
* {@link #computeEntropy(int[], int, int)} for more details.
*
*
* @see boofcv.alg.misc.GImageStatistics#histogram(ImageGray, int, int[])
*
* @param input Input gray-scale image
* @param minValue The minimum value of a pixel in the image. (inclusive)
* @param maxValue The maximum value of a pixel in the image. (inclusive)
* @return Selected threshold.
*/
public static int computeEntropy(ImageGray input , int minValue , int maxValue ) {
int range = 1 + maxValue - minValue;
int histogram[] = new int[ range ];
GImageStatistics.histogram(input,minValue,histogram);
// Total number of pixels
int total = input.width*input.height;
return computeEntropy(histogram, range, total)+minValue;
}
/**
*
* Computes a threshold which maximizes the entropy between the foreground and background regions. See [1]
* for algorithmic details, which cites [2].
*
*
*
* [1] E.R. Davies "Machine Vision Theory Algorithms Practicalities" 3rd Ed. 2005. pg. 124
* [2] Hannah, Ian, Devesh Patel, and Roy Davies. "The use of variance and entropic thresholding methods
* for image segmentation." Pattern Recognition 28.8 (1995): 1135-1143.
*
*
* @param histogram Histogram of pixel intensities.
* @param length Number of elements in the histogram.
* @param totalPixels Total pixels in the image
* @return Selected threshold
*/
public static int computeEntropy( int histogram[] , int length , int totalPixels ) {
// precompute p[i]*ln(p[i]) and handle special case where p[i] = 0
double p[] = new double[length];
for (int i = 0; i < length; i++) {
int h = histogram[i];
if( h == 0 ) {
p[i] = 0;
} else {
p[i] = h/(double)totalPixels;
p[i] *= Math.log(p[i]);
}
}
double bestScore = 0;
int bestIndex = 0;
int countF = 0;
for (int i=0 ; i bestScore ) {
bestScore = entropy;
bestIndex = i;
}
}
return bestIndex;
}
/**
* Applies a global threshold across the whole image. If 'down' is true, then pixels with values ≤
* to 'threshold' are set to 1 and the others set to 0. If 'down' is false, then pixels with values >
* to 'threshold' are set to 1 and the others set to 0.
*
* @param input Input image. Not modified.
* @param output (Optional) Binary output image. If null a new image will be declared. Modified.
* @param threshold threshold value.
* @param down If true then the inequality ≤ is used, otherwise if false then > is used.
* @return binary image.
*/
public static
GrayU8 threshold(T input , GrayU8 output ,
double threshold , boolean down )
{
if( input instanceof GrayF32) {
return ThresholdImageOps.threshold((GrayF32)input,output,(float)threshold,down);
} else if( input instanceof GrayU8) {
return ThresholdImageOps.threshold((GrayU8)input,output,(int)threshold,down);
} else if( input instanceof GrayU16) {
return ThresholdImageOps.threshold((GrayU16)input,output,(int)threshold,down);
} else if( input instanceof GrayS16) {
return ThresholdImageOps.threshold((GrayS16)input,output,(int)threshold,down);
} else if( input instanceof GrayS32) {
return ThresholdImageOps.threshold((GrayS32)input,output,(int)threshold,down);
} else if( input instanceof GrayF64) {
return ThresholdImageOps.threshold((GrayF64)input,output,threshold,down);
} else {
throw new IllegalArgumentException("Unknown image type: "+input.getClass().getSimpleName());
}
}
/**
*
* Thresholds the image using a locally adaptive threshold that is computed using a local square region centered
* on each pixel. The threshold is equal to the average value of the surrounding pixels times the scale.
* If down is true then b(x,y) = I(x,y) ≤ T(x,y) * scale ? 1 : 0. Otherwise
* b(x,y) = I(x,y) > T(x,y) * scale ? 0 : 1
*
*
*
* NOTE: Internally, images are declared to store intermediate results. If more control is needed over memory
* call the type specific function.
*
*
* @param input Input image.
* @param output (optional) Output binary image. If null it will be declared internally.
* @param radius Radius of square region.
* @param scale Scale factor used to adjust threshold
* @param down Should it threshold up or down.
* @param work1 (Optional) Internal workspace. Can be null
* @param work2 (Optional) Internal workspace. Can be null
* @return binary image.
*/
public static
GrayU8 localSquare(T input, GrayU8 output,
int radius, double scale, boolean down, T work1, T work2)
{
if( input instanceof GrayF32) {
return ThresholdImageOps.localSquare((GrayF32) input, output, radius, (float) scale, down,
(GrayF32) work1, (GrayF32) work2);
} else if( input instanceof GrayU8) {
return ThresholdImageOps.localSquare((GrayU8) input, output, radius, (float) scale, down,
(GrayU8) work1, (GrayU8) work2);
} else {
throw new IllegalArgumentException("Unknown image type: "+input.getClass().getSimpleName());
}
}
/**
*
* Thresholds the image using a locally adaptive threshold that is computed using a local square region centered
* on each pixel. The threshold is equal to the gaussian weighted sum of the surrounding pixels times the scale.
* If down is true then b(x,y) = I(x,y) ≤ T(x,y) * scale ? 1 : 0. Otherwise
* b(x,y) = I(x,y) > T(x,y) * scale ? 0 : 1
*
*
*
* NOTE: Internally, images are declared to store intermediate results. If more control is needed over memory
* call the type specific function.
*
*
* @param input Input image.
* @param output (optional) Output binary image. If null it will be declared internally.
* @param radius Radius of square region.
* @param scale Scale factor used to adjust threshold
* @param down Should it threshold up or down.
* @param work1 (Optional) Internal workspace. Can be null
* @param work2 (Optional) Internal workspace. Can be null
* @return binary image.
*/
public static
GrayU8 localGaussian(T input, GrayU8 output,
int radius, double scale, boolean down,
T work1, ImageGray work2)
{
if( input instanceof GrayF32) {
return ThresholdImageOps.localGaussian((GrayF32) input, output, radius, (float) scale, down,
(GrayF32) work1, (GrayF32) work2);
} else if( input instanceof GrayU8) {
return ThresholdImageOps.localGaussian((GrayU8) input, output, radius, (float) scale, down,
(GrayU8) work1, (GrayU8) work2);
} else {
throw new IllegalArgumentException("Unknown image type: "+input.getClass().getSimpleName());
}
}
/**
* Applies {@link boofcv.alg.filter.binary.impl.ThresholdSauvola Sauvola} thresholding to the input image.
* Intended for use with text image.
*
* @see boofcv.alg.filter.binary.impl.ThresholdSauvola
*
* @param input Input image.
* @param output (optional) Output binary image. If null it will be declared internally.
* @param radius Radius of local region. Try 15
* @param k Positive parameter used to tune threshold. Try 0.3
* @param down Should it threshold up or down.
* @return binary image
*/
public static
GrayU8 localSauvola(T input, GrayU8 output, int radius, float k, boolean down)
{
ThresholdSauvola alg = new ThresholdSauvola(radius,k, down);
if( output == null )
output = new GrayU8(input.width,input.height);
if( input instanceof GrayF32) {
alg.process((GrayF32)input,output);
} else {
GrayF32 conv = new GrayF32(input.width,input.height);
GConvertImage.convert(input, conv);
alg.process(conv,output);
}
return output;
}
/**
* Applies a threshold to an image by computing the min and max values in a regular grid across
* the input image. See {@link ThresholdSquareBlockMinMax} for the details.
*
* @param input Input image.
* @param output (optional) Output binary image. If null it will be declared internally.
* @param radius Radius of square region.
* @param scale Scale factor used to adjust threshold
* @param down Should it threshold up or down.
* @param textureThreshold If the min and max values are within this threshold the pixel will be set to 1.
* @return Binary image
*/
public static
GrayU8 localBlockMinMax(T input, GrayU8 output, int radius, double scale , boolean down, double textureThreshold)
{
LocalSquareBlockMinMaxBinaryFilter alg = new LocalSquareBlockMinMaxBinaryFilter(textureThreshold,radius*2+1,scale,down,
(Class)input.getClass());
if( output == null )
output = new GrayU8(input.width,input.height);
alg.process(input,output);
return output;
}
}
Ads help maintain this website.