001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.image.objectdetection.hog;
031
032import java.util.ArrayList;
033import java.util.List;
034
035import org.openimaj.image.FImage;
036import org.openimaj.image.objectdetection.AbstractMultiScaleObjectDetector;
037import org.openimaj.math.geometry.shape.Rectangle;
038
039public class HOGDetector extends AbstractMultiScaleObjectDetector<FImage, Rectangle> {
040        protected float scaleFactor = 1.2f;
041        protected HOGClassifier classifier;
042        double threshold = 0.5;
043
044        public HOGDetector(HOGClassifier classifier, float scaleFactor) {
045                this.classifier = classifier;
046                this.scaleFactor = scaleFactor;
047        }
048
049        public HOGDetector(HOGClassifier classifier) {
050                this.classifier = classifier;
051        }
052
053        @Override
054        public List<Rectangle> detect(FImage image) {
055                final List<Rectangle> results = new ArrayList<Rectangle>();
056
057                final int imageWidth = image.getWidth();
058                final int imageHeight = image.getHeight();
059
060                classifier.prepare(image);
061
062                // compute the number of scales to test and the starting factor
063                int nFactors = 0;
064                int startFactor = 0;
065                for (float factor = 1; factor * classifier.width < imageWidth &&
066                                factor * classifier.height < imageHeight; factor *= scaleFactor)
067                {
068                        final float width = factor * classifier.width;
069                        final float height = factor * classifier.height;
070
071                        if (width < minSize || height < minSize) {
072                                startFactor++;
073                        }
074
075                        if (maxSize > 0 && (width > maxSize || height > maxSize)) {
076                                break;
077                        }
078
079                        nFactors++;
080                }
081
082                // run the detection at each scale
083                float factor = (float) Math.pow(scaleFactor, startFactor);
084                for (int scaleStep = startFactor; scaleStep < nFactors; factor *=
085                                scaleFactor, scaleStep++)
086                {
087                        final float ystep = 8 * factor;
088                        final int windowWidth = (int) (factor * classifier.width);
089                        final int windowHeight = (int) (factor * classifier.height);
090
091                        // determine the spatial range, taking into account any ROI.
092                        final int startX = (int) (roi == null ? 0 : Math.max(0, roi.x));
093                        final int startY = (int) (roi == null ? 0 : Math.max(0, roi.y));
094                        final int stopX = Math.round(
095                                        (roi == null ? imageWidth : Math.min(imageWidth, roi.x + roi.width)) - windowWidth);
096                        final int stopY = Math.round((((roi == null ? imageHeight : Math.min(imageHeight, roi.y +
097                                        roi.height)) - windowHeight)));
098
099                        detectAtScale(startX, stopX, startY, stopY, ystep, windowWidth, windowHeight, results);
100                }
101
102                return results;
103        }
104
105        /**
106         * Perform detection at a single scale. Subclasses may override this to
107         * customise the spatial search. The given starting and stopping coordinates
108         * take into account any region of interest set on this detector.
109         * 
110         * @param startX
111         *            the starting x-ordinate
112         * @param stopX
113         *            the stopping x-ordinate
114         * @param startY
115         *            the starting y-ordinate
116         * @param stopY
117         *            the stopping y-ordinate
118         * @param ystep
119         *            the amount to step
120         * @param windowWidth
121         *            the window width at the current scale
122         * @param windowHeight
123         *            the window height at the current scale
124         * @param results
125         *            the list to store detection results in
126         */
127        protected void detectAtScale(final int startX, final int stopX, final int startY,
128                        final int stopY, final float ystep, final int windowWidth, final int windowHeight,
129                        final List<Rectangle> results)
130        {
131                final Rectangle current = new Rectangle();
132
133                for (int iy = startY; iy < stopY; iy += ystep) {
134                        for (int ix = startX; ix < stopX; ix += ystep) {
135                                current.x = ix;
136                                current.y = iy;
137                                current.width = windowWidth;
138                                current.height = windowHeight;
139
140                                if (classifier.classify(current) > threshold) {
141                                        results.add(current.clone());
142                                }
143                        }
144                }
145        }
146}