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.annotation.evaluation.datasets; 031 032import java.io.File; 033import java.io.IOException; 034import java.io.InputStream; 035import java.net.URL; 036 037import org.apache.commons.io.FileUtils; 038import org.apache.commons.vfs2.FileObject; 039import org.apache.commons.vfs2.FileSystemException; 040import org.apache.commons.vfs2.FileSystemManager; 041import org.apache.commons.vfs2.VFS; 042import org.openimaj.data.DataUtils; 043import org.openimaj.data.dataset.VFSGroupDataset; 044import org.openimaj.data.dataset.VFSListDataset; 045import org.openimaj.data.identity.Identifiable; 046import org.openimaj.experiment.annotations.DatasetDescription; 047import org.openimaj.image.Image; 048import org.openimaj.image.ImageProvider; 049import org.openimaj.image.ImageUtilities; 050import org.openimaj.io.InputStreamObjectReader; 051import org.openimaj.io.ObjectReader; 052import org.openimaj.math.geometry.point.Point2dImpl; 053import org.openimaj.math.geometry.shape.Polygon; 054import org.openimaj.math.geometry.shape.Rectangle; 055 056import com.jmatio.io.MatFileReader; 057import com.jmatio.types.MLDouble; 058 059/** 060 * The CalTech101 image dataset. Contains 102 classes of image (101 objects + 061 * background), and (for most images) outlines and bounding boxes of the object. 062 * Images are approximately 300x200 pixels in size. 063 * 064 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 065 */ 066@DatasetDescription( 067 name = "CalTech101", 068 description = "Pictures of objects belonging to 101 categories. " + 069 "About 40 to 800 images per category. Most categories have about " + 070 "50 images. The size of each image is roughly 300 x 200 pixels.", 071 creator = "Fei-Fei Li, Marco Andreetto, and Marc 'Aurelio Ranzato", 072 url = "http://www.vision.caltech.edu/Image_Datasets/Caltech101/", 073 downloadUrls = { 074 "http://datasets.openimaj.org/Caltech101/101_ObjectCategories.zip", 075 "http://datasets.openimaj.org/Caltech101/Annotations.zip" 076 }) 077public class Caltech101 { 078 private static final String IMAGES_ZIP = "Caltech101/101_ObjectCategories.zip"; 079 private static final String IMAGES_DOWNLOAD_URL = "http://datasets.openimaj.org/Caltech101/101_ObjectCategories.zip"; 080 private static final String ANNOTATIONS_ZIP = "Caltech101/Annotations.zip"; 081 private static final String ANNOTATIONS_DOWNLOAD_URL = "http://datasets.openimaj.org/Caltech101/Annotations.zip"; 082 083 private Caltech101() { 084 } 085 086 /** 087 * Get a dataset of the Caltech 101 images. If the dataset hasn't been 088 * downloaded, it will be fetched automatically and stored in the OpenIMAJ 089 * data directory. The images in the dataset are grouped by their class. 090 * 091 * @see DataUtils#getDataDirectory() 092 * 093 * @param reader 094 * @return a dataset of images 095 * @throws IOException 096 * if a problem occurs loading the dataset 097 */ 098 public static <IMAGE extends Image<?, IMAGE>> VFSGroupDataset<IMAGE> getImages(InputStreamObjectReader<IMAGE> reader) 099 throws IOException 100 { 101 return new VFSGroupDataset<IMAGE>(downloadAndGetImagePath(), reader); 102 } 103 104 private static String downloadAndGetImagePath() throws IOException { 105 final File dataset = DataUtils.getDataLocation(IMAGES_ZIP); 106 107 if (!(dataset.exists())) { 108 dataset.getParentFile().mkdirs(); 109 FileUtils.copyURLToFile(new URL(IMAGES_DOWNLOAD_URL), dataset); 110 } 111 112 return "zip:file:" + dataset.toString() + "!101_ObjectCategories/"; 113 } 114 115 private static String downloadAndGetAnnotationPath() throws IOException { 116 final File dataset = DataUtils.getDataLocation(ANNOTATIONS_ZIP); 117 118 if (!(dataset.exists())) { 119 dataset.getParentFile().mkdirs(); 120 FileUtils.copyURLToFile(new URL(ANNOTATIONS_DOWNLOAD_URL), dataset); 121 } 122 123 return "zip:file:" + dataset.toString() + "!Annotations/"; 124 } 125 126 /** 127 * A record in the Caltech 101 dataset. Contains the image together with 128 * (optional) metadata on the bounds of the object in the image as well as 129 * the class of object in the image. 130 * 131 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 132 * 133 * @param <IMAGE> 134 * The type of image that is loaded 135 */ 136 public static abstract class Record<IMAGE extends Image<?, IMAGE>> implements Identifiable, ImageProvider<IMAGE> { 137 private Rectangle bounds; 138 private Polygon contour; 139 private String id; 140 private String objectClass; 141 142 protected Record(FileObject image) throws FileSystemException, IOException { 143 final FileSystemManager fsManager = VFS.getManager(); 144 final FileObject imagesBase = fsManager.resolveFile(downloadAndGetImagePath()); 145 final FileObject annotationsBase = fsManager.resolveFile(downloadAndGetAnnotationPath()); 146 147 // get the id 148 id = imagesBase.getName().getRelativeName(image.getName()); 149 150 // the class 151 objectClass = image.getParent().getName().getBaseName(); 152 153 // find the annotation file 154 final String annotationFileName = id.replace("image_", "annotation_").replace(".jpg", ".mat"); 155 final FileObject annotationFile = annotationsBase.resolveFile(annotationFileName); 156 parseAnnotations(annotationFile); 157 } 158 159 private void parseAnnotations(FileObject annotationFile) throws IOException { 160 if (!annotationFile.exists()) { 161 return; 162 } 163 164 final MatFileReader reader = new MatFileReader(annotationFile.getContent().getInputStream()); 165 166 final MLDouble boxes = (MLDouble) reader.getMLArray("box_coord"); 167 this.bounds = new Rectangle( 168 (float) (double) boxes.getReal(2) - 1, 169 (float) (double) boxes.getReal(0) - 1, 170 (float) (boxes.getReal(3) - boxes.getReal(2)) - 1, 171 (float) (boxes.getReal(1) - boxes.getReal(0)) - 1); 172 173 final double[][] contourData = ((MLDouble) reader.getMLArray("obj_contour")).getArray(); 174 this.contour = new Polygon(); 175 for (int i = 0; i < contourData[0].length; i++) { 176 contour.points.add( 177 new Point2dImpl((float) contourData[0][i] + bounds.x - 1, 178 (float) contourData[1][i] + bounds.y - 1) 179 ); 180 } 181 contour.close(); 182 } 183 184 @Override 185 public String getID() { 186 return id; 187 } 188 189 /** 190 * Get the bounds rectangle if it is available 191 * 192 * @return the bounds 193 */ 194 public Rectangle getBounds() { 195 return bounds; 196 } 197 198 /** 199 * Get the object polygon if it is available. 200 * 201 * @return the contour 202 */ 203 public Polygon getContour() { 204 return contour; 205 } 206 207 /** 208 * Get the class of the object depicted in the image. 209 * 210 * @return the class 211 */ 212 public String getObjectClass() { 213 return objectClass; 214 } 215 } 216 217 /** 218 * An {@link ObjectReader} for {@link Record}s. 219 * 220 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 221 * 222 * @param <IMAGE> 223 * Type of image being read 224 */ 225 private static class RecordReader<IMAGE extends Image<?, IMAGE>> implements ObjectReader<Record<IMAGE>, FileObject> { 226 private VFSListDataset.FileObjectISReader<IMAGE> imageReader; 227 228 public RecordReader(InputStreamObjectReader<IMAGE> reader) { 229 this.imageReader = new VFSListDataset.FileObjectISReader<IMAGE>(reader); 230 } 231 232 @Override 233 public Record<IMAGE> read(final FileObject source) throws IOException { 234 return new Record<IMAGE>(source) { 235 236 @Override 237 public IMAGE getImage() { 238 try { 239 return imageReader.read(source); 240 } catch (final IOException e) { 241 throw new RuntimeException(e); 242 } 243 } 244 }; 245 } 246 247 @Override 248 public boolean canRead(FileObject source, String name) { 249 InputStream stream = null; 250 try { 251 stream = source.getContent().getInputStream(); 252 253 return ImageUtilities.FIMAGE_READER.canRead(stream, source.getName().getBaseName()); 254 } catch (final FileSystemException e) { 255 return false; 256 } finally { 257 if (stream != null) { 258 try { 259 stream.close(); 260 } catch (final IOException e) { 261 } 262 } 263 } 264 } 265 } 266 267 /** 268 * Get a dataset of the Caltech 101 images and metadata. If the dataset 269 * hasn't been downloaded, it will be fetched automatically and stored in 270 * the OpenIMAJ data directory. The images in the dataset are grouped by 271 * their class. 272 * 273 * @see DataUtils#getDataDirectory() 274 * 275 * @param reader 276 * a reader for reading images (usually a 277 * {@link ImageUtilities#FIMAGE_READER} or 278 * {@link ImageUtilities#MBFIMAGE_READER}). 279 * @return a dataset of images and metadate 280 * @throws IOException 281 * if a problem occurs loading the dataset 282 */ 283 public static <IMAGE extends Image<?, IMAGE>> VFSGroupDataset<Record<IMAGE>> getData( 284 InputStreamObjectReader<IMAGE> reader) throws IOException 285 { 286 return new VFSGroupDataset<Record<IMAGE>>(downloadAndGetImagePath(), new RecordReader<IMAGE>(reader)); 287 } 288}