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

boofcv.alg.feature.detect.peak.MeanShiftPeak Maven / Gradle / Ivy

/*
 * Copyright (c) 2011-2019, 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.feature.detect.peak;

import boofcv.alg.interpolate.InterpolatePixelS;
import boofcv.alg.weights.WeightPixel_F32;
import boofcv.factory.interpolate.FactoryInterpolation;
import boofcv.struct.border.BorderType;
import boofcv.struct.image.ImageGray;

/**
 * Simple implementations of mean-shift intended to finding local peaks inside an intensity image.
 * Implementations differ in how they weigh each point.  In each iterations the mean of the square sample region
 * centered around the target is computed and the center shifted so that location.  It stops when change
 * is less than a delta or the maximum number of iterations has been exceeded.
 *
 * If the image border is hit while searching the square will be push back until it is entirely contained inside
 * the image.
 *
 * This is NOT thread safe.
 *
 * @author Peter Abeles
 */
public class MeanShiftPeak> {

	// Input image and interpolation function
	protected T image;
	protected InterpolatePixelS interpolate;

	// the maximum number of iterations it will perform
	protected int maxIterations;

	// size of the kernel
	protected int radius;
	protected int width;

	// if the change in x and y is less than this value stop the search
	protected float convergenceTol;

	// The peak in x and y
	protected float peakX,peakY;

	protected boolean odd;

	// used to compute the weight each pixel contributes to the mean
	protected WeightPixel_F32 weights;

	/**
	 * Configures search.
	 *  @param maxIterations  Maximum number of iterations.  Try 10
	 * @param convergenceTol Convergence tolerance.  Try 1e-3
	 * @param weights Used to compute the weight each pixel contributes to the mean.  Try a uniform distribution.
	 * @param borderType
	 */
	public MeanShiftPeak(int maxIterations, float convergenceTol,
						 WeightPixel_F32 weights, boolean odd,
						 Class imageType, BorderType borderType) {
		this.maxIterations = maxIterations;
		this.convergenceTol = convergenceTol;
		this.odd = odd;
		this.weights = weights;
		interpolate = FactoryInterpolation.bilinearPixelS(imageType, borderType);
	}

	/**
	 * Specifies the input image
	 * @param image input image
	 */
	public void setImage(T image) {
		this.image = image;
		this.interpolate.setImage(image);
	}

	public void setRadius(int radius) {
		this.weights.setRadius( radius, radius, odd );
		this.radius = radius;
		if( odd ) {
			this.width = radius*2+1;
		} else {
			this.width = radius*2;
		}
	}

	/**
	 * Performs a mean-shift search center at the specified coordinates
	 */
	public void search( float cx , float cy ) {
		peakX = cx; peakY = cy;
		if( radius <= 0 ) { // can turn off refinement by setting radius to zero
			return;
		}
		float offset = -radius + (weights.isOdd()?0:0.5f);

		for( int i = 0; i < maxIterations; i++ ) {
			float total = 0;
			float sumX = 0, sumY = 0;

			int kernelIndex = 0;

			// see if it can use fast interpolation otherwise use the safer technique
			if( interpolate.isInFastBounds(peakX+offset, peakY+offset) &&
					interpolate.isInFastBounds(peakX-offset, peakY-offset)) {
				for( int yy = 0; yy < width; yy++ ) {
					float y = offset+yy;
					for( int xx = 0; xx < width; xx++ ) {
						float x = offset+xx;
						float w = weights.weightIndex(kernelIndex++);
						float weight = w*interpolate.get_fast(peakX+x, peakY+y);
						total += weight;
						sumX += weight*x;
						sumY += weight*y;
					}
				}
			} else {
				for( int yy = 0; yy < width; yy++ ) {
					float y = offset+yy;
					for( int xx = 0; xx < width; xx++ ) {
						float x = offset+xx;
						float w = weights.weightIndex(kernelIndex++);
						float weight = w*interpolate.get(peakX+x, peakY+y);
						total += weight;
						sumX += weight*x;
						sumY += weight*y;
					}
				}
			}

			// avoid divided by zero
			if( total == 0 )
				break;

			cx = peakX + sumX/total;
			cy = peakY + sumY/total;

			float dx = cx-peakX;
			float dy = cy-peakY;

			peakX = cx; peakY = cy;

			if( Math.abs(dx) < convergenceTol && Math.abs(dy) < convergenceTol ) {
				break;
			}
		}
	}

	/**
	 * Performs a mean-shift search center at the specified coordinates but with negative weights ignored
	 */
	public void searchPositive( float cx , float cy ) {
		peakX = cx; peakY = cy;
		if( radius <= 0 ) { // can turn off refinement by setting radius to zero
			return;
		}
		float offset = -radius + (weights.isOdd()?0:0.5f);

		for( int i = 0; i < maxIterations; i++ ) {
			float total = 0;
			float sumX = 0, sumY = 0;

			int kernelIndex = 0;

			// see if it can use fast interpolation otherwise use the safer technique
			if( interpolate.isInFastBounds(peakX+offset, peakY+offset) &&
					interpolate.isInFastBounds(peakX-offset, peakY-offset)) {
				for( int yy = 0; yy < width; yy++ ) {
					float y = offset+yy;
					for( int xx = 0; xx < width; xx++ ) {
						float x = offset+xx;
						float w = weights.weightIndex(kernelIndex++);
						float weight = w*interpolate.get_fast(peakX+x, peakY+y);
						if( weight > 0f ) {
							total += weight;
							sumX += weight * x;
							sumY += weight * y;
						}
					}
				}
			} else {
				for( int yy = 0; yy < width; yy++ ) {
					float y = offset+yy;
					for( int xx = 0; xx < width; xx++ ) {
						float x = offset+xx;
						float w = weights.weightIndex(kernelIndex++);
						float weight = w*interpolate.get(peakX+x, peakY+y);
						if( weight > 0f ) {
							total += weight;
							sumX += weight * x;
							sumY += weight * y;
						}
					}
				}
			}

			// avoid divided by zero
			if( total == 0 )
				break;

			cx = peakX + sumX/total;
			cy = peakY + sumY/total;

			float dx = cx-peakX;
			float dy = cy-peakY;

			peakX = cx; peakY = cy;

			if( Math.abs(dx) < convergenceTol && Math.abs(dy) < convergenceTol ) {
				break;
			}
		}
	}

	public float getPeakX() {
		return peakX;
	}

	public float getPeakY() {
		return peakY;
	}

	public int getRadius() {
		return radius;
	}

	public InterpolatePixelS getInterpolate() {
		return interpolate;
	}

	public void setInterpolate(InterpolatePixelS interpolate) {
		this.interpolate = interpolate;
	}

	public boolean isOdd() {
		return odd;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy