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.image.Image;
038import org.openimaj.math.geometry.shape.Rectangle;
039
040/**
041 * A {@link RectangleSampler} provides an easy way to generate a sliding window
042 * of rectangle over an image or other domain.
043 * 
044 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
045 * 
046 */
047public class RectangleSampler implements Iterable<Rectangle> {
048        float minx;
049        float maxx;
050        float miny;
051        float maxy;
052        float stepx;
053        float stepy;
054        float width;
055        float height;
056
057        /**
058         * Construct the sampler with the given parameters
059         * 
060         * @param minx
061         *            starting x-ordinate
062         * @param maxx
063         *            finishing x-ordinate
064         * @param miny
065         *            starting y-ordinate
066         * @param maxy
067         *            finishing y-ordinate
068         * @param stepx
069         *            step size in x direction
070         * @param stepy
071         *            step size in y direction
072         * @param width
073         *            width of sample window
074         * @param height
075         *            height of sample window
076         */
077        public RectangleSampler(float minx, float maxx, float miny, float maxy, float stepx, float stepy, float width,
078                        float height)
079        {
080                setBounds(minx, maxx, miny, maxy);
081                this.stepx = stepx;
082                this.stepy = stepy;
083                this.width = width;
084                this.height = height;
085        }
086
087        /**
088         * Construct the sampler with the given parameters
089         * 
090         * @param bounds
091         *            the bounds in which the window must remain; it will start in
092         *            the top-left corner
093         * @param stepx
094         *            step size in x direction
095         * @param stepy
096         *            step size in y direction
097         * @param width
098         *            width of sample window
099         * @param height
100         *            height of sample window
101         */
102        public RectangleSampler(Rectangle bounds, float stepx, float stepy, float width, float height)
103        {
104                setBounds(bounds);
105                this.stepx = stepx;
106                this.stepy = stepy;
107                this.width = width;
108                this.height = height;
109        }
110
111        /**
112         * Construct the sampler with the given parameters
113         * 
114         * @param img
115         *            the image from which to set the sampler bounds (will be set to
116         *            the complete image)
117         * @param stepx
118         *            step size in x direction
119         * @param stepy
120         *            step size in y direction
121         * @param width
122         *            width of sample window
123         * @param height
124         *            height of sample window
125         */
126        public RectangleSampler(Image<?, ?> img, float stepx, float stepy, float width, float height)
127        {
128                setBounds(img);
129                this.stepx = stepx;
130                this.stepy = stepy;
131                this.width = width;
132                this.height = height;
133        }
134
135        /**
136         * Adjust the bounds of the sampler
137         * 
138         * @param minx
139         *            starting x-ordinate
140         * @param maxx
141         *            finishing x-ordinate
142         * @param miny
143         *            starting y-ordinate
144         * @param maxy
145         *            finishing y-ordinate
146         */
147        public void setBounds(float minx, float maxx, float miny, float maxy) {
148                this.minx = minx;
149                this.maxx = maxx;
150                this.miny = miny;
151                this.maxy = maxy;
152        }
153
154        /**
155         * Adjust the bounds of the sampler
156         * 
157         * @param r
158         *            the new bounds rectangle
159         */
160        public void setBounds(Rectangle r) {
161                if (r == null)
162                        return;
163
164                this.minx = r.x;
165                this.maxx = r.x + r.width;
166                this.miny = r.y;
167                this.maxy = r.y + r.height;
168        }
169
170        /**
171         * Adjust the bounds of the sampler
172         * 
173         * @param img
174         *            the image to determine the bounds from
175         */
176        public void setBounds(Image<?, ?> img) {
177                if (img == null)
178                        return;
179
180                setBounds(img.getBounds());
181        }
182
183        /**
184         * Get a list of all the rectangles that can be produced by this sampler
185         * 
186         * @return all the rectangles
187         */
188        public List<Rectangle> allRectangles() {
189                final List<Rectangle> list = new ArrayList<Rectangle>();
190
191                for (final Rectangle r : this)
192                        list.add(r);
193
194                return list;
195        }
196
197        @Override
198        public Iterator<Rectangle> iterator() {
199                return new Iterator<Rectangle>() {
200                        float x = minx;
201                        float y = miny;
202
203                        @Override
204                        public boolean hasNext() {
205                                if (x + width <= maxx && y + height <= maxy)
206                                        return true;
207
208                                return false;
209                        }
210
211                        @Override
212                        public Rectangle next() {
213                                if (y + height > maxy)
214                                        throw new NoSuchElementException();
215
216                                float nextX = x + stepx;
217                                float nextY = y;
218                                if (nextX + width > maxx) {
219                                        nextX = minx;
220                                        nextY += stepy;
221                                }
222
223                                final Rectangle r = new Rectangle(x, y, width, height);
224
225                                x = nextX;
226                                y = nextY;
227
228                                return r;
229                        }
230
231                        @Override
232                        public void remove() {
233                                throw new UnsupportedOperationException("Removal is not supported!");
234                        }
235                };
236        }
237
238        /**
239         * Create an iterator to extract sub-images from an image based on the
240         * rectangles defined by this sampler.
241         * 
242         * @param image
243         *            the image to extract from
244         * @return an iterator over the extracted sub-images.
245         */
246        public <I extends Image<?, I>> Iterator<I> subImageIterator(final I image) {
247                return new Iterator<I>() {
248                        Iterator<Rectangle> inner = iterator();
249
250                        @Override
251                        public boolean hasNext() {
252                                return inner.hasNext();
253                        }
254
255                        @Override
256                        public I next() {
257                                final Rectangle r = inner.next();
258
259                                return image.extractROI(r);
260                        }
261
262                        @Override
263                        public void remove() {
264                                throw new UnsupportedOperationException("Removal is not supported!");
265                        }
266                };
267        }
268}