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}