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

org.openimaj.image.objectdetection.haar.Detector 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.image.objectdetection.haar;

import java.util.ArrayList;
import java.util.List;

import org.openimaj.citation.annotation.Reference;
import org.openimaj.citation.annotation.ReferenceType;
import org.openimaj.image.FImage;
import org.openimaj.image.analysis.algorithm.SummedSqTiltAreaTable;
import org.openimaj.image.objectdetection.AbstractMultiScaleObjectDetector;
import org.openimaj.math.geometry.shape.Rectangle;

/**
 * Basic, single-threaded multi-scale Haar cascade/tree object detector. The
 * detector determines a range of scales to search based on an optional minimum
 * and maximum detection size (if not specified, minimum is the size of the
 * {@link StageTreeClassifier} and maximum is the image size), and a
 * scale-factor which determines the amount to change between scales. At a given
 * scale, the entire image is searched. In order to speed-up detection, if no
 * detection is made for a given (x, y) coordinate, the x-ordinate position is
 * incremented by {@link #bigStep()}, otherwise it is incremented by
 * {@link #smallStep()}.
 * 

* Important note: This detector is NOT thread-safe due to the * fact that {@link StageTreeClassifier}s are not themselves thread-safe. Do not * attempt to use it in a multi-threaded environment! * * @author Jonathon Hare ([email protected]) */ @Reference( type = ReferenceType.Inproceedings, author = { "Viola, P.", "Jones, M." }, title = "Rapid object detection using a boosted cascade of simple features", year = "2001", booktitle = "Computer Vision and Pattern Recognition, 2001. CVPR 2001. Proceedings of the 2001 IEEE Computer Society Conference on", pages = { " I", "511 ", " I", "518 vol.1" }, number = "", volume = "1", customData = { "keywords", " AdaBoost; background regions; boosted simple feature cascade; classifiers; face detection; image processing; image representation; integral image; machine learning; object specific focus-of-attention mechanism; rapid object detection; real-time applications; statistical guarantees; visual object detection; feature extraction; image classification; image representation; learning (artificial intelligence); object detection;", "doi", "10.1109/CVPR.2001.990517", "ISSN", "1063-6919 " }) public class Detector extends AbstractMultiScaleObjectDetector { /** * Default step size to make when there is a hint of detection. */ public static final int DEFAULT_SMALL_STEP = 1; /** * Default step size to make when there is definitely no detection. */ public static final int DEFAULT_BIG_STEP = 2; /** * Default scale factor multiplier. */ public static final float DEFAULT_SCALE_FACTOR = 1.1f; protected StageTreeClassifier cascade; protected float scaleFactor = 1.1f; protected int smallStep = 1; protected int bigStep = 2; /** * Construct the {@link Detector} with the given parameters. * * @param cascade * the cascade or tree of stages. * @param scaleFactor * the amount to change between scales (multiplicative) * @param smallStep * the amount to step when there is a hint of detection * @param bigStep * the amount to step when there is definitely no detection */ public Detector(StageTreeClassifier cascade, float scaleFactor, int smallStep, int bigStep) { super(Math.max(cascade.width, cascade.height), 0); this.cascade = cascade; this.scaleFactor = scaleFactor; this.smallStep = smallStep; this.bigStep = bigStep; } /** * Construct the {@link Detector} with the given tree of stages and scale * factor. The default step sizes are used. * * @param cascade * the cascade or tree of stages. * @param scaleFactor * the amount to change between scales */ public Detector(StageTreeClassifier cascade, float scaleFactor) { this(cascade, scaleFactor, DEFAULT_SMALL_STEP, DEFAULT_BIG_STEP); } /** * Construct the {@link Detector} with the given tree of stages, and the * default parameters for step sizes and scale factor. * * @param cascade * the cascade or tree of stages. */ public Detector(StageTreeClassifier cascade) { this(cascade, DEFAULT_SCALE_FACTOR, DEFAULT_SMALL_STEP, DEFAULT_BIG_STEP); } /** * Perform detection at a single scale. Subclasses may override this to * customise the spatial search. The given starting and stopping coordinates * take into account any region of interest set on this detector. * * @param sat * the summed area table(s) * @param startX * the starting x-ordinate * @param stopX * the stopping x-ordinate * @param startY * the starting y-ordinate * @param stopY * the stopping y-ordinate * @param ystep * the amount to step * @param windowWidth * the window width at the current scale * @param windowHeight * the window height at the current scale * @param results * the list to store detection results in */ protected void detectAtScale(final SummedSqTiltAreaTable sat, final int startX, final int stopX, final int startY, final int stopY, final float ystep, final int windowWidth, final int windowHeight, final List results) { for (int iy = startY; iy < stopY; iy++) { final int y = Math.round(iy * ystep); for (int ix = startX, xstep = 0; ix < stopX; ix += xstep) { final int x = Math.round(ix * ystep); final int result = cascade.classify(sat, x, y); if (result > 0) { results.add(new Rectangle(x, y, windowWidth, windowHeight)); } // if there is no detection, then increase the step size xstep = (result > 0 ? smallStep : bigStep); // TODO: think about what to do if there isn't a detection, but // we're very close to having one based on the ratio of stages // passes to total stages. } } } @Override public List detect(FImage image) { final List results = new ArrayList(); final int imageWidth = image.getWidth(); final int imageHeight = image.getHeight(); final SummedSqTiltAreaTable sat = new SummedSqTiltAreaTable(image, cascade.hasTiltedFeatures); // compute the number of scales to test and the starting factor int nFactors = 0; int startFactor = 0; for (float factor = 1; factor * cascade.width < imageWidth - 10 && factor * cascade.height < imageHeight - 10; factor *= scaleFactor) { final float width = factor * cascade.width; final float height = factor * cascade.height; if (width < minSize || height < minSize) { startFactor++; } if (maxSize > 0 && (width > maxSize || height > maxSize)) { break; } nFactors++; } // run the detection at each scale float factor = (float) Math.pow(scaleFactor, startFactor); for (int scaleStep = startFactor; scaleStep < nFactors; factor *= scaleFactor, scaleStep++) { final float ystep = Math.max(2, factor); final int windowWidth = (int) (factor * cascade.width); final int windowHeight = (int) (factor * cascade.height); // determine the spatial range, taking into account any ROI. final int startX = (int) (roi == null ? 0 : Math.max(0, roi.x)); final int startY = (int) (roi == null ? 0 : Math.max(0, roi.y)); final int stopX = Math.round( (((roi == null ? imageWidth : Math.min(imageWidth, roi.x + roi.width)) - windowWidth)) / ystep); final int stopY = Math.round( (((roi == null ? imageHeight : Math.min(imageHeight, roi.y + roi.height)) - windowHeight)) / ystep); // prepare the cascade for this scale cascade.setScale(factor); detectAtScale(sat, startX, stopX, startY, stopY, ystep, windowWidth, windowHeight, results); } return results; } /** * Get the step size the detector will make if there is any hint of a * detection. This should be smaller than {@link #bigStep()}. * * @return the amount to step on any hint of detection. */ public int smallStep() { return smallStep; } /** * Get the step size the detector will make if there is definitely no * detection. This should be bigger than {@link #smallStep()}. * * @return the amount to step when there is definitely no detection. */ public int bigStep() { return bigStep; } /** * Set the step size the detector will make if there is any hint of a * detection. This should be smaller than {@link #bigStep()}. * * @param smallStep * The amount to step on any hint of detection. */ public void setSmallStep(int smallStep) { this.smallStep = smallStep; } /** * Set the step size the detector will make if there is definitely no * detection. This should be bigger than {@link #smallStep()}. * * @param bigStep * The amount to step when there is definitely no detection. */ public void bigStep(int bigStep) { this.bigStep = bigStep; } /** * Get the scale factor (the amount to change between scales * (multiplicative)). * * @return the scaleFactor */ public float getScaleFactor() { return scaleFactor; } /** * Set the scale factor (the amount to change between scales * (multiplicative)). * * @param scaleFactor * the scale factor to set */ public void setScaleFactor(float scaleFactor) { this.scaleFactor = scaleFactor; } /** * Get the classifier tree or cascade used by this detector. * * @return the classifier tree or cascade. */ public StageTreeClassifier getClassifier() { return cascade; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy