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}