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.haar;
031
032import java.util.List;
033import java.util.concurrent.ThreadPoolExecutor;
034
035import org.openimaj.image.analysis.algorithm.SummedSqTiltAreaTable;
036import org.openimaj.math.geometry.shape.Rectangle;
037import org.openimaj.util.function.Operation;
038import org.openimaj.util.parallel.GlobalExecutorPool;
039import org.openimaj.util.parallel.Parallel;
040import org.openimaj.util.parallel.Parallel.IntRange;
041
042/**
043 * Multi-threaded version of the {@link Detector}. The search algorithm is
044 * identical, but the image is separated into multiple vertical stripes for each
045 * thread to process independently.
046 * <p>
047 * <strong>Important note:</strong> This detector is NOT thread-safe due to the
048 * fact that {@link StageTreeClassifier}s are not themselves thread-safe. Do not
049 * attempt to use it in a multi-threaded environment!
050 * 
051 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
052 * 
053 */
054public class MultiThreadedDetector extends Detector {
055        private ThreadPoolExecutor threadPool;
056
057        /**
058         * Construct the {@link MultiThreadedDetector} with the given parameters.
059         * 
060         * @param cascade
061         *            the cascade or tree of stages.
062         * @param scaleFactor
063         *            the amount to change between scales (multiplicative)
064         * @param smallStep
065         *            the amount to step when there is a hint of detection
066         * @param bigStep
067         *            the amount to step when there is definitely no detection
068         * @param threadPool
069         *            the thread pool. If <code>null</code> the global pool is used.
070         */
071        public MultiThreadedDetector(StageTreeClassifier cascade, float scaleFactor, int smallStep, int bigStep,
072                        ThreadPoolExecutor threadPool)
073        {
074                super(cascade, scaleFactor, smallStep, bigStep);
075
076                if (threadPool == null)
077                        threadPool = GlobalExecutorPool.getPool();
078
079                this.threadPool = threadPool;
080        }
081
082        /**
083         * Construct the {@link MultiThreadedDetector} with the given tree of stages
084         * and scale factor. The default step sizes are used.
085         * 
086         * @param cascade
087         *            the cascade or tree of stages.
088         * @param scaleFactor
089         *            the amount to change between scales
090         */
091        public MultiThreadedDetector(StageTreeClassifier cascade, float scaleFactor) {
092                this(cascade, scaleFactor, DEFAULT_SMALL_STEP, DEFAULT_BIG_STEP, null);
093        }
094
095        /**
096         * Construct the {@link MultiThreadedDetector} with the given tree of
097         * stages, and the default parameters for step sizes and scale factor.
098         * 
099         * @param cascade
100         *            the cascade or tree of stages.
101         */
102        public MultiThreadedDetector(StageTreeClassifier cascade) {
103                this(cascade, DEFAULT_SCALE_FACTOR, DEFAULT_SMALL_STEP, DEFAULT_BIG_STEP, null);
104        }
105
106        @Override
107        protected void detectAtScale(final SummedSqTiltAreaTable sat, final int startX, final int stopX, final int startY,
108                        final int stopY, final float ystep, final int windowWidth, final int windowHeight,
109                        final List<Rectangle> results)
110        {
111                Parallel.forRange(startY, stopY, 1, new Operation<IntRange>() {
112                        @Override
113                        public void perform(IntRange range) {
114                                for (int iy = range.start; iy < range.stop; iy += range.incr) {
115                                        final int y = Math.round(iy * ystep);
116
117                                        for (int ix = startX, xstep = 0; ix < stopX; ix += xstep) {
118                                                final int x = Math.round(ix * ystep);
119
120                                                final int result = cascade.classify(sat, x, y);
121
122                                                if (result > 0) {
123                                                        synchronized (results) {
124                                                                results.add(new Rectangle(x, y, windowWidth, windowHeight));
125                                                        }
126                                                }
127
128                                                // if there is no hint of detection, then increase the
129                                                // step size
130                                                xstep = result == 0 ? smallStep : bigStep;
131                                        }
132                                }
133                        }
134                }, threadPool);
135        }
136}