001/* Load a sequence of binary images from files.
002
003 @Copyright (c) 1998-2014 The Regents of the University of California.
004 All rights reserved.
005
006 Permission is hereby granted, without written agreement and without
007 license or royalty fees, to use, copy, modify, and distribute this
008 software and its documentation for any purpose, provided that the
009 above copyright notice and the following two paragraphs appear in all
010 copies of this software.
011
012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016 SUCH DAMAGE.
017
018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023 ENHANCEMENTS, OR MODIFICATIONS.
024
025 PT_COPYRIGHT_VERSION 2
026 COPYRIGHTENDKEY
027 */
028package ptolemy.domains.sdf.lib.vq;
029
030import java.io.FileNotFoundException;
031import java.io.IOException;
032import java.io.InputStream;
033import java.net.URL;
034
035import ptolemy.actor.lib.Source;
036import ptolemy.data.IntMatrixToken;
037import ptolemy.data.IntToken;
038import ptolemy.data.StringToken;
039import ptolemy.data.expr.Parameter;
040import ptolemy.data.type.BaseType;
041import ptolemy.kernel.CompositeEntity;
042import ptolemy.kernel.util.IllegalActionException;
043import ptolemy.kernel.util.NameDuplicationException;
044
045///////////////////////////////////////////////////////////////////
046//// ImageSequence
047
048/**
049 Load a sequence of binary images from files, and create a sequence of
050 IntMatrixTokens from them.  The data is assumed to row scanned, starting
051 at the top row.  Each byte of the binary file is assumed to be the
052 greyscale intensity of a single pixel in the image.
053 <p>
054 The files to be loaded are specified as relative URLs from the base URL path.
055 Usually the base path should be set to the root ptolemy classpath.
056 The file names are created by replacing *'s in the filename with consecutive
057 integers (using zero padding).  For example, specifying a URLtemplate of
058 "missa***.qcf" and a starting frame of
059 zero, will create the names:
060 <ul>
061 <li>missa000.qcf
062 <li>missa001.qcf
063 <li>missa002.qcf
064 <li>...
065 </ul>
066 The name manufacturing algorithm is not especially robust, so
067 debug listeners attached to this actor will receive a list of the file names.
068
069 This actor could be greatly expanded to use the Java Advanced Imaging API
070 for loading images.
071
072 @author Steve Neuendorffer
073 @version $Id$
074 @since Ptolemy II 0.2
075 @Pt.ProposedRating Yellow (neuendor)
076 @Pt.AcceptedRating Red
077 */
078public class ImageSequence extends Source {
079    /** Construct an actor with the given container and name.
080     *  @param container The container.
081     *  @param name The name of this actor.
082     *  @exception IllegalActionException If the actor cannot be contained
083     *   by the proposed container.
084     *  @exception NameDuplicationException If the container already has an
085     *   actor with this name.
086     */
087    public ImageSequence(CompositeEntity container, String name)
088            throws IllegalActionException, NameDuplicationException {
089        super(container, name);
090
091        output.setTypeEquals(BaseType.INT_MATRIX);
092
093        imageURLTemplate = new Parameter(this, "imageURLTemplate",
094                new StringToken("ptolemy/domains/sdf/lib/vq"
095                        + "/data/seq/missa/missa***.qcf"));
096        imageColumns = new Parameter(this, "imageColumns", new IntToken("176"));
097        imageRows = new Parameter(this, "imageRows", new IntToken("144"));
098        startFrame = new Parameter(this, "startFrame", new IntToken("0"));
099        endFrame = new Parameter(this, "endFrame", new IntToken("29"));
100    }
101
102    ///////////////////////////////////////////////////////////////////
103    ////                         public variables                  ////
104
105    /** The image filename templates. */
106    public Parameter imageURLTemplate;
107
108    /** The number of columns in each image. */
109    public Parameter imageColumns;
110
111    /** The number of rows in each image. */
112    public Parameter imageRows;
113
114    /** The starting frame number. */
115    public Parameter startFrame;
116
117    /** The ending frame number. */
118    public Parameter endFrame;
119
120    ///////////////////////////////////////////////////////////////////
121    ////                         public methods                    ////
122
123    /** Initialize this actor.
124     *  Read in the image files.
125     *  @exception IllegalActionException If any of the input files could not
126     *  be read.
127     */
128    @Override
129    public void initialize() throws IllegalActionException {
130        super.initialize();
131
132        InputStream source = null;
133
134        String fileRoot = ((StringToken) imageURLTemplate.getToken())
135                .stringValue();
136        _startFrame = ((IntToken) startFrame.getToken()).intValue();
137        _endFrame = ((IntToken) endFrame.getToken()).intValue();
138        _imageColumns = ((IntToken) imageColumns.getToken()).intValue();
139        _imageRows = ((IntToken) imageRows.getToken()).intValue();
140
141        // If we've already loaded all these images, then don't load
142        // them again.
143        if (_images != null) {
144            return;
145        }
146
147        _frameCount = _endFrame - _startFrame + 1;
148        _images = new IntMatrixToken[_frameCount];
149        _frameInts = new int[_imageRows][_imageColumns];
150        _frameBytes = new byte[_imageRows * _imageColumns];
151
152        for (_frameNumber = 0; _frameNumber < _frameCount; _frameNumber++) {
153            try {
154                // Assemble the file name, replacing '*'
155                byte[] arr = fileRoot.getBytes();
156                int i;
157                int j;
158                int n;
159                i = _frameNumber + _startFrame;
160
161                String temporaryFileName = fileRoot;
162                int location = temporaryFileName.lastIndexOf('*');
163
164                while (location >= 0) {
165                    arr[location] = (byte) ('0' + i % 10);
166                    i = i / 10;
167                    temporaryFileName = new String(arr);
168                    location = temporaryFileName.lastIndexOf('*');
169                }
170
171                String fileName = new String(arr);
172                _debug("file = " + fileName + "\n");
173
174                URL dataurl = getClass().getClassLoader().getResource(fileName);
175
176                if (dataurl == null) {
177                    throw new FileNotFoundException("Failed to find '"
178                            + fileName + "' as " + "a resource");
179                }
180
181                source = dataurl.openStream();
182
183                // Load the frame from the file.
184                if (_fullRead(source, _frameBytes) != _imageRows
185                        * _imageColumns) {
186                    throw new IllegalActionException(
187                            "Error reading " + "image file!");
188                }
189
190                // This is necessary to convert from bytes to ints
191                for (i = 0, n = 0; i < _imageRows; i++) {
192                    for (j = 0; j < _imageColumns; j++, n++) {
193                        _frameInts[i][j] = _frameBytes[n] & 255;
194                    }
195                }
196
197                _images[_frameNumber] = new IntMatrixToken(_frameInts);
198            } catch (IllegalActionException ex) {
199                _images = null;
200                throw new IllegalActionException(this, ex,
201                        "Failed to initialize");
202            } catch (Exception ex) {
203                _images = null;
204                throw new IllegalActionException(this, ex,
205                        "Failed to initialize");
206            } finally {
207                if (source != null) {
208                    try {
209                        source.close();
210                    } catch (IOException ex) {
211                        _images = null;
212                        throw new IllegalActionException(this, ex,
213                                "Failed to close source");
214                    }
215                }
216            }
217        }
218
219        _frameNumber = 0;
220    }
221
222    /** Fire this actor.
223     *  Output the next image in the sequence.  If the sequence has no more
224     *  images, then loop back to the first image in the sequence.
225     */
226    @Override
227    public void fire() throws IllegalActionException {
228        super.fire();
229
230        output.send(0, _images[_frameNumber]);
231        _frameNumber++;
232
233        if (_frameNumber >= _frameCount) {
234            _frameNumber = 0;
235        }
236    }
237
238    ///////////////////////////////////////////////////////////////////
239    ////                         private methods                   ////
240    private int _fullRead(InputStream s, byte[] b) throws IOException {
241        int length = 0;
242        int remaining = b.length;
243        int bytesRead = 0;
244
245        while (remaining > 0) {
246            bytesRead = s.read(b, length, remaining);
247
248            if (bytesRead == -1) {
249                throw new IOException("Unexpected EOF:" + s);
250            }
251
252            remaining -= bytesRead;
253            length += bytesRead;
254        }
255
256        return length;
257    }
258
259    ///////////////////////////////////////////////////////////////////
260    ////                         private variables                 ////
261    private int _frameCount;
262
263    private IntMatrixToken[] _images;
264
265    private byte[] _frameBytes;
266
267    private int[][] _frameInts;
268
269    private int _imageColumns;
270
271    private int _imageRows;
272
273    private int _startFrame;
274
275    private int _endFrame;
276
277    private int _frameNumber;
278}