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.training;
031
032import java.util.ArrayList;
033import java.util.Arrays;
034import java.util.Collection;
035import java.util.EnumSet;
036import java.util.List;
037
038import org.openimaj.image.objectdetection.haar.HaarFeature;
039
040/**
041 * Definitions of standard haar-like features.
042 * 
043 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
044 * 
045 */
046public enum HaarFeatureType {
047        /**
048         * Two component feature:
049         * 
050         * <pre>
051         * 0 X
052         * </pre>
053         */
054        X2 {
055                @Override
056                public HaarFeature create(int x, int y, int dx, int dy, int winWidth, int winHeight) {
057                        // haar_x2
058                        if ((x + dx * 2 <= winWidth) && (y + dy <= winHeight)) {
059                                return HaarFeature.create(false,
060                                                x, y, dx * 2, dy, -1,
061                                                x + dx, y, dx, dy, +2);
062                        }
063                        return null;
064                }
065        },
066        /**
067         * Two component feature:
068         * 
069         * <pre>
070         * 0
071         * X
072         * </pre>
073         */
074        Y2 {
075                @Override
076                public HaarFeature create(int x, int y, int dx, int dy, int winWidth, int winHeight) {
077                        // haar_y2
078                        if ((x + dx <= winWidth) && (y + dy * 2 <= winHeight)) {
079                                return HaarFeature.create(false,
080                                                x, y, dx, dy * 2, -1,
081                                                x, y + dy, dx, dy, +2);
082                        }
083                        return null;
084                }
085        },
086        /**
087         * Three component feature:
088         * 
089         * <pre>
090         * 0 X 0
091         * </pre>
092         */
093        X3 {
094                @Override
095                public HaarFeature create(int x, int y, int dx, int dy, int winWidth, int winHeight) {
096                        // haar_x3
097                        if ((x + dx * 3 <= winWidth) && (y + dy <= winHeight)) {
098                                return HaarFeature.create(false,
099                                                x, y, dx * 3, dy, -1,
100                                                x + dx, y, dx, dy, +3);
101                        }
102                        return null;
103                }
104        },
105        /**
106         * Three component feature:
107         * 
108         * <pre>
109         * 0
110         * X
111         * 0
112         * </pre>
113         */
114        Y3 {
115                @Override
116                public HaarFeature create(int x, int y, int dx, int dy, int winWidth, int winHeight) {
117                        // haar_y3
118                        if ((x + dx <= winWidth) && (y + dy * 3 <= winHeight)) {
119                                return HaarFeature.create(false,
120                                                x, y, dx, dy * 3, -1,
121                                                x, y + dy, dx, dy, +3);
122                        }
123                        return null;
124                }
125        },
126        /**
127         * Three component feature:
128         * 
129         * <pre>
130         * 0 X X 0
131         * </pre>
132         */
133        X4 {
134                @Override
135                public HaarFeature create(int x, int y, int dx, int dy, int winWidth, int winHeight) {
136                        // haar_x4
137                        if ((x + dx * 4 <= winWidth) && (y + dy <= winHeight)) {
138                                return HaarFeature.create(false,
139                                                x, y, dx * 4, dy, -1,
140                                                x + dx, y, dx * 2, dy, +2);
141                        }
142                        return null;
143                }
144        },
145        /**
146         * Three component feature:
147         * 
148         * <pre>
149         * 0
150         * X
151         * X
152         * 0
153         * </pre>
154         */
155        Y4 {
156                @Override
157                public HaarFeature create(int x, int y, int dx, int dy, int winWidth, int winHeight) {
158                        // haar_y4
159                        if ((x + dx <= winWidth) && (y + dy * 4 <= winHeight)) {
160                                return HaarFeature.create(false,
161                                                x, y, dx, dy * 4, -1,
162                                                x, y + dy, dx, dy * 2, +2);
163                        }
164                        return null;
165                }
166        },
167        /**
168         * Four component feature:
169         * 
170         * <pre>
171         * X 0
172         * 0 X
173         * </pre>
174         */
175        X2Y2 {
176                @Override
177                public HaarFeature create(int x, int y, int dx, int dy, int winWidth, int winHeight) {
178                        // x2_y2
179                        if ((x + dx * 2 <= winWidth) && (y + dy * 2 <= winHeight)) {
180                                return HaarFeature.create(false,
181                                                x, y, dx * 2, dy * 2, -1,
182                                                x, y, dx, dy, +2,
183                                                x + dx, y + dy, dx, dy, +2);
184                        }
185                        return null;
186                }
187        },
188        /**
189         * Centre-surround feature:
190         * 
191         * <pre>
192         * 0 0 0
193         * 0 X 0
194         * 0 0 0
195         * </pre>
196         */
197        CS {
198                @Override
199                public HaarFeature create(int x, int y, int dx, int dy, int winWidth, int winHeight) {
200                        if ((x + dx * 3 <= winWidth) && (y + dy * 3 <= winHeight)) {
201                                return HaarFeature.create(false,
202                                                x, y, dx * 3, dy * 3, -1,
203                                                x + dx, y + dy, dx, dy, +9);
204                        }
205                        return null;
206                }
207        },
208        /**
209         * Tilted two component feature:
210         * 
211         * <pre>
212         * 0 - -X
213         * </pre>
214         */
215        TX2 {
216                @Override
217                public HaarFeature create(int x, int y, int dx, int dy, int winWidth, int winHeight) {
218                        // tilted haar_x2
219                        if ((x + 2 * dx <= winWidth) && (y + 2 * dx + dy <= winHeight) && (x - dy
220                                        >= 0))
221                        {
222                                return HaarFeature.create(true,
223                                                x, y, dx * 2, dy, -1,
224                                                x, y, dx, dy, +2);
225                        }
226                        return null;
227                }
228        },
229        /**
230         * Tilted two component feature:
231         * 
232         * <pre>
233         * - 0
234         * X -
235         * </pre>
236         */
237        TY2 {
238                @Override
239                public HaarFeature create(int x, int y, int dx, int dy, int winWidth, int winHeight) {
240                        // tilted haar_y2
241                        if ((x + dx <= winWidth) && (y + dx + 2 * dy <= winHeight) && (x - 2 * dy
242                                        >= 0))
243                        {
244                                return HaarFeature.create(true,
245                                                x, y, dx, 2 * dy, -1,
246                                                x, y, dx, dy, +2);
247                        }
248                        return null;
249                }
250        },
251        /**
252         * Tilted three component feature:
253         * 
254         * <pre>
255         * 0 - -
256         *              -X -
257         *              - -0
258         * </pre>
259         */
260        TX3 {
261                @Override
262                public HaarFeature create(int x, int y, int dx, int dy, int winWidth, int winHeight) {
263                        // tilted haar_x3
264                        if ((x + 3 * dx <= winWidth) && (y + 3 * dx + dy <= winHeight) && (x - dy
265                                        >= 0))
266                        {
267                                return HaarFeature.create(true,
268                                                x, y, dx * 3, dy, -1,
269                                                x + dx, y + dx, dx, dy, +3);
270                        }
271                        return null;
272                }
273        },
274        /**
275         * Tilted three component feature:
276         * 
277         * <pre>
278         * - - 0
279         * - X -
280         * 0 - -
281         * </pre>
282         */
283        TY3 {
284                @Override
285                public HaarFeature create(int x, int y, int dx, int dy, int winWidth, int winHeight) {
286                        // tilted haar_y3
287                        if ((x + dx <= winWidth) && (y + dx + 3 * dy <= winHeight) && (x - 3 * dy
288                                        >= 0))
289                        {
290                                return HaarFeature.create(true,
291                                                x, y, dx, 3 * dy, -1,
292                                                x - dy, y + dy, dx, dy, +3);
293                        }
294                        return null;
295                }
296        },
297        /**
298         * Tilted three component feature:
299         * 
300         * <pre>
301         * 0 - - -
302         *              -X - -
303         *              - -X -
304         *              - - -0
305         * </pre>
306         */
307        TX4 {
308                @Override
309                public HaarFeature create(int x, int y, int dx, int dy, int winWidth, int winHeight) {
310                        // tilted haar_x4
311                        if ((x + 4 * dx <= winWidth) && (y + 4 * dx + dy <= winHeight) && (x - dy
312                                        >= 0))
313                        {
314                                return HaarFeature.create(true,
315                                                x, y, dx * 4, dy, -1,
316                                                x + dx, y + dx, dx * 2, dy, +2);
317                        }
318                        return null;
319                }
320        },
321        /**
322         * Tilted three component feature:
323         * 
324         * <pre>
325         * - - - 0
326         * - - X -
327         * - X - - 
328         * 0 - - -
329         * </pre>
330         */
331        TY4 {
332                @Override
333                public HaarFeature create(int x, int y, int dx, int dy, int winWidth, int winHeight) {
334                        // tilted haar_y4
335                        if ((x + dx <= winWidth) && (y + dx + 4 * dy <= winHeight) && (x - 4 * dy
336                                        >= 0))
337                        {
338                                return HaarFeature.create(true,
339                                                x, y, dx, 4 * dy, -1,
340                                                x - dy, y + dy, dx, 2 * dy, +2);
341                        }
342                        return null;
343                }
344        };
345
346        /**
347         * Set of all the features
348         */
349        public static EnumSet<HaarFeatureType> ALL = EnumSet.allOf(HaarFeatureType.class);
350
351        /**
352         * Set of the basic features (non tilted edges & lines + {@link #X2Y2})
353         */
354        public static EnumSet<HaarFeatureType> BASIC = EnumSet.of(X2, Y2, X3, Y3, X2Y2);
355
356        /**
357         * Set of the core features (all but tilted features)
358         */
359        public static EnumSet<HaarFeatureType> CORE = EnumSet.of(X2, Y2, X3, Y3, X2Y2, X4, Y4, CS);
360
361        /**
362         * Create a feature.
363         * 
364         * @param x
365         *            x-location
366         * @param y
367         *            y-location
368         * @param dx
369         *            x-delta
370         * @param dy
371         *            y-delta
372         * @param winWidth
373         *            window width
374         * @param winHeight
375         *            window height
376         * @return the new feature, or null if the parameters are out of range.
377         */
378        public abstract HaarFeature create(int x, int y, int dx, int dy, int winWidth, int winHeight);
379
380        /**
381         * Generate features of the given types for all possible locations and sizes
382         * in the given window bounds.
383         * 
384         * @param winWidth
385         *            window width
386         * @param winHeight
387         *            window height
388         * @param types
389         *            types of feature to generate
390         * @return the generated features
391         */
392        public static List<HaarFeature> generateFeatures(int winWidth, int winHeight, HaarFeatureType... types) {
393                return generateFeatures(winWidth, winHeight, Arrays.asList(types));
394        }
395
396        /**
397         * Generate features of the given types for all possible locations and sizes
398         * in the given window bounds.
399         * 
400         * @param winWidth
401         *            window width
402         * @param winHeight
403         *            window height
404         * @param types
405         *            types of feature to generate
406         * @return the generated features
407         */
408        public static List<HaarFeature> generateFeatures(int winWidth, int winHeight, Collection<HaarFeatureType> types) {
409                final List<HaarFeature> features = new ArrayList<HaarFeature>();
410
411                for (int x = 0; x < winWidth; x++) {
412                        for (int y = 0; y < winHeight; y++) {
413                                for (int dx = 1; dx <= winWidth; dx++) {
414                                        for (int dy = 1; dy <= winHeight; dy++) {
415                                                for (final HaarFeatureType type : types) {
416                                                        final HaarFeature f = type.create(x, y, dx, dy, winWidth, winHeight);
417
418                                                        if (f != null) {
419                                                                features.add(f);
420                                                        }
421                                                }
422                                        }
423                                }
424                        }
425                }
426
427                return features;
428        }
429}