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.pixel.sampling;
031
032import java.util.ArrayList;
033import java.util.Iterator;
034import java.util.List;
035import java.util.NoSuchElementException;
036
037import org.openimaj.math.geometry.shape.Rectangle;
038import org.openimaj.util.iterator.IterableIterator;
039
040/**
041 * A {@link QuadtreeSampler} provides an easy way of extracting sample patches
042 * from an image or other domain in both spatial and scale directions. The
043 * sampling regions returned by the sampler form a quadtree. At the first level
044 * there is a single sample region covering the entire region; at the second
045 * level, there are 4 non-overlapping sample regions, and so on.
046 * 
047 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
048 */
049public class QuadtreeSampler implements Iterable<Rectangle> {
050        Rectangle base;
051        int nLevels;
052
053        /**
054         * Construct the sampler with the given parameters
055         * 
056         * @param r
057         *            the size of the initial sample region on the first level
058         * @param nLevels
059         *            the number of levels to sample over
060         */
061        public QuadtreeSampler(Rectangle r, int nLevels)
062        {
063                this.base = r;
064                this.nLevels = nLevels;
065        }
066
067        /**
068         * Get all the sampling regions grouped by level
069         * 
070         * @return the sampling regions grouped by level
071         */
072        public List<List<Rectangle>> levelRectangles() {
073                final List<List<Rectangle>> rects = new ArrayList<List<Rectangle>>();
074
075                for (final RectangleSampler rs : IterableIterator.in(levelIterator())) {
076                        rects.add(rs.allRectangles());
077                }
078                return rects;
079        }
080
081        /**
082         * Get all the sampling regions
083         * 
084         * @return all the sampling regions
085         */
086        public List<Rectangle> allRectangles() {
087                final List<Rectangle> list = new ArrayList<Rectangle>();
088
089                for (final Rectangle r : this)
090                        list.add(r);
091
092                return list;
093        }
094
095        @Override
096        public Iterator<Rectangle> iterator() {
097                return new Iterator<Rectangle>() {
098                        Iterator<RectangleSampler> levelIter = levelIterator();
099                        Iterator<Rectangle> rectIter = levelIter.hasNext() ? levelIter.next().iterator() : null;
100
101                        @Override
102                        public boolean hasNext() {
103                                if (rectIter != null && rectIter.hasNext())
104                                        return true;
105
106                                return levelIter.hasNext();
107                        }
108
109                        @Override
110                        public Rectangle next() {
111                                if (!rectIter.hasNext()) {
112                                        rectIter = levelIter.next().iterator();
113                                }
114
115                                return rectIter.next();
116                        }
117
118                        @Override
119                        public void remove() {
120                                throw new UnsupportedOperationException("Removal is not supported!");
121                        }
122
123                };
124        }
125
126        /**
127         * Create an iterator over each of the levels in the pyramid. Each level is
128         * represented by a {@link RectangleSampler} that describes the individual
129         * rectangles on a level.
130         * 
131         * @return an iterator over the levels.
132         */
133        public Iterator<RectangleSampler> levelIterator() {
134                return new Iterator<RectangleSampler>() {
135                        int level = 0;
136
137                        @Override
138                        public boolean hasNext() {
139                                return level < nLevels;
140                        }
141
142                        @Override
143                        public RectangleSampler next() {
144                                if (level >= nLevels)
145                                        throw new NoSuchElementException();
146
147                                final float sz = (float) Math.pow(2, level);
148                                final float dx = base.width / sz;
149                                final float dy = base.height / sz;
150
151                                final RectangleSampler r = new RectangleSampler(base, dx, dy, dx, dy);
152                                level++;
153
154                                return r;
155                        }
156
157                        @Override
158                        public void remove() {
159                                throw new UnsupportedOperationException("Removal is not supported!");
160                        }
161                };
162        }
163}