Page 4
Conversiones entre zonas horarias
Page 5
Index
See this article in english  Ver este artículo en español 
Page 6
Collapse or Expand completely a JTree

Retrieve all frames from an image (e.g. animated GIF)

Alexander Hristov

The following class (Java 1.4+) allows you to retrieve all frames from a multiframe image (usually an animated GIF) and operate on them independantly. The class does not handle animation, nor metadata (like looping times), but it is a good starting point if you want to delve into the advanced features of the ImageIO system. It also works with whatever image plugins are installed, so it is not necessarily limited to animated GIFs. LGPL License

Here is what the program does:

In lines 37, we retrieve the default instance of IIORegistry - the place where the different image-handling plugins register themselves. After that (line 38), we request from the registry a list of all providers that can build an ImageInputStream for us. For each of these providers, we ask him if the input type of objects he requires is compatible with the input type we have (an InputStream). If so, then we just pick the first provider that answers positively. Why can't we just use the InputStream? Because image processing plugins require a more versatile stream that is - for example - capable of seeking forward and backward, reading individual bits, etc.

If by line 49 we haven't found any provider capable of creating ImageInputStreams from the InputStream we have, we have no choice but to abort the operation.

The next step is checking which decoder class can handle the information present in the InputStream. I specifically designed the method to handle streams and not just file names not only because it is more flexible, but because relying on extensions for determining the contents of a file is dangerous and error-prone.

So in line 52 we ask the registry to give us a list of all ImageReaderSpis that can decode the information in the stream. The "Can decode" filter is implemented in the static CanDecodeFilter class (lines 73 to 90). It is a callback filter that is called by the getServiceProviders() method for each service provider. In this case, each call supplies a registered ImageReaderSpi. The filter stores the current position of the image stream (line 82), asks the ImageReaderSpi if it can read the stream and afterwards restores the stream position (line 84), just in case.

So using this filter on line 52 we have an iterator that can iterate all capable readers. If there are none (line 54-55), we throw an exception. Otherwise we just pick the first one, tell him to create a reader, initialize the reader with the default parameters for this kind of reader and set its input to our image stream (lines 58-61). From this point its easy : we just ask the reader about the number of frames contained in the image (the true parameter tells that the reader can read the whole stream if necessary in order to get this information), and then proceed to read each of the frames using the read() method.

 

FrameReader.java
 
0001/**
0002 * FrameReader.java - Retrieves all frames from an image
0003 * Copyright (c) 2007 Alexander Hristov .
0004 *
0005 * Licensed under the LGPL License - http://www.gnu.org/licenses/lgpl.txt
0006 * 
0007 * This library is free software; you can redistribute it and/or
0008 * modify it under the terms of the GNU Lesser General Public
0009 * License as published by the Free Software Foundation; either
0010 * version 2.1 of the License, or (at your option) any later version.
0011 *
0012 * This library is distributed in the hope that it will be useful,
0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015 * Lesser General Public License for more details.
0016 *
0017 * You should have received a copy of the GNU Lesser General Public
0018 * License along with this library; if not, write to the Free Software
0019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA  
0020 */
0021
0022import java.awt.image.BufferedImage;
0023import java.io.IOException;
0024import java.io.InputStream;
0025import java.util.Iterator;
0026
0027import javax.imageio.ImageReadParam;
0028import javax.imageio.ImageReader;
0029import javax.imageio.spi.IIORegistry;
0030import javax.imageio.spi.ImageInputStreamSpi;
0031import javax.imageio.spi.ImageReaderSpi;
0032import javax.imageio.spi.ServiceRegistry;
0033import javax.imageio.stream.ImageInputStream;
0034
0035public class FrameReader {
0036  public static BufferedImage[] getAllFrames(InputStream in) throws IOException {
0037    IIORegistry providers = IIORegistry.getDefaultInstance();
0038    Iterator it = providers.getServiceProviders(ImageInputStreamSpi.class,true);
0039    ImageInputStream imgStream = null;
0040    
0041    // Create an ImageInputStream with the first appropriate SPI
0042    while (it.hasNext()) {
0043      ImageInputStreamSpi spi = (ImageInputStreamSpi)it.next();
0044      if (spi.getInputClass().isInstance(in)) {
0045        imgStream = spi.createInputStreamInstance(in);
0046        break;
0047      }
0048    }
0049    if (imgStream == null)
0050      throw new IOException("No appropriate SPI for this input stream ");
0051    
0052    it = providers.getServiceProviders(
0053        ImageReaderSpi.class, new CanDecodeFilter(imgStream),true);
0054    if (!it.hasNext())
0055      throw new IOException("No class can read this image format");
0056    
0057    ImageReaderSpi readerSpi = (ImageReaderSpi)it.next();
0058    ImageReader reader = readerSpi.createReaderInstance();
0059    ImageReadParam param = reader.getDefaultReadParam();
0060    
0061    reader.setInput(imgStream, false, true);
0062    int numFrames = reader.getNumImages(true);
0063    BufferedImage[] frames = new BufferedImage[numFrames];
0064    for (int i = 0; i < numFrames; i++) {
0065      frames[i] = reader.read(i,param);
0066    }
0067    
0068    imgStream.close();
0069    reader.dispose();
0070    return frames;
0071  }
0072  
0073  static class CanDecodeFilter implements ServiceRegistry.Filter {
0074    ImageInputStream stream;
0075    public CanDecodeFilter(ImageInputStream stream) {
0076      this.stream = stream;
0077    }
0078    public boolean filter(Object element) {
0079      try {
0080          ImageReaderSpi spi = (ImageReaderSpi)element;
0081          boolean canDecode = false;
0082          stream.mark();
0083          canDecode = spi.canDecodeInput(stream);
0084          stream.reset();
0085          return canDecode;
0086      } catch (IOException e) {
0087          return false;
0088      }
0089    }
0090  }
0091}
0092
 


Source code



 

Comments

Nov 04, 2007 at 06:34 Sent by Stefan Reiser
Hello, thanks for this useful class! I use it within an applet. Therefore one has to change line 45 from imgStream = spi.createInputStreamInstance(in); into imgStream = spi.createInputStreamInstance(in, false, null); Usage of temporary cache files would bring up security exceptions. best regards S.Reiser
Oct 26, 2007 at 22:04 Sent by lchaconm
Como puedo obtener todos los fotogramas de un video ......

 

Add a Comment

Name (optional)
EMail (optional, will not be displayed)

Text