All Downloads are FREE. Search and download functionalities are using the official Maven repository.

boofcv.alg.filter.binary.ThresholdSquareBlockMinMax Maven / Gradle / Ivy

/*
 * 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.alg.InputSanityCheck;
import boofcv.struct.image.GrayU8;
import boofcv.struct.image.ImageGray;
import boofcv.struct.image.ImageInterleaved;

/**
 * 

* Applies a threshold to an image by computing the min and max values in a regular grid across * the image. When thresholding all the pixels inside a box (grid element) the min max values is found * in the surrounding 3x3 grid region. If the difference between min and max is ≤ textureThreshold then * it will be marked as one, since it is considered a textureless region. Otherwise the pixel threshold * is set to (min+max)/2. *

* *

This thresholding strategy is designed to quickly detect shapes with nearly uniform values both inside * the image and along the image border, with locally variable intensity values. The image border is * particularly problematic since there are no neighboring pixels outside the image from which to compute * a local threshold. This is why if a region is considered textureless it is marked as 1.

* *

The min-max values inside a local 3x3 grid region is used to reduce the adverse affects of using a grid. * Ideally a local region around each pixel would be used, but this is expensive to compute. Since a grid is * used instead of a pixel local region boundary conditions can be an issue. For example, consider a black square * in the image, if the grid just happens to lie on this black square perfectly then if you look at only a single * grid element it will be considered textureless and the edge lost. This problem isn't an issue if you consder * a local 3x3 region of blocks.

* *

The size each block in the grid in pixels is adjusted depending on image size. This is done to minimize * "squares" in the upper image boundaries from having many more pixels than other blocks.

* *

The block based approach used were was inspired by a high level description found in AprilTags.

* * @author Peter Abeles */ public abstract class ThresholdSquareBlockMinMax { // interleaved image which stores min and max values inside each block protected I minmax; // if the min and max value's difference is <= to this value then it is considered // to be textureless and a default value is used protected double minimumSpread; // the desired width and height of a block requested by the user protected int requestedBlockWidth; // the adjusted size to minimize extra pixels near the image upper extreme protected int blockWidth,blockHeight; /** * Configures the detector * @param minimumSpread If the difference between min max is less than or equal to this * value then it is considered textureless. Set to <= -1 to disable. * @param requestedBlockWidth About how wide and tall you wish a block to be in pixels. */ public ThresholdSquareBlockMinMax(double minimumSpread, int requestedBlockWidth) { this.minimumSpread = minimumSpread; this.requestedBlockWidth = requestedBlockWidth; } /** * Converts the gray scale input image into a binary image * @param input Input image * @param output Output binary image */ public void process(T input , GrayU8 output ) { InputSanityCheck.checkSameShape(input,output); if( input.width < requestedBlockWidth || input.height < requestedBlockWidth ) { throw new IllegalArgumentException("Image is smaller than block size"); } selectBlockSize(input.width,input.height); minmax.reshape(input.width/blockWidth,input.height/blockHeight); int innerWidth = input.width%blockWidth == 0 ? input.width : input.width-blockWidth-input.width%blockWidth; int innerHeight = input.height%blockHeight == 0 ? input.height : input.height-blockHeight-input.height%blockHeight; computeMinMax(input, innerWidth, innerHeight); applyThreshold(input,output); } /** * Selects a block size which is close to the requested block size by the user */ void selectBlockSize( int width , int height ) { int rows = height/requestedBlockWidth; int cols = width/requestedBlockWidth; blockHeight = height/rows; blockWidth = width/cols; } /** * Computes the min-max value for each block in the image */ private void computeMinMax(T input, int innerWidth, int innerHeight) { int indexMinMax = 0; for (int y = 0; y < innerHeight; y += blockHeight) { for (int x = 0; x < innerWidth; x += blockWidth, indexMinMax += 2) { computeMinMaxBlock(x,y,blockWidth,blockHeight,indexMinMax,input); } // handle the case where the image's width isn't evenly divisible by the block's width if( innerWidth != input.width ) { computeMinMaxBlock(innerWidth,y,input.width-innerWidth,blockHeight,indexMinMax,input); indexMinMax += 2; } } // handle the case where the image's height isn't evenly divisible by the block's height if( innerHeight != input.height ) { int y = innerHeight; int blockHeight = input.height-innerHeight; for (int x = 0; x < innerWidth; x += blockWidth, indexMinMax += 2) { computeMinMaxBlock(x,y,blockWidth,blockHeight,indexMinMax,input); } if( innerWidth != input.width ) { computeMinMaxBlock(innerWidth,y,input.width-innerWidth,blockHeight,indexMinMax,input); } } } /** * Applies the dynamically computed threshold to each pixel in the image, one block at a time */ private void applyThreshold( T input, GrayU8 output ) { for (int blockY = 0; blockY < minmax.height; blockY++) { for (int blockX = 0; blockX < minmax.width; blockX++) { thresholdBlock(blockX,blockY,input,output); } } } /** * Thresholds all the pixels inside the specified block * @param blockX0 Block x-coordinate * @param blockY0 Block y-coordinate * @param input Input image * @param output Output image */ protected abstract void thresholdBlock(int blockX0 , int blockY0 , T input, GrayU8 output ); /** * Computes the min-max value inside a block * @param x0 lower bound pixel value of block, x-axis * @param y0 upper bound pixel value of block, y-axis * @param width Block's width * @param height Block's height * @param indexMinMax array index of min-max image pixel * @param input Input image */ protected abstract void computeMinMaxBlock(int x0 , int y0 , int width , int height , int indexMinMax , T input); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy