如何将gif拆分为png文件?

时间:2020-12-26 16:33:10

标签: java

some weird image error 我正在尝试将 gif 分割为 png 图像,但只有第一张图像很好,其余部分出现了一些颜色错误...

(我不使用任何库,只使用 java 1.8)

public class GifSplitter {
    
    public static void main(String[] args) {
        try {
            splitGif(new File(FCFinder.getOS().getMc() + File.separator + "test.gif"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void splitGif(File file) throws IOException {
        ImageReader reader = ImageIO.getImageReadersBySuffix("gif").next();
        reader.setInput(ImageIO.createImageInputStream(new FileInputStream(file)), false);
        BufferedImage lastImage = reader.read(0);
        ImageIO.write(lastImage, "PNG", new File(0 + ".png"));

        for (int i = 1; i < reader.getNumImages(true); i++) {
            BufferedImage readImage = reader.read(i);
            BufferedImage image = new BufferedImage(readImage.getWidth(), readImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
            
            ImageIO.write(image, "PNG", new File(i + ".png"));
        }
    }
    
}

3 个答案:

答案 0 :(得分:3)

看起来您正在循环中创建一个空图像并将其写入文件。尝试对您的代码进行这个小改动:

public class GifSplitter {

public static void main(String[] args) {
    try {
        splitGif(new File(FCFinder.getOS().getMc() + File.separator + "test.gif"));
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public static void splitGif(File file) throws IOException {
    ImageReader reader = ImageIO.getImageReadersBySuffix("gif").next();
    reader.setInput(ImageIO.createImageInputStream(new FileInputStream(file)), false);
    BufferedImage lastImage = reader.read(0);
    ImageIO.write(lastImage, "PNG", new File(0 + ".png"));

    for (int i = 1; i < reader.getNumImages(true); i++) {
        BufferedImage readImage = reader.read(i);
        ImageIO.write(readImage, "PNG", new File(i + ".png"));
    }
}

}

答案 1 :(得分:2)

这不是错误,很可能是启用了透明度的 GIF 图像,这是一项有助于减小整个图像文件大小的优化功能。

第一个 GIF 帧始终是完整图像,因此您必须对后续帧执行一些额外的后期处理才能获得实际输出。

所以基本上,当你用 reader.read(i) 读取每一帧时,它最初会是这样的:

enter image description here

处理后应该是这样的:

enter image description here

然后您可以将完全转换的帧转换为 PNG。

在此处(第 4 页)阅读有关 GIF 的“处置方法”的更多信息:https://cs.nyu.edu/courses/fall10/V22.0004-002/animatedGifs.pdf

示例代码(后处理部分):

public static final String DISPOSAL_PREVIOUS = "restoreToPrevious";

public static final String DISPOSAL_BACKGROUND = "restoreToBackgroundColor";

public static final String DISPOSAL_NONE = "none";

public static 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) {
        this.image = image;
        this.delay = delay;
        this.disposal = disposal;
        this.width = -1;
        this.height = -1;
    }

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

private static class ImageMetaData {

    int index;

    int delay;

    String disposal;

    int x;

    int y;

    private ImageMetaData(int index, int delay, String disposal, int x, int y) {
        this.index = index;
        this.delay = delay;
        this.disposal = disposal;
        this.x = x;
        this.y = y;
    }
}

private static ImageMetaData extractImageMetaData(ImageReader reader, int frameIndex) {
    try {
        var metadata = reader.getImageMetadata(frameIndex);
        var root = (IIOMetadataNode) metadata.getAsTree("javax_imageio_gif_image_1.0");
        var gce = (IIOMetadataNode) root.getElementsByTagName("GraphicControlExtension").item(0);
        int delay = Integer.parseInt(gce.getAttribute("delayTime"));
        String disposal = gce.getAttribute("disposalMethod");
        int x = 0, y = 0;
        var children = root.getChildNodes();
        for (int nodeIndex = 0; nodeIndex < children.getLength(); nodeIndex++) {
            Node nodeItem = children.item(nodeIndex);
            if ("ImageDescriptor".equalsIgnoreCase(nodeItem.getNodeName())) {
                NamedNodeMap map = nodeItem.getAttributes();
                x = Integer.parseInt(map.getNamedItem("imageLeftPosition").getNodeValue());
                y = Integer.parseInt(map.getNamedItem("imageTopPosition").getNodeValue());
            }
        }
        return new ImageMetaData(frameIndex, delay, disposal, x, y);
    } catch (IOException e) {
        throw new IllegalStateException(e);
    }
}

private static Integer[] extractLogicalScreenSize(ImageReader reader) {
    Integer[] size = null;
    try {
        var metadata = reader.getStreamMetadata();
        if (metadata == null)
            return null;
        var globalRoot = (IIOMetadataNode) metadata.getAsTree(metadata.getNativeMetadataFormatName());
        var globalScreenDescriptor = globalRoot.getElementsByTagName("LogicalScreenDescriptor");
        if (globalScreenDescriptor != null && globalScreenDescriptor.getLength() > 0) {
            IIOMetadataNode screenDescriptor = (IIOMetadataNode) globalScreenDescriptor.item(0);
            if (screenDescriptor != null) {
                size = new Integer[2];
                size[0] = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenWidth"));
                size[1] = Integer.parseInt(screenDescriptor.getAttribute("logicalScreenHeight"));
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return size;
}

private static List<ImageFrame> readGifFrames(File file) throws IOException {

    var frames = new ArrayList<ImageFrame>(2);

    try (var is = ImageIO.createImageInputStream(file)) {

        var it = ImageIO.getImageReadersBySuffix("gif");
        if (!it.hasNext())
            throw new IOException("No supported reader for this format");

        final var reader = it.next();
        reader.setInput(is);

        try {
            final var size = extractLogicalScreenSize(reader);
            int width = size != null ? size[0] : -1;
            int height = size != null ? size[1] : -1;

            BufferedImage master = null;
            Graphics2D baseImage = null;
            int numFrames = reader.getNumImages(true);

            for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
                BufferedImage img;

                try {
                    img = reader.read(frameIndex);
                } catch (IndexOutOfBoundsException e) {
                    break;
                }

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

                ImageMetaData imageMetadata = extractImageMetaData(reader, frameIndex);
                int x = imageMetadata.x;
                int y = imageMetadata.y;

                if (master == null) {
                    master = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
                    baseImage = master.createGraphics();
                    baseImage.setBackground(new java.awt.Color(0, 0, 0, 0));
                }

                baseImage.drawImage(img, x, y, null);

                //copy master image to image frame
                var copy = new BufferedImage(master.getColorModel(), master.copyData(null), master.isAlphaPremultiplied(), null);
                var imageFrame = new ImageFrame(copy, imageMetadata.delay, imageMetadata.disposal, width, height);
                frames.add(imageFrame);

                //Process disposal
                if (DISPOSAL_PREVIOUS.equals(imageMetadata.disposal)) {
                    BufferedImage from = null;
                    //scan frames backwards (from current to first), search for last processed undisposed frame
                    for (int i = frameIndex - 1; i >= 0; i--) {
                        var frame = frames.get(i);
                        //scan previous undisposed frame
                        if (!DISPOSAL_PREVIOUS.equals(frame.getDisposal()) || frameIndex == 0) {
                            from = frame.getImage();
                            break;
                        }
                    }
                    if (from != null) {
                        master = new BufferedImage(from.getColorModel(), from.copyData(null), from.isAlphaPremultiplied(), null);
                        baseImage = master.createGraphics();
                        //clear everything with transparent pixels
                        baseImage.setBackground(new java.awt.Color(0, 0, 0, 0));
                    }
                } else if (DISPOSAL_BACKGROUND.equals(imageMetadata.disposal)) {
                    baseImage.clearRect(x, y, img.getWidth(), img.getHeight());
                }

            } //end for
            return frames;
        } finally {
            reader.dispose();
        }
    }
}

public static void main(String[] args) throws Exception {
    File testGif = new File("/home/test/test.gif");
    List<ImageFrame> frames = readGifFrames(testGif);
    
    for (ImageFrame frame : frames) {
        var frameImage = frame.getImage();
        //convert frameImage to PNG
    }
}

答案 2 :(得分:1)

您可以使用 glide 将 gif 分解为图像。试试这个:

final ArrayList<Bitmap> bitmaps = new ArrayList<>();

 Glide.with(MainActivity.this)
                .asGif()
                .load("url")
                .into(new SimpleTarget<GifDrawable>() {
                    @Override
                    public void onResourceReady(@NonNull GifDrawable resource, @Nullable Transition<? super GifDrawable> transition) {
                        try {
                            resource.start();
                            resource.setLoopCount(100);
                            Object GifState = resource.getConstantState();
                            assert GifState != null;
                            Field frameLoader = GifState.getClass().getDeclaredField("frameLoader");
                            frameLoader.setAccessible(true);
                            Object gifFrameLoader = frameLoader.get(GifState);
                            assert gifFrameLoader != null;
                            Field gifDecoder = gifFrameLoader.getClass().getDeclaredField("gifDecoder");
                            gifDecoder.setAccessible(true);
                            StandardGifDecoder standardGifDecoder = (StandardGifDecoder) gifDecoder.get(gifFrameLoader);
                            for (int i = 0; i < Objects.requireNonNull(standardGifDecoder).getFrameCount(); i++) {
                                standardGifDecoder.advance();
                                bitmaps.add(standardGifDecoder.getNextFrame());
                                Log.e("bitmapcheck", String.valueOf(bitmaps.size()));
                            }
                        } catch (Exception ex) {
                            Log.e("bitmapcheck", ex.getMessage());
                        }

                    }
                });

依赖关系

  implementation 'com.github.bumptech.glide:glide:4.11.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
相关问题