View Javadoc

1   /**
2    * Copyright (c) 2011, The University of Southampton and the individual contributors.
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without modification,
6    * are permitted provided that the following conditions are met:
7    *
8    *   * 	Redistributions of source code must retain the above copyright notice,
9    * 	this list of conditions and the following disclaimer.
10   *
11   *   *	Redistributions in binary form must reproduce the above copyright notice,
12   * 	this list of conditions and the following disclaimer in the documentation
13   * 	and/or other materials provided with the distribution.
14   *
15   *   *	Neither the name of the University of Southampton nor the names of its
16   * 	contributors may be used to endorse or promote products derived from this
17   * 	software without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package org.openimaj.image.model.asm.datasets;
31  
32  import java.io.BufferedReader;
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.io.InputStreamReader;
36  import java.util.List;
37  
38  import org.apache.commons.vfs2.FileObject;
39  import org.apache.commons.vfs2.FileSystemManager;
40  import org.apache.commons.vfs2.VFS;
41  import org.openimaj.data.dataset.ListBackedDataset;
42  import org.openimaj.data.dataset.VFSListDataset;
43  import org.openimaj.image.Image;
44  import org.openimaj.io.InputStreamObjectReader;
45  import org.openimaj.io.ObjectReader;
46  import org.openimaj.math.geometry.point.Point2dImpl;
47  import org.openimaj.math.geometry.point.PointList;
48  import org.openimaj.math.geometry.point.PointListConnections;
49  import org.openimaj.util.pair.IndependentPair;
50  
51  /**
52   * Utilities for creating with {@link ShapeModelDataset} instances.
53   * 
54   * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
55   * 
56   */
57  public class ShapeModelDatasets
58  {
59  	/**
60  	 * Basic in memory dataset
61  	 * 
62  	 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
63  	 * 
64  	 * @param <IMAGE>
65  	 *            type of the images in the collection
66  	 */
67  	private static class BasicDataset<IMAGE extends Image<?, IMAGE>>
68  			extends
69  			ListBackedDataset<IndependentPair<PointList, IMAGE>> implements ShapeModelDataset<IMAGE>
70  	{
71  		private PointListConnections connections;
72  
73  		public BasicDataset(List<IndependentPair<PointList, IMAGE>> data, PointListConnections connections) {
74  			this.data = data;
75  			this.connections = connections;
76  		}
77  
78  		@Override
79  		public PointListConnections getConnections() {
80  			return connections;
81  		}
82  
83  		@Override
84  		public List<PointList> getPointLists() {
85  			return IndependentPair.getFirst(this);
86  		}
87  
88  		@Override
89  		public List<IMAGE> getImages() {
90  			return IndependentPair.getSecond(this);
91  		}
92  	}
93  
94  	/**
95  	 * File-backed dataset
96  	 * 
97  	 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
98  	 * 
99  	 * @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 }