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.model.asm.datasets; 031 032import java.io.BufferedReader; 033import java.io.IOException; 034import java.io.InputStream; 035import java.io.InputStreamReader; 036import java.util.List; 037 038import org.apache.commons.vfs2.FileObject; 039import org.apache.commons.vfs2.FileSystemManager; 040import org.apache.commons.vfs2.VFS; 041import org.openimaj.data.dataset.ListBackedDataset; 042import org.openimaj.data.dataset.VFSListDataset; 043import org.openimaj.image.Image; 044import org.openimaj.io.InputStreamObjectReader; 045import org.openimaj.io.ObjectReader; 046import org.openimaj.math.geometry.point.Point2dImpl; 047import org.openimaj.math.geometry.point.PointList; 048import org.openimaj.math.geometry.point.PointListConnections; 049import org.openimaj.util.pair.IndependentPair; 050 051/** 052 * Utilities for creating with {@link ShapeModelDataset} instances. 053 * 054 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 055 * 056 */ 057public class ShapeModelDatasets 058{ 059 /** 060 * Basic in memory dataset 061 * 062 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 063 * 064 * @param <IMAGE> 065 * type of the images in the collection 066 */ 067 private static class BasicDataset<IMAGE extends Image<?, IMAGE>> 068 extends 069 ListBackedDataset<IndependentPair<PointList, IMAGE>> implements ShapeModelDataset<IMAGE> 070 { 071 private PointListConnections connections; 072 073 public BasicDataset(List<IndependentPair<PointList, IMAGE>> data, PointListConnections connections) { 074 this.data = data; 075 this.connections = connections; 076 } 077 078 @Override 079 public PointListConnections getConnections() { 080 return connections; 081 } 082 083 @Override 084 public List<PointList> getPointLists() { 085 return IndependentPair.getFirst(this); 086 } 087 088 @Override 089 public List<IMAGE> getImages() { 090 return IndependentPair.getSecond(this); 091 } 092 } 093 094 /** 095 * File-backed dataset 096 * 097 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 098 * 099 * @param <IMAGE> 100 * type of the images in the collection 101 */ 102 private abstract static class FileBackedDataset<IMAGE extends Image<?, IMAGE>> 103 extends 104 VFSListDataset<IndependentPair<PointList, IMAGE>> implements ShapeModelDataset<IMAGE> 105 { 106 protected PointListConnections connections; 107 108 public FileBackedDataset(String path, ObjectReader<IndependentPair<PointList, IMAGE>, FileObject> reader, 109 PointListConnections conns) 110 throws IOException 111 { 112 super(path, reader); 113 this.connections = conns; 114 } 115 116 @Override 117 public PointListConnections getConnections() { 118 return connections; 119 } 120 121 @Override 122 public List<PointList> getPointLists() { 123 return IndependentPair.getFirst(this); 124 } 125 126 @Override 127 public List<IMAGE> getImages() { 128 return IndependentPair.getSecond(this); 129 } 130 } 131 132 private static class ASFDataset<IMAGE extends Image<?, IMAGE>> extends FileBackedDataset<IMAGE> { 133 private static class ASFReader<IMAGE extends Image<?, IMAGE>> 134 implements 135 ObjectReader<IndependentPair<PointList, IMAGE>, FileObject> 136 { 137 private static String[] SUPPORTED_IMAGE_EXTS = { "jpg", "jpeg", "bmp", "png" }; 138 139 private InputStreamObjectReader<IMAGE> imReader; 140 141 public ASFReader(InputStreamObjectReader<IMAGE> reader) { 142 this.imReader = reader; 143 } 144 145 @Override 146 public IndependentPair<PointList, IMAGE> read(FileObject source) throws IOException { 147 final PointList pl = new PointList(); 148 BufferedReader br = null; 149 150 try { 151 br = new BufferedReader(new InputStreamReader(source.getContent().getInputStream())); 152 153 String line; 154 while ((line = br.readLine()) != null) { 155 if (!line.startsWith("#")) { 156 final String[] parts = line.split("\\s+"); 157 158 if (parts.length < 7) 159 continue; 160 161 final float x = Float.parseFloat(parts[2].trim()); 162 final float y = Float.parseFloat(parts[3].trim()); 163 164 pl.points.add(new Point2dImpl(x, y)); 165 } 166 } 167 } finally { 168 if (br != null) 169 try { 170 br.close(); 171 } catch (final IOException e) { 172 // ignore 173 } 174 } 175 176 IMAGE image = null; 177 if (imReader != null) { 178 for (final String ext : SUPPORTED_IMAGE_EXTS) { 179 String name = source.getName().getBaseName(); 180 name = name.substring(0, name.lastIndexOf(".") + 1) + ext; 181 final FileObject file = source.getParent().getChild(name); 182 183 if (file != null && file.exists()) { 184 InputStream imstream = null; 185 try { 186 imstream = file.getContent().getInputStream(); 187 image = imReader.read(imstream); 188 break; 189 } catch (final IOException e) { 190 // ignore 191 } finally { 192 if (imstream != null) { 193 try { 194 imstream.close(); 195 } catch (final IOException e) { 196 // ignore 197 } 198 } 199 } 200 } 201 } 202 } 203 204 if (image != null) 205 pl.scaleXY(image.getWidth(), image.getHeight()); 206 207 return new IndependentPair<PointList, IMAGE>(pl, image); 208 } 209 210 @Override 211 public boolean canRead(FileObject source, String name) { 212 return name.endsWith(".asf"); 213 } 214 } 215 216 public ASFDataset(String path, InputStreamObjectReader<IMAGE> reader) throws IOException { 217 super(path, new ASFReader<IMAGE>(reader), null); 218 readConnections(); 219 } 220 221 void readConnections() throws IOException 222 { 223 connections = new PointListConnections(); 224 final FileObject firstASF = this.getFileObject(0); 225 BufferedReader br = null; 226 227 try { 228 br = new BufferedReader(new InputStreamReader(firstASF.getContent().getInputStream())); 229 230 String line; 231 while ((line = br.readLine()) != null) { 232 if (!line.startsWith("#")) { 233 final String[] parts = line.split("\\s+"); 234 235 if (parts.length < 7) 236 continue; 237 238 final int from = Integer.parseInt(parts[4].trim()); 239 final int to = Integer.parseInt(parts[6].trim()); 240 241 connections.addConnection(from, to); 242 } 243 } 244 } finally { 245 if (br != null) { 246 try { 247 br.close(); 248 } catch (final IOException e) { 249 // ignore 250 } 251 ; 252 } 253 } 254 } 255 } 256 257 private static class PTSDataset<IMAGE extends Image<?, IMAGE>> extends FileBackedDataset<IMAGE> { 258 private static class PTSReader<IMAGE extends Image<?, IMAGE>> 259 implements 260 ObjectReader<IndependentPair<PointList, IMAGE>, FileObject> 261 { 262 private static String[] SUPPORTED_IMAGE_EXTS = { "jpg", "jpeg", "bmp", "png" }; 263 264 private InputStreamObjectReader<IMAGE> imReader; 265 private FileObject ptsPath; 266 private FileObject imgsPath; 267 268 public PTSReader(InputStreamObjectReader<IMAGE> imReader, String ptsPath, String imgsPath) throws IOException 269 { 270 this.imReader = imReader; 271 272 final FileSystemManager fsManager = VFS.getManager(); 273 274 this.ptsPath = fsManager.resolveFile(ptsPath); 275 this.imgsPath = fsManager.resolveFile(imgsPath); 276 } 277 278 @Override 279 public IndependentPair<PointList, IMAGE> read(FileObject source) throws IOException { 280 final PointList pl = new PointList(); 281 BufferedReader br = null; 282 283 try { 284 br = new BufferedReader(new InputStreamReader(source.getContent().getInputStream())); 285 br.readLine(); 286 br.readLine(); 287 br.readLine(); 288 289 String line; 290 while ((line = br.readLine()) != null) { 291 if (!line.startsWith("}") && line.trim().length() > 0) { 292 final String[] parts = line.split("\\s+"); 293 294 final float x = Float.parseFloat(parts[0].trim()); 295 final float y = Float.parseFloat(parts[1].trim()); 296 297 pl.points.add(new Point2dImpl(x, y)); 298 } 299 } 300 } finally { 301 if (br != null) 302 try { 303 br.close(); 304 } catch (final IOException e) { 305 } 306 } 307 308 IMAGE image = null; 309 if (this.imReader != null) { 310 final String relPath = ptsPath.getName().getRelativeName(source.getName()); 311 for (final String ext : SUPPORTED_IMAGE_EXTS) { 312 final String imRelPath = relPath.substring(0, relPath.lastIndexOf(".") + 1) + ext; 313 final FileObject imgPath = imgsPath.resolveFile(imRelPath); 314 315 if (imgPath.exists()) { 316 InputStream imstream = null; 317 try { 318 imstream = imgPath.getContent().getInputStream(); 319 image = imReader.read(imstream); 320 break; 321 } catch (final IOException e) { 322 // ignore 323 } finally { 324 if (imstream != null) { 325 try { 326 imstream.close(); 327 } catch (final IOException e) { 328 // ignore 329 } 330 } 331 } 332 break; 333 } 334 } 335 } 336 337 return IndependentPair.pair(pl, image); 338 } 339 340 @Override 341 public boolean canRead(FileObject source, String name) { 342 return name.endsWith(".pts") && !name.equals("dummy.pts"); 343 } 344 } 345 346 public PTSDataset(String imgsPath, String ptsPath, String modelPath, InputStreamObjectReader<IMAGE> reader) 347 throws IOException 348 { 349 super(ptsPath, new PTSReader<IMAGE>(reader, ptsPath, imgsPath), null); 350 readConnections(modelPath); 351 } 352 353 void readConnections(String path) 354 throws IOException 355 { 356 BufferedReader br = null; 357 try { 358 final FileSystemManager fsManager = VFS.getManager(); 359 360 br = new BufferedReader(new InputStreamReader(fsManager.resolveFile(path).getContent().getInputStream())); 361 this.connections = new PointListConnections(); 362 363 String line; 364 while ((line = br.readLine()) != null) { 365 if (!line.trim().startsWith("indices")) 366 continue; 367 368 final String[] data = line.trim().replace("indices(", "").replace(")", "").split(","); 369 final boolean isOpen = (br.readLine().contains("open_boundary")); 370 371 int prev = Integer.parseInt(data[0]); 372 for (int i = 1; i < data.length; i++) { 373 final int next = Integer.parseInt(data[i]); 374 connections.addConnection(prev, next); 375 prev = next; 376 } 377 378 if (!isOpen) { 379 connections.addConnection(Integer.parseInt(data[data.length - 1]), Integer.parseInt(data[0])); 380 } 381 } 382 } finally { 383 try { 384 if (br != null) 385 br.close(); 386 } catch (final IOException e) { 387 } 388 } 389 } 390 391 } 392 393 private ShapeModelDatasets() { 394 } 395 396 /** 397 * Create a dataset with the given data. 398 * 399 * @param data 400 * the image-pointset pairs 401 * @param connections 402 * the connections across the points 403 * @return the dataset 404 */ 405 public static <IMAGE extends Image<?, IMAGE>> ShapeModelDataset<IMAGE> create( 406 List<IndependentPair<PointList, IMAGE>> data, PointListConnections connections) 407 { 408 return new BasicDataset<IMAGE>(data, connections); 409 } 410 411 /** 412 * Load a dataset from ASF format files as used by the IMM dataset. If the 413 * images are present, they will also be loaded (images must have the same 414 * name as the corresponding ASF files, but with a different extension). 415 * 416 * @see IMMFaceDatabase 417 * @see "http://commons.apache.org/proper/commons-vfs/filesystems.html" 418 * @param path 419 * the file system path or uri. See the Apache Commons VFS2 420 * documentation for all the details. 421 * @param reader 422 * the reader with which to load the images 423 * 424 * @return the dataset 425 * @throws IOException 426 * if an error occurs 427 */ 428 public static <IMAGE extends Image<?, IMAGE>> ShapeModelDataset<IMAGE> loadASFDataset(String path, 429 InputStreamObjectReader<IMAGE> reader) throws IOException 430 { 431 return new ASFDataset<IMAGE>(path, reader); 432 } 433 434 /** 435 * Load a dataset from PTS format files as used by Tim Cootes's ASM/AAM 436 * tools. If the images are present, they will also be loaded (images must 437 * have the same name as the corresponding PTS files, but with a different 438 * extension). 439 * 440 * @param ptsDirPath 441 * the directory containing the pts files 442 * @param imgDirPath 443 * the directory containing the images 444 * @param modelFilePath 445 * the path to the model (connections) file 446 * 447 * @see IMMFaceDatabase 448 * @see "http://commons.apache.org/proper/commons-vfs/filesystems.html" 449 * @param reader 450 * the reader with which to load the images 451 * 452 * @return the dataset 453 * @throws IOException 454 * if an error occurs 455 */ 456 public static <IMAGE extends Image<?, IMAGE>> ShapeModelDataset<IMAGE> loadPTSDataset(String ptsDirPath, 457 String imgDirPath, String modelFilePath, 458 InputStreamObjectReader<IMAGE> reader) throws IOException 459 { 460 return new PTSDataset<IMAGE>(imgDirPath, ptsDirPath, modelFilePath, reader); 461 } 462}