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}