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.processing.face.recognition.benchmarking.dataset;
031
032import java.io.BufferedReader;
033import java.io.BufferedWriter;
034import java.io.File;
035import java.io.FileReader;
036import java.io.FileWriter;
037import java.io.IOException;
038import java.util.AbstractList;
039import java.util.ArrayList;
040import java.util.List;
041
042import org.apache.log4j.Logger;
043import org.openimaj.data.dataset.ListBackedDataset;
044import org.openimaj.data.dataset.ListDataset;
045import org.openimaj.data.dataset.MapBackedDataset;
046import org.openimaj.image.FImage;
047import org.openimaj.image.ImageUtilities;
048
049/**
050 * A simple dataset of people and their images, backed by a 
051 * text file with the following format:
052 * <pre>
053 *  personA,/path/to/image.jpg
054 *  personA,/path/to/image1.jpg
055 *  personB,/path/to/image2.jpg
056 *  ...
057 * </pre>
058 * 
059 * The default separator is a comma, but is user controllable.
060 * <p>
061 * In addition to allowing datasets to be read from a file, this
062 * implementation also allows datasets to be created or appended
063 * to through the {@link #add(String, File)} method.
064 * 
065 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
066 *
067 */
068public class TextFileDataset extends MapBackedDataset<String, ListDataset<FImage>, FImage> {
069        private class LazyImageList extends AbstractList<FImage> {
070                List<File> files = new ArrayList<File>();
071                
072                @Override
073                public FImage get(int index) {
074                        File f = files.get(index);
075                        
076                        if (f.isAbsolute()) {
077                                try {
078                                        return ImageUtilities.readF(f);
079                                } catch (IOException e) {
080                                        logger.warn(e);
081                                        return null;
082                                }
083                        } else {
084                                try {
085                                        return ImageUtilities.readF(new File(file.getParentFile(), f.toString()));
086                                } catch (IOException e) {
087                                        logger.warn(e);
088                                        return null;
089                                }
090                        }
091                }
092
093                @Override
094                public int size() {
095                        return files.size();
096                }
097                
098                @Override
099                public String toString() {
100                        return files.toString();
101                }
102        }
103        
104        private static final Logger logger = Logger.getLogger(TextFileDataset.class);
105        private String separator = ",";
106        File file;
107        BufferedWriter writer;
108        
109        /**
110         * Construct from the given file. The default separator
111         * of a comma "," will be used.
112         * 
113         * @param file the file
114         * @throws IOException if an error occurs
115         */
116        public TextFileDataset(File file) throws IOException {
117                this(file, ",");
118        }
119        
120        /**
121         * Construct from the given file, using the given separator.
122         * @param file the file
123         * @param separator the separator
124         * @throws IOException if an error occurs
125         */
126        public TextFileDataset(File file, String separator) throws IOException {
127                this.file = file;
128                this.separator = separator;
129                
130                if (file.exists())
131                        read();
132                else
133                        openWriter();
134        }
135        
136        /* (non-Javadoc)
137         * @see java.lang.Object#finalize()
138         */
139        @Override
140        protected void finalize() throws Throwable {
141                if (writer != null) {
142                        try { writer.close(); } catch (IOException e) {}
143                }
144                
145                super.finalize();
146        }
147
148        private void read() throws IOException {
149                BufferedReader br = null;
150                
151                try {
152                        br = new BufferedReader(new FileReader(file));
153                        
154                        String line;
155                        while ((line = br.readLine()) != null) {
156                                String[] parts = line.split(separator);
157                                
158                                addInternal(parts[0].trim(), new File (parts[1].trim()));
159                        }
160                } finally {
161                        if (br != null) try { br.close(); } catch (IOException e) {}
162                }
163        }
164
165        private void addInternal(String person, File file) {
166                ListBackedDataset<FImage> list = (ListBackedDataset<FImage>) map.get(person);
167                
168                if (list == null) map.put(person, list = new ListBackedDataset<FImage>(new LazyImageList()));
169                ((LazyImageList)list.getList()).files.add(file);
170        }
171        
172        /**
173         * Add an instance to the dataset.  
174         * 
175         * @param person
176         * @param file
177         * @throws IOException
178         */
179        public void add(String person, File file) throws IOException {
180                if (writer == null)
181                        openWriter();
182                
183                writer.write(person+separator+file.getAbsolutePath()+"\n");
184                writer.flush();
185                
186                addInternal(person, file);
187        }
188
189        private void openWriter() throws IOException {
190                try {
191                        writer = new BufferedWriter(new FileWriter(file, true));
192                } catch (IOException e) {
193                        writer = null;
194                        throw e;
195                }
196        }
197        
198        @Override
199        public String toString() {
200                return "Text File Dataset (" + file + ")";
201        }
202}