2012-01-19 14 views
16

Desidero essere in grado di prendere un GIF animato come input, contare i frame (e forse altri metadati) e convertirli ciascuno in uno BufferedImage. Come posso fare questo?Converti ogni frame GIF animato in un'immagine Buffered separata

+0

See: http://stackoverflow.com/questions/777947/creating-animated-gif-with-imageio – beerbajay

+1

Questa libreria può anche aiutare: https://github.com/dragon66/icafe/wiki – dragon66

risposta

10

Se si desidera che tutti i fotogrammi siano della stessa dimensione (per ottimizzare GIF) provare qualcosa di simile:

try { 
    String[] imageatt = new String[]{ 
      "imageLeftPosition", 
      "imageTopPosition", 
      "imageWidth", 
      "imageHeight" 
    };  

    ImageReader reader = (ImageReader)ImageIO.getImageReadersByFormatName("gif").next(); 
    ImageInputStream ciis = ImageIO.createImageInputStream(new File("house2.gif")); 
    reader.setInput(ciis, false); 

    int noi = reader.getNumImages(true); 
    BufferedImage master = null; 

    for (int i = 0; i < noi; i++) { 
     BufferedImage image = reader.read(i); 
     IIOMetadata metadata = reader.getImageMetadata(i); 

     Node tree = metadata.getAsTree("javax_imageio_gif_image_1.0"); 
     NodeList children = tree.getChildNodes(); 

     for (int j = 0; j < children.getLength(); j++) { 
      Node nodeItem = children.item(j); 

      if(nodeItem.getNodeName().equals("ImageDescriptor")){ 
       Map<String, Integer> imageAttr = new HashMap<String, Integer>(); 

       for (int k = 0; k < imageatt.length; k++) { 
        NamedNodeMap attr = nodeItem.getAttributes(); 
        Node attnode = attr.getNamedItem(imageatt[k]); 
        imageAttr.put(imageatt[k], Integer.valueOf(attnode.getNodeValue())); 
       } 
       if(i==0){ 
        master = new BufferedImage(imageAttr.get("imageWidth"), imageAttr.get("imageHeight"), BufferedImage.TYPE_INT_ARGB); 
       } 
       master.getGraphics().drawImage(image, imageAttr.get("imageLeftPosition"), imageAttr.get("imageTopPosition"), null); 
      } 
     } 
     ImageIO.write(master, "GIF", new File(i + ".gif")); 
    } 
} catch (IOException e) { 
    e.printStackTrace(); 
} 
7

destro, non ho mai fatto niente anche un po 'di simile prima, ma un po' di usare Google e giocherellare in Java mi ha fatto questo:

public ArrayList<BufferedImage> getFrames(File gif) throws IOException{ 
    ArrayList<BufferedImage> frames = new ArrayList<BufferedImage>(); 
    ImageReader ir = new GIFImageReader(new GIFImageReaderSpi()); 
    ir.setInput(ImageIO.createImageInputStream(gif)); 
    for(int i = 0; i < ir.getNumImages(true); i++) 
     frames.add(ir.getRawImageType(i).createBufferedImage(ir.getWidth(i), ir.getHeight(i))); 
    return frames; 
} 

Edit: vedere Ansel Zandegran's modification alla mia risposta.

2

Utilizzando c24w's solution, sostituire:

frames.add(ir.getRawImageType(i).createBufferedImage(ir.getWidth(i), ir.getHeight(i))); 

Con:

frames.add(ir.read(i)); 
8

Nessuna delle risposte qui sono corrette e adatto per l'animazione. Ci sono molti problemi in ogni soluzione, quindi ho scritto qualcosa che funziona davvero con tutti i file gif. Ad esempio, questo prende in considerazione la larghezza e l'altezza effettive dell'immagine invece di prendere la larghezza e l'altezza del primo fotogramma assumendo che riempirà l'intera tela, no, sfortunatamente non è così semplice. In secondo luogo, questo non lascia sottaceti trasparenti. In terzo luogo, questo prende in considerazione i metodi di smaltimento. Quarto, questo ti dà ritardi tra i frame (* 10 se vuoi usarlo in Thread.sleep()).

private ImageFrame[] readGif(InputStream stream) throws IOException{ 
    ArrayList<ImageFrame> frames = new ArrayList<ImageFrame>(2); 

    ImageReader reader = (ImageReader) ImageIO.getImageReadersByFormatName("gif").next(); 
    reader.setInput(ImageIO.createImageInputStream(stream)); 

    int lastx = 0; 
    int lasty = 0; 

    int width = -1; 
    int height = -1; 

    IIOMetadata metadata = reader.getStreamMetadata(); 

    Color backgroundColor = null; 

    if(metadata != null) { 
     IIOMetadataNode globalRoot = (IIOMetadataNode) metadata.getAsTree(metadata.getNativeMetadataFormatName()); 

     NodeList globalColorTable = globalRoot.getElementsByTagName("GlobalColorTable"); 
     NodeList globalScreeDescriptor = globalRoot.getElementsByTagName("LogicalScreenDescriptor"); 

     if (globalScreeDescriptor != null && globalScreeDescriptor.getLength() > 0){ 
      IIOMetadataNode screenDescriptor = (IIOMetadataNode) globalScreeDescriptor.item(0); 

      if (screenDescriptor != null){ 
       width = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenWidth")); 
       height = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenHeight")); 
      } 
     } 

     if (globalColorTable != null && globalColorTable.getLength() > 0){ 
      IIOMetadataNode colorTable = (IIOMetadataNode) globalColorTable.item(0); 

      if (colorTable != null) { 
       String bgIndex = colorTable.getAttribute("backgroundColorIndex"); 

       IIOMetadataNode colorEntry = (IIOMetadataNode) colorTable.getFirstChild(); 
       while (colorEntry != null) { 
        if (colorEntry.getAttribute("index").equals(bgIndex)) { 
         int red = Integer.parseInt(colorEntry.getAttribute("red")); 
         int green = Integer.parseInt(colorEntry.getAttribute("green")); 
         int blue = Integer.parseInt(colorEntry.getAttribute("blue")); 

         backgroundColor = new Color(red, green, blue); 
         break; 
        } 

        colorEntry = (IIOMetadataNode) colorEntry.getNextSibling(); 
       } 
      } 
     } 
    } 

    BufferedImage master = null; 
    boolean hasBackround = false; 

    for (int frameIndex = 0;; frameIndex++) { 
     BufferedImage image; 
     try{ 
      image = reader.read(frameIndex); 
     }catch (IndexOutOfBoundsException io){ 
      break; 
     } 

     if (width == -1 || height == -1){ 
      width = image.getWidth(); 
      height = image.getHeight(); 
     } 

     IIOMetadataNode root = (IIOMetadataNode) reader.getImageMetadata(frameIndex).getAsTree("javax_imageio_gif_image_1.0"); 
     IIOMetadataNode gce = (IIOMetadataNode) root.getElementsByTagName("GraphicControlExtension").item(0); 
     NodeList children = root.getChildNodes(); 

     int delay = Integer.valueOf(gce.getAttribute("delayTime")); 

     String disposal = gce.getAttribute("disposalMethod"); 

     if (master == null){ 
      master = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 
      master.createGraphics().setColor(backgroundColor); 
      master.createGraphics().fillRect(0, 0, master.getWidth(), master.getHeight()); 

     hasBackround = image.getWidth() == width && image.getHeight() == height; 

      master.createGraphics().drawImage(image, 0, 0, null); 
     }else{ 
      int x = 0; 
      int y = 0; 

      for (int nodeIndex = 0; nodeIndex < children.getLength(); nodeIndex++){ 
       Node nodeItem = children.item(nodeIndex); 

       if (nodeItem.getNodeName().equals("ImageDescriptor")){ 
        NamedNodeMap map = nodeItem.getAttributes(); 

        x = Integer.valueOf(map.getNamedItem("imageLeftPosition").getNodeValue()); 
        y = Integer.valueOf(map.getNamedItem("imageTopPosition").getNodeValue()); 
       } 
      } 

      if (disposal.equals("restoreToPrevious")){ 
       BufferedImage from = null; 
       for (int i = frameIndex - 1; i >= 0; i--){ 
        if (!frames.get(i).getDisposal().equals("restoreToPrevious") || frameIndex == 0){ 
         from = frames.get(i).getImage(); 
         break; 
        } 
       } 

       { 
        ColorModel model = from.getColorModel(); 
        boolean alpha = from.isAlphaPremultiplied(); 
        WritableRaster raster = from.copyData(null); 
        master = new BufferedImage(model, raster, alpha, null); 
       } 
      }else if (disposal.equals("restoreToBackgroundColor") && backgroundColor != null){ 
       if (!hasBackround || frameIndex > 1){ 
        master.createGraphics().fillRect(lastx, lasty, frames.get(frameIndex - 1).getWidth(), frames.get(frameIndex - 1).getHeight()); 
       } 
      } 
      master.createGraphics().drawImage(image, x, y, null); 

      lastx = x; 
      lasty = y; 
     } 

     { 
      BufferedImage copy; 

      { 
       ColorModel model = master.getColorModel(); 
       boolean alpha = master.isAlphaPremultiplied(); 
       WritableRaster raster = master.copyData(null); 
       copy = new BufferedImage(model, raster, alpha, null); 
      } 
      frames.add(new ImageFrame(copy, delay, disposal, image.getWidth(), image.getHeight())); 
     } 

     master.flush(); 
    } 
    reader.dispose(); 

    return frames.toArray(new ImageFrame[frames.size()]); 
} 

E la classe ImageFrame:

import java.awt.image.BufferedImage; 
public class ImageFrame { 
    private final int delay; 
    private final BufferedImage image; 
    private final String disposal; 
    private final int width, height; 

    public ImageFrame (BufferedImage image, int delay, String disposal, int width, int height){ 
     this.image = image; 
     this.delay = delay; 
     this.disposal = disposal; 
     this.width = width; 
     this.height = height; 
    } 

    public ImageFrame (BufferedImage image){ 
     this.image = image; 
     this.delay = -1; 
     this.disposal = null; 
     this.width = -1; 
     this.height = -1; 
    } 

    public BufferedImage getImage() { 
     return image; 
    } 

    public int getDelay() { 
     return delay; 
    } 

    public String getDisposal() { 
     return disposal; 
    } 

    public int getWidth() { 
     return width; 
    } 

    public int getHeight() { 
      return height; 
    } 
} 
+0

Gli altri metodi hanno prodotto risultati leggermente corrotti, questo ha funzionato perfettamente. – cen

+0

Questo era da alcuni anni fa wow. Ad ogni modo, ho scritto un'implementazione gif completa in javascript. Se hai ancora problemi, posso portarlo via per te ed è garantito che funzioni. Ho notato che il built-in ImageReader per gif ha qualche problema a decodificare i dati reali dei pixel a volte, nulla può risolverli se non per una riscrittura. –

+0

Ho dovuto mettere insieme alcune parti del codice SO per implementare un ridimensionamento e ritaglio per le GIF animate. Ho capito che funziona perfettamente con questo esempio. Per prima cosa ho provato il metodo di Francesco ma i primi fotogrammi di output avevano un po 'di rumore. Getterò la mia soluzione su Github nei prossimi giorni se qualche altra anima persa cerca di fare qualcosa di simile. Java e GIF sono una sorta di selvaggio west. – cen

4

risposta di Alex copre la maggior parte dei casi, ma ha un paio di problemi. Non gestisce correttamente la trasparenza (almeno secondo la convenzione comune) e applica il metodo di eliminazione del frame corrente al frame precedente che non è corretto. Ecco una versione che consente di gestire questi casi in modo corretto:

private ImageFrame[] readGIF(ImageReader reader) throws IOException { 
    ArrayList<ImageFrame> frames = new ArrayList<ImageFrame>(2); 

    int width = -1; 
    int height = -1; 

    IIOMetadata metadata = reader.getStreamMetadata(); 
    if (metadata != null) { 
     IIOMetadataNode globalRoot = (IIOMetadataNode) metadata.getAsTree(metadata.getNativeMetadataFormatName()); 

     NodeList globalScreenDescriptor = globalRoot.getElementsByTagName("LogicalScreenDescriptor"); 

     if (globalScreenDescriptor != null && globalScreenDescriptor.getLength() > 0) { 
      IIOMetadataNode screenDescriptor = (IIOMetadataNode) globalScreenDescriptor.item(0); 

      if (screenDescriptor != null) { 
       width = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenWidth")); 
       height = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenHeight")); 
      } 
     } 
    } 

    BufferedImage master = null; 
    Graphics2D masterGraphics = null; 

    for (int frameIndex = 0;; frameIndex++) { 
     BufferedImage image; 
     try { 
      image = reader.read(frameIndex); 
     } catch (IndexOutOfBoundsException io) { 
      break; 
     } 

     if (width == -1 || height == -1) { 
      width = image.getWidth(); 
      height = image.getHeight(); 
     } 

     IIOMetadataNode root = (IIOMetadataNode) reader.getImageMetadata(frameIndex).getAsTree("javax_imageio_gif_image_1.0"); 
     IIOMetadataNode gce = (IIOMetadataNode) root.getElementsByTagName("GraphicControlExtension").item(0); 
     int delay = Integer.valueOf(gce.getAttribute("delayTime")); 
     String disposal = gce.getAttribute("disposalMethod"); 

     int x = 0; 
     int y = 0; 

     if (master == null) { 
      master = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 
      masterGraphics = master.createGraphics(); 
      masterGraphics.setBackground(new Color(0, 0, 0, 0)); 
     } else { 
      NodeList children = root.getChildNodes(); 
      for (int nodeIndex = 0; nodeIndex < children.getLength(); nodeIndex++) { 
       Node nodeItem = children.item(nodeIndex); 
       if (nodeItem.getNodeName().equals("ImageDescriptor")) { 
        NamedNodeMap map = nodeItem.getAttributes(); 
        x = Integer.valueOf(map.getNamedItem("imageLeftPosition").getNodeValue()); 
        y = Integer.valueOf(map.getNamedItem("imageTopPosition").getNodeValue()); 
       } 
      } 
     } 
     masterGraphics.drawImage(image, x, y, null); 

     BufferedImage copy = new BufferedImage(master.getColorModel(), master.copyData(null), master.isAlphaPremultiplied(), null); 
     frames.add(new ImageFrame(copy, delay, disposal)); 

     if (disposal.equals("restoreToPrevious")) { 
      BufferedImage from = null; 
      for (int i = frameIndex - 1; i >= 0; i--) { 
       if (!frames.get(i).getDisposal().equals("restoreToPrevious") || frameIndex == 0) { 
        from = frames.get(i).getImage(); 
        break; 
       } 
      } 

      master = new BufferedImage(from.getColorModel(), from.copyData(null), from.isAlphaPremultiplied(), null); 
      masterGraphics = master.createGraphics(); 
      masterGraphics.setBackground(new Color(0, 0, 0, 0)); 
     } else if (disposal.equals("restoreToBackgroundColor")) { 
      masterGraphics.clearRect(x, y, image.getWidth(), image.getHeight()); 
     } 
    } 
    reader.dispose(); 

    return frames.toArray(new ImageFrame[frames.size()]); 
} 

private class ImageFrame { 
    private final int delay; 
    private final BufferedImage image; 
    private final String disposal; 

    public ImageFrame(BufferedImage image, int delay, String disposal) { 
     this.image = image; 
     this.delay = delay; 
     this.disposal = disposal; 
    } 

    public BufferedImage getImage() { 
     return image; 
    } 

    public int getDelay() { 
     return delay; 
    } 

    public String getDisposal() { 
     return disposal; 
    } 
} 

V'è una buona descrizione di come animazioni GIF funzionano in this ImageMagick tutorial.

+0

è necessario applicare alcune modifiche a questo codice. Se il ritardo è zero, impostalo su 100 o se è inferiore a 30 ms, quindi moltiplicalo con 10. Questo ha funzionato per me. – Soley

+0

questo soution ha funzionato perfettamente per me – user3896501

+0

Questo funziona diversamente dalla maggior parte degli altri qui, che ha posto un problema in un modo o nell'altro. –

1

ho scritto un decoder immagine GIF per conto mio e rilasciato sotto la licenza Apache 2.0 su GitHub. Puoi scaricarlo qui: https://github.com/DhyanB/Open-Imaging. Esempio di utilizzo:

void example(final byte[] data) throws Exception { 
    final GifImage gif = GifDecoder .read(data); 
    final int width = gif.getWidth(); 
    final int height = gif.getHeight(); 
    final int background = gif.getBackgroundColor(); 
    final int frameCount = gif.getFrameCount(); 
    for (int i = 0; i < frameCount; i++) { 
     final BufferedImage img = gif.getFrame(i); 
     final int delay = gif.getDelay(i); 
     ImageIO.write(img, "png", new File(OUTPATH + "frame_" + i + ".png")); 
    } 
} 

Il decoder supporta GIF87a, GIF89a, l'animazione, la trasparenza e l'interlacciamento. I fotogrammi avranno la larghezza e l'altezza dell'immagine stessa e saranno posizionati sulla posizione corretta sulla tela. Rispetta la trasparenza dei frame e i metodi di smaltimento. Controlla la descrizione del progetto per ulteriori dettagli come la gestione dei colori di sfondo.

Inoltre, il decodificatore non soffre di questo errore ImageIO: ArrayIndexOutOfBoundsException: 4096 while reading gif file.

Sarei felice di ottenere un feedback. Ho provato con una serie di immagini rappresentative, tuttavia, alcuni test sul campo reali sarebbero buoni.

5

Per dividere una GIF animata in separati BufferedImage cornici:

try { 
    ImageReader reader = ImageIO.getImageReadersByFormatName("gif").next(); 
    File input = new File("input.gif"); 
    ImageInputStream stream = ImageIO.createImageInputStream(input); 
    reader.setInput(stream); 

    int count = reader.getNumImages(true); 
    for (int index = 0; index < count; index++) { 
     BufferedImage frame = reader.read(index); 
     // Here you go 
    } 
} catch (IOException ex) { 
    // An I/O problem has occurred 
}