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.feature.local.list; 031 032import java.io.BufferedInputStream; 033import java.io.DataInput; 034import java.io.DataOutput; 035import java.io.File; 036import java.io.IOException; 037import java.io.InputStream; 038import java.io.PrintWriter; 039import java.lang.reflect.Array; 040import java.util.ArrayList; 041import java.util.Collection; 042import java.util.Collections; 043 044import org.openimaj.data.RandomData; 045import org.openimaj.feature.local.LocalFeature; 046import org.openimaj.feature.local.LocationFilter; 047import org.openimaj.io.IOUtils; 048 049/** 050 * An in-memory list of local features. 051 * 052 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 053 * 054 * @param <T> 055 * the type of local feature 056 */ 057public class MemoryLocalFeatureList<T extends LocalFeature<?, ?>> extends ArrayList<T> implements LocalFeatureList<T> { 058 private static final long serialVersionUID = 1L; 059 060 protected int cached_veclen = -1; 061 062 /** 063 * Construct an empty feature list 064 */ 065 public MemoryLocalFeatureList() { 066 } 067 068 /** 069 * Construct an empty list with the given feature-vector length. 070 * 071 * @param veclen 072 * the expected length of the feature vectors of each local 073 * feature. 074 */ 075 public MemoryLocalFeatureList(int veclen) { 076 super(); 077 this.cached_veclen = veclen; 078 } 079 080 /** 081 * Construct a local feature list from the given collection of local 082 * features. 083 * 084 * @param c 085 * Collection of local feature to add to the list instance. 086 */ 087 public MemoryLocalFeatureList(Collection<? extends T> c) { 088 super(c); 089 if (size() > 0) 090 cached_veclen = this.get(0).getFeatureVector().length(); 091 } 092 093 /** 094 * Construct an empty list with the given feature-vector length and 095 * pre-allocate the underlying array with space for initialCapacity local 096 * features. The list will automatically grow once initialCapacity is 097 * reached. 098 * 099 * @param veclen 100 * the expected length of the feature vectors of each local 101 * feature. 102 * @param initialCapacity 103 * the initial capacity of the list. 104 */ 105 public MemoryLocalFeatureList(int veclen, int initialCapacity) { 106 super(initialCapacity); 107 this.cached_veclen = veclen; 108 } 109 110 /** 111 * Create a MemoryLocalFeatureList by reading all the local features from 112 * the specified file. 113 * 114 * @param <T> 115 * the type of local feature 116 * @param keypointFile 117 * the file from which to read the features 118 * @param clz 119 * the class of local feature 120 * @return a new MemoryLocalFeatureList populated with features from the 121 * file 122 * @throws IOException 123 * if an error occurs reading the file 124 */ 125 public static <T extends LocalFeature<?, ?>> MemoryLocalFeatureList<T> read(File keypointFile, Class<T> clz) 126 throws IOException 127 { 128 final boolean isBinary = IOUtils.isBinary(keypointFile, LocalFeatureList.BINARY_HEADER); 129 final MemoryLocalFeatureList<T> list = new MemoryLocalFeatureList<T>(); 130 131 if (isBinary) { 132 LocalFeatureListUtils.readBinary(keypointFile, list, clz); 133 } else { 134 LocalFeatureListUtils.readASCII(keypointFile, list, clz); 135 } 136 137 return list; 138 } 139 140 /** 141 * Create a MemoryLocalFeatureList by reading all the local features from 142 * the specified stream. 143 * 144 * @param <T> 145 * the type of local feature 146 * @param stream 147 * the input stream from which to read the features 148 * @param clz 149 * the class of local feature 150 * @return a new MemoryLocalFeatureList populated with features from the 151 * file 152 * @throws IOException 153 * if an error occurs reading the file 154 */ 155 public static <T extends LocalFeature<?, ?>> MemoryLocalFeatureList<T> read(InputStream stream, Class<T> clz) 156 throws IOException 157 { 158 return read(new BufferedInputStream(stream), clz); 159 } 160 161 /** 162 * Create a MemoryLocalFeatureList by reading all the local features from 163 * the specified stream. 164 * 165 * @param <T> 166 * the type of local feature 167 * @param stream 168 * the input stream from which to read the features 169 * @param clz 170 * the class of local feature 171 * @return a new MemoryLocalFeatureList populated with features from the 172 * file 173 * @throws IOException 174 * if an error occurs reading the file 175 */ 176 public static <T extends LocalFeature<?, ?>> MemoryLocalFeatureList<T> read(BufferedInputStream stream, Class<T> clz) 177 throws IOException 178 { 179 final boolean isBinary = IOUtils.isBinary(stream, LocalFeatureList.BINARY_HEADER); 180 final MemoryLocalFeatureList<T> list = new MemoryLocalFeatureList<T>(); 181 182 if (isBinary) { 183 LocalFeatureListUtils.readBinary(stream, list, clz); 184 } else { 185 LocalFeatureListUtils.readASCII(stream, list, clz); 186 } 187 188 return list; 189 } 190 191 /** 192 * Create a MemoryLocalFeatureList by reading all the local features from 193 * the specified {@link DataInput}. Reading of the header is skipped, and it 194 * is assumed that the data is in binary format. 195 * 196 * @param <T> 197 * the type of local feature 198 * @param in 199 * the data input from which to read the features 200 * @param clz 201 * the class of local feature 202 * @return a new MemoryLocalFeatureList populated with features from the 203 * file 204 * @throws IOException 205 * if an error occurs reading the file 206 */ 207 public static <T extends LocalFeature<?, ?>> MemoryLocalFeatureList<T> readNoHeader(DataInput in, Class<T> clz) 208 throws IOException 209 { 210 final MemoryLocalFeatureList<T> list = new MemoryLocalFeatureList<T>(); 211 212 LocalFeatureListUtils.readBinary(in, list, clz); 213 214 return list; 215 } 216 217 @SuppressWarnings("unchecked") 218 @Override 219 public <Q> Q[] asDataArray(Q[] a) { 220 if (a.length < size()) { 221 System.out.println(a.getClass()); 222 a = (Q[]) Array.newInstance(a.getClass().getComponentType(), size()); 223 } 224 225 int i = 0; 226 for (final T t : this) { 227 a[i++] = (Q) t.getFeatureVector().getVector(); 228 } 229 230 return a; 231 } 232 233 @Override 234 public MemoryLocalFeatureList<T> randomSubList(int nelem) { 235 MemoryLocalFeatureList<T> kl; 236 237 if (nelem > size()) { 238 kl = new MemoryLocalFeatureList<T>(this); 239 Collections.shuffle(kl); 240 } else { 241 final int[] rnds = RandomData.getUniqueRandomInts(nelem, 0, this.size()); 242 kl = new MemoryLocalFeatureList<T>(cached_veclen); 243 244 for (final int idx : rnds) 245 kl.add(this.get(idx)); 246 } 247 248 return kl; 249 } 250 251 @Override 252 public void writeBinary(DataOutput out) throws IOException { 253 resetVecLength(); 254 LocalFeatureListUtils.writeBinary(out, this); 255 } 256 257 @Override 258 public void writeASCII(PrintWriter out) throws IOException { 259 resetVecLength(); 260 LocalFeatureListUtils.writeASCII(out, this); 261 } 262 263 @Override 264 public byte[] binaryHeader() { 265 return LocalFeatureList.BINARY_HEADER; 266 } 267 268 @Override 269 public String asciiHeader() { 270 return ""; 271 } 272 273 /* 274 * @see org.openimaj.feature.local.list.LocalFeatureList#vecLength() 275 */ 276 @Override 277 public int vecLength() { 278 resetVecLength(); 279 280 if (cached_veclen == -1) { 281 if (size() > 0) { 282 cached_veclen = get(0).getFeatureVector().length(); 283 } 284 } 285 return cached_veclen; 286 } 287 288 /** 289 * Reset the internal feature vector length to the length of the first 290 * feature. You must call this if you change the length of the features 291 * within the list. 292 */ 293 public void resetVecLength() { 294 if (size() > 0) { 295 cached_veclen = get(0).getFeatureVector().length(); 296 } 297 } 298 299 /** 300 * Create a new list by applying a {@link LocationFilter} to all the 301 * elements of this list. Only items which are accepted by the filter will 302 * be added to the new list. 303 * 304 * @param locationFilter 305 * the location filter 306 * @return a filtered list 307 */ 308 public MemoryLocalFeatureList<T> filter(LocationFilter locationFilter) { 309 final MemoryLocalFeatureList<T> newlist = new MemoryLocalFeatureList<T>(); 310 for (final T t : this) { 311 if (locationFilter.accept(t.getLocation())) 312 newlist.add(t); 313 } 314 return newlist; 315 } 316 317 @Override 318 public MemoryLocalFeatureList<T> subList(int fromIndex, int toIndex) { 319 return new MemoryLocalFeatureList<T>(super.subList(fromIndex, toIndex)); 320 } 321}