Convertir chaque image gif animée en une image BufferedImage séparée

je veux pouvoir prendre un GIF animé comme entrée, compter les cadres (et peut-être d'autres métadonnées), et convertir chacun en BufferedImage. Comment puis-je faire cela?

17
demandé sur Francesco Menzani 2012-01-20 01:52:48

7 réponses

Si vous voulez toutes les images à la même taille (pour l'optimisation de la GIFs) essayez quelque chose comme ceci:

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();
}
13
répondu Runtherisc 2016-08-10 21:32:24

aucune des réponses ici ne sont correctes et appropriées pour l'animation. Il y a beaucoup de problèmes dans chaque solution donc j'ai écrit quelque chose qui fonctionne réellement avec tous les fichiers gif. Par exemple, cela prend en compte la largeur et la hauteur réelles de l'image au lieu de prendre la largeur et la hauteur du premier cadre en supposant qu'il remplira la toile entière, non, malheureusement ce n'est pas si simple. Deuxièmement, ça ne laisse pas de cornichons transparents. Troisièmement, cela tient compte des méthodes d'élimination. Quatrièmement, cela vous donne des délais entre les cadres (*10 si vous voulez l'utiliser dans le Thread.dormir.))(

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()]);
}
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;
    }
}
7
répondu Alex Orzechowski 2015-08-19 12:09:10

droite, je n'ai jamais rien fait, même légèrement comme cela avant, mais un peu de Googling et de violon en Java m'a obtenu ceci:

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: voir modification D'Ansel Zandegran pour ma réponse.

7
répondu c24w 2017-05-23 12:18:17

pour séparer un GIF animé en BufferedImage images:

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
}
7
répondu Francesco Menzani 2015-09-04 17:43:40

la réponse D'Alex couvre la plupart des cas, mais elle a quelques problèmes. Il ne gère pas la transparence correctement (au moins selon la convention commune) et il applique la méthode d'élimination du cadre actuel au cadre précédent qui est incorrect. Voici une version qui traite ces cas correctement:

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;
    }
}

Il y a une bonne description du fonctionnement des animations GIF en ce tutoriel ImageMagick.

4
répondu SteveH 2013-08-25 05:10:58

en utilisant LA solution de c24w, remplacer:

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

Avec:

frames.add(ir.read(i));
2
répondu Ansel Zandegran 2017-05-23 11:55:06

j'ai écrit un décodeur D'image GIF moi-même et je l'ai sorti sous la licence Apache 2.0 sur GitHub. Vous pouvez le télécharger ici:https://github.com/DhyanB/Open-Imaging