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

org.openimaj.workinprogress.AdaptiveMoGBackgroundEstimator Maven / Gradle / Ivy

/**
 * Copyright (c) 2011, The University of Southampton and the individual contributors.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *   * 	Redistributions of source code must retain the above copyright notice,
 * 	this list of conditions and the following disclaimer.
 *
 *   *	Redistributions in binary form must reproduce the above copyright notice,
 * 	this list of conditions and the following disclaimer in the documentation
 * 	and/or other materials provided with the distribution.
 *
 *   *	Neither the name of the University of Southampton nor the names of its
 * 	contributors may be used to endorse or promote products derived from this
 * 	software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.openimaj.workinprogress;

import java.io.File;
import java.io.IOException;

import org.openimaj.feature.FloatFVComparison;
import org.openimaj.image.DisplayUtilities;
import org.openimaj.image.FImage;
import org.openimaj.image.Image;
import org.openimaj.image.MBFImage;
import org.openimaj.image.processor.SinglebandImageProcessor;
import org.openimaj.util.array.ArrayUtils;
import org.openimaj.video.Video;
import org.openimaj.video.processor.VideoProcessor;
import org.openimaj.video.xuggle.XuggleVideo;

public class AdaptiveMoGBackgroundEstimator & SinglebandImageProcessor.Processable>
extends
VideoProcessor
{
	/**
	 * The mixture means [y][x][gaussian][band]
	 */
	float[][][][] mu;

	/**
	 * The mixture standard deviations (Gaussians are spherical)
	 * [y][x][gaussian]
	 */
	float[][][] sigma;

	/**
	 * The mixture weights [y][x][gaussian]
	 */
	float[][][] weights;

	/**
	 * Number of dimensions
	 */
	int n;

	/**
	 * Number of Gaussians per mixture
	 */
	int K = 3;

	/**
	 * Learning rate
	 */
	float alpha = 0.005f;

	/**
	 * Initial (low) weight for new Gaussians
	 */
	float initialWeight = 0.05f;

	/**
	 * Initial (high) standard deviation for new Gaussians
	 */
	float initialSigma = 30f / 255f;

	/**
	 * Number of standard deviations for a pixel to be considered a match
	 */
	float matchThreshold = 2.5f;

	private float T = 0.7f;

	/**
	 * The segmentation mask
	 */
	private float[][] mask;

	public AdaptiveMoGBackgroundEstimator(Video xv) {
		super(xv);
	}

	private float density(float[] pixel, float[] pkmu, float pksigma) {
		if (pksigma == 0)
			return 0;

		final double norm = 1 / Math.sqrt(Math.pow(2 * Math.PI, n) * pksigma * pksigma);
		double exp = 0;
		for (int i = 0; i < n; i++) {
			final float diff = pixel[i] - pkmu[i];
			exp += diff * diff / (pksigma * pksigma);
		}

		return (float) (Math.exp(-0.5 * exp) * norm);
	}

	void updateModel(float[][][] tImg) {
		final int height = tImg[0].length;
		final int width = tImg[0][0].length;

		if (n != tImg.length || height != mu.length || width != mu[0].length)
			initialiseModel(width, height, tImg.length);

		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {
				// final float[] pixel = tImg[y][x];
				final float[] pixel = new float[n];
				for (int i = 0; i < n; i++)
					pixel[i] = tImg[i][y][x];

				final float[][] means = mu[y][x];
				final float[] stddev = sigma[y][x];
				final float[] weight = weights[y][x];

				int match = -1;
				for (int i = 0; i < K; i++) {
					final double distance = FloatFVComparison.EUCLIDEAN.compare(means[i], pixel);

					if (distance < stddev[i] * matchThreshold) {
						match = i;
						break;
					}
				}

				if (match >= 0) {
					// update matched distribution
					final float p = alpha * density(pixel, means[match], stddev[match]);

					float dot = 0;
					for (int i = 0; i < n; i++) {
						means[match][i] = (1 - p) * means[match][i] + p * pixel[i];

						dot += (pixel[i] - means[match][i]) * (pixel[i] - means[match][i]);
					}

					stddev[match] = (float) Math.sqrt((1 - p) * stddev[match] * stddev[match] + p * dot);
				} else {
					// find least-probable gaussian
					float minProb = density(pixel, means[0], stddev[0]);
					int minProbIdx = 0;
					for (int i = 1; i < K; i++) {
						final float prob = density(pixel, means[i], stddev[i]);
						if (prob < minProb) {
							minProb = prob;
							minProbIdx = i;
						}
					}

					// init new gaussian:
					means[minProbIdx] = pixel.clone();
					stddev[minProbIdx] = initialSigma;
					weight[minProbIdx] = initialWeight;
					match = minProbIdx;
				}

				// update weights
				float weightsum = 0;
				for (int i = 0; i < K; i++) {
					weight[i] = (1 - alpha) * weight[i];
					if (i == match) {
						weight[i] += alpha;
					}
					weightsum += weight[i];
				}

				// renormalise weights
				ArrayUtils.divide(weight, weightsum);

				// compute scores
				final float[] scores = new float[K];
				for (int i = 0; i < K; i++) {
					if (stddev[i] == 0)
						scores[i] = 0;
					else
						scores[i] = weight[i] / stddev[i];
				}

				final int[] indices = ArrayUtils.indexSort(scores);

				float c = 0;
				boolean found = false;
				for (int i = indices.length - 1; i >= 0; i--) {
					c += weight[indices[i]];
					if (match == indices[i]) {
						found = true;
						break;
					}
					if (c > T) {
						break;
					}
				}
				mask[y][x] = found ? 1 : 0;
			}
		}
	}

	/**
	 * Initialise the internal state of the model. This does not need to be
	 * called manually (the first call to {@link #updateModel(Image)} will call
	 * it automatically), however, it is public to allow the model to be reset.
	 *
	 * @param width
	 *            the frame width
	 * @param height
	 *            the frame height
	 * @param numBands
	 *            the number of image bands
	 */
	public void initialiseModel(int width, int height, int numBands) {
		this.n = numBands;
		this.mu = new float[height][width][K][numBands];
		this.sigma = new float[height][width][K];
		this.weights = new float[height][width][K];
		this.mask = new float[height][width];

		// for (int y=0; y proc = new AdaptiveMoGBackgroundEstimator(xv);
		for (final MBFImage img : proc) {
			DisplayUtilities.displayName(img, "video");
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy