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.tools.clusterquantiser.samplebatch;
031
032import java.io.BufferedInputStream;
033import java.io.BufferedOutputStream;
034import java.io.DataInput;
035import java.io.DataInputStream;
036import java.io.DataOutput;
037import java.io.DataOutputStream;
038import java.io.File;
039import java.io.FileInputStream;
040import java.io.FileOutputStream;
041import java.io.IOException;
042import java.util.ArrayList;
043import java.util.Arrays;
044import java.util.List;
045
046import org.openimaj.io.IOUtils;
047import org.openimaj.io.ReadWriteableBinary;
048import org.openimaj.tools.clusterquantiser.FileType;
049
050/**
051 * A batch of samples
052 * 
053 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
054 */
055public class SampleBatch implements ReadWriteableBinary {
056        /**
057         * Header for SampleBatch files
058         */
059        public static final byte[] HEADER = "SAMPLEBATCH".getBytes();
060
061        private FileType type;
062        private File sampleSource;
063        private int startIndex;
064        private int endIndex;
065        private int[] relativeIndexList;
066        
067        /**
068         * Default constructor
069         */
070        public SampleBatch(){
071                
072        }
073        /**
074         * Default constructor
075         * @param type
076         * @param sampleSource
077         * @param startIndex
078         * @param endIndex
079         * @param relativeIndexList
080         */
081        public SampleBatch(FileType type, File sampleSource, int startIndex, int endIndex, int[] relativeIndexList){
082                this.endIndex = endIndex;
083                this.startIndex = startIndex;
084                this.type = type;
085                this.sampleSource = sampleSource;
086                this.relativeIndexList = relativeIndexList;
087        }
088        
089        /**
090         * Default constructor
091         * @param type
092         * @param sampleSource
093         * @param startIndex
094         * @param endIndex
095         */
096        public SampleBatch(FileType type, File sampleSource, int startIndex,int endIndex) {
097                this.type = type;
098                this.startIndex = startIndex;
099                this.endIndex = endIndex;
100                this.sampleSource = sampleSource;
101                this.relativeIndexList = null;
102        }
103
104        /**
105         * @return the stored samples
106         * @throws IOException
107         */
108        public byte[][] getStoredSamples() throws IOException{
109                byte [][] f  = null;
110                if(relativeIndexList == null){
111                        f = type.readFeatures(sampleSource);
112                }
113                else
114                {
115                        f = type.readFeatures(sampleSource, relativeIndexList);
116                }
117                return f;
118        }
119        
120        /**
121         * Get the stored samples
122         * @param indices
123         * @return the stored samples
124         * @throws IOException
125         */
126        public byte[][] getStoredSamples(int[] indices) throws IOException{
127                byte [][] f  = null;
128                int[] corrected = new int[indices.length];
129                for(int i = 0; i < corrected.length;i++) corrected[i] = this.relativeIndexList == null ? indices[i] : this.relativeIndexList[indices[i]];
130                f = type.readFeatures(sampleSource, corrected);
131                return f;
132        }
133        
134        /**
135         * Get the stored samples
136         * @param interestedStart
137         * @param interestedEnd
138         * @return the stored samples
139         * @throws IOException
140         */
141        public byte[][] getStoredSamples(int interestedStart, int interestedEnd) throws IOException {
142                int[] interestedList = new int[interestedEnd - interestedStart];
143                int j = 0;
144                for(int i = interestedStart; i < interestedEnd; i++,j++)
145                        interestedList[j] = this.relativeIndexList == null ? j : this.relativeIndexList[j]; 
146                return type.readFeatures(sampleSource, interestedList);
147        }
148        
149        /**
150         * @return the start index of the batch
151         */
152        public int getStartIndex() {
153                return startIndex;
154        }
155        
156        /**
157         * @return the end index of the batch
158         */
159        public int getEndIndex() {
160                return endIndex;
161        }
162        
163        /**
164         * Write batches to a file
165         * @param sampleBatches
166         * @param sampleBatchOut
167         */
168        public static void writeSampleBatches(List<SampleBatch> sampleBatches,File sampleBatchOut) {
169                DataOutputStream dos = null;
170                try{
171                        FileOutputStream fos = new FileOutputStream(sampleBatchOut);
172                        BufferedOutputStream bos = new BufferedOutputStream(fos);
173                        dos = new DataOutputStream(bos);
174                        dos.writeInt(sampleBatches.size());
175                        for(SampleBatch sb : sampleBatches){
176                                IOUtils.writeBinary(bos, sb);
177                                dos.flush();
178                        }
179                        
180                } catch (IOException e) {
181                        e.printStackTrace();
182                }
183                finally{
184                        try {
185                                dos.close();
186                        } catch (IOException e) {
187                                e.printStackTrace();
188                        }
189                }
190        }
191        
192        /**
193         * Read batches from a file
194         * @param sampleBatchOut
195         * @return batches
196         */
197        public static List<SampleBatch> readSampleBatches(File sampleBatchOut) {
198                FileInputStream fis = null;
199                List<SampleBatch> sbl = new ArrayList<SampleBatch>();
200                try{
201                        fis = new FileInputStream(sampleBatchOut);
202                        DataInputStream dis = new DataInputStream(new BufferedInputStream(fis));
203                        int toRead = dis.readInt();
204                        for(int i = 0; i < toRead; i++){
205                                dis.read(new byte[SampleBatch.HEADER.length]);
206                                SampleBatch sb = new SampleBatch();
207                                sb.readBinary(dis);
208                                sbl.add(sb);
209                                System.err.printf("\r%8d / %8d", i, toRead);
210                        }
211                } catch (IOException e) {
212                }
213                finally{
214                        try {
215                                fis.close();
216                        } catch (IOException e) {
217                                
218                        }
219                }
220                System.out.println(" Done!");
221                return sbl;
222        }
223        
224        @Override
225        public boolean equals(Object o){
226                if(!(o instanceof SampleBatch)) return false;
227                SampleBatch sbo = (SampleBatch) o;
228                
229                return  sbo.endIndex == this.endIndex && 
230                                sbo.startIndex == this.startIndex && 
231                                Arrays.equals(sbo.relativeIndexList, this.relativeIndexList) &&
232                                sbo.sampleSource.getAbsolutePath().equals(this.sampleSource.getAbsolutePath()) &&
233                                sbo.type.equals(this.type);
234        }
235        
236        /**
237         * @return list of relative indices
238         */
239        public int[] getRelativeIndexList() {
240                return this.relativeIndexList;
241        }
242        
243        @Override
244        public String toString(){
245                String out = "";
246                out += "|" + this.startIndex + "->" + this.endIndex + "| == " + (this.endIndex - this.startIndex);
247                return out;
248                
249        }
250
251        @Override
252        public void writeBinary(DataOutput out) throws IOException {
253                out.writeInt(type.ordinal());
254                out.writeInt(sampleSource.getAbsolutePath().length());
255                byte[] path = sampleSource.getAbsolutePath().getBytes();
256                out.write(path, 0, path.length);
257                out.writeInt(startIndex);
258                out.writeInt(endIndex);
259                if(relativeIndexList == null)
260                        out.writeInt(0);
261                else
262                {
263                        out.writeInt(relativeIndexList.length);
264                        for(int i = 0; i < relativeIndexList.length; i++){
265                                out.writeInt(relativeIndexList[i]);
266                        }
267                }
268                
269        }
270
271        @Override
272        public void readBinary(DataInput in) throws IOException {
273                type = FileType.values()[in.readInt()];
274                int pathLen = in.readInt();
275                byte[] path = new byte[pathLen];
276                in.readFully(path, 0, pathLen);
277                sampleSource = new File(new String(path));
278                startIndex = in.readInt();
279                endIndex = in.readInt();
280                int nRelativeIndexList = in.readInt();
281                if(nRelativeIndexList == 0){
282                        relativeIndexList  = null;
283                }
284                else{
285                        relativeIndexList = new int[nRelativeIndexList];
286                        for(int i = 0 ; i < nRelativeIndexList; i++){
287                                relativeIndexList[i] = in.readInt();
288                        }
289                }
290        }
291
292        @Override
293        public byte[] binaryHeader() {
294                return SampleBatch.HEADER;
295        }
296}