Página 4
Conversiones entre zonas horarias
Página 5
Índice
See this article in english  Ver este artículo en español 
Página 6
Expandir o Colapsar por completo un JTree

Obtener todos los fotogramas de una imagen

Alexander Hristov

La siguiente clase (Java 1.4+) permite obtener todos los fotogramas de una imagen múltiple (por ejemplo, un GIF animado) y operar sobre ellas de forma independiente. La clase no gestiona la animación en sí, ni tampoco lee la metainformación contenida en el archivo (con datos por ejemplo acerca de las repeticiones o de los tiempos de visuzalización), pero es un buen punto de partida si se quiere avanzar en las profundidades del sistema ImageIO. La clase además es compatible con los plugins de procesamiento de imágenes que haya instalados en el sistema, de forma que no está necesariamente limitada a imágenes GIF. Licencia LGPL .

Esto es lo que hace el programa:

En la línea 37, obtenemos la instancia predeterminada de IIORegistry - el sitio donde los diferentes plugins de procesamiento de imágenes se dan de alta en Java. A continuación (línea 38), solicitamos al registro una lista de todos los proveedores que son capaces de construirnos un ImageInputStream . Para cada uno de estos proveedores, les preguntamos si el tipo de objetos de entrada de los que son capaces de leer información coinciden con el tipo de flijo que nosotros tenemos (un InputStream). Si ese es el caso, entonces cogemos el primero que sea capaz de hacerlo. ¿Por qué el sistema no puede utilizar InputStream directamente y necesita en cambio ImageInputStream? Porque los procesadores de imágenes necesitan un flujo un poco más flexible y versátil que InputStream, que tiene entre otras capacidades la de acceso aleatorio y lectura de bits individuales.

Si para la línea 49 no hemos encontrado ningún proveedor capaz de crear ImageInputStreams para el tipo de flujo que tenemos, abortamos la lectura con un error.

El siguiente paso es ver qué decodificadores son capaces de leer la información contenida en el flujo. El método fue diseñado para trabajar con flujos y no con archivos no solo por motivos de flexibilidad, sino también porque confiar en la extensión de un archivo como guía sobre lo que contiene es peligroso y con múltiples posibilidades de fallo.

De esta forma, en la línea 52 solicitamos al registro que nos dé una lista de todo los ImageReaderSpis que son capaces de decodificar la información del flujo. El filtrado lo realizamos nosotros mismos mediante la clase estática CanDecodeFilter class (líneas 73 a 90). Esta clase es un filtro de retrollamada al que el método getServiceProviders() llama por cada proveedor ImageReaderSpi registrado. El filtro almacena la posición actual del flujo (línea 82) y pregunta al proveedor si puede decodificar la información del flujo. Posteriormente se recupera la posición del flujo (línea 84)

Utilizando este filtro, en la línea 52 tenemos un iterador que es capaz de recorrer todos los proveedores compatibles. Si no hay ninguno (lineas 54-55), lanzamos una excepción. En caso de que sí los haya, simplemente elegimos el primero de ellos y le pedimos que nos cree un lector (ImageReader), inicializamos el lector con el conjunto de parámetros que sea predeterminado para su tipo y establecemos su origen de datos (lines 58-61). Desde este punto en adelante es fácil : simplemente pedimos al lector que nos diga el número de fotogramas (el parámetrro "true" indica al método getNumImages() que puede escanear todo el flujo si es necesario con el fín de obtener la información), y a continuación procedemos a leer uno a uno todos los fotogramas con el método read().

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
 


Código Fuente



 

Comentarios

04/11/2007 a las 06:34 Enviado por 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
26/10/2007 a las 22:04 Enviado por lchaconm
Como puedo obtener todos los fotogramas de un video ......

 

Añadir Comentario

Nombre (opcional)
EMail (opcional, no se muestra)

Texto