【问题标题】:extracting png files from APNG in java在java中从APNG中提取png文件
【发布时间】:2012-10-12 12:47:24
【问题描述】:

我一直在尝试从 APNG 文件中提取所有 png 文件。 我已经寻求帮助,而且没有太多。 我能找到的只是一个开源库 pngj,有了它我就可以得到 APNG 文件的第一帧。

这是我正在使用的代码

public static void mirror(File orig, File dest, boolean overwrite)
         {
    PngReader pngr = FileHelper.createPngReader(orig);
    PngWriter pngw = FileHelper.createPngWriter(dest, pngr.imgInfo,
            overwrite);
    pngw.setFilterType(FilterType.FILTER_CYCLIC); // just to test all
                                                    // filters
    int copyPolicy = ChunkCopyBehaviour.COPY_ALL;
    pngw.copyChunksFirst(pngr, copyPolicy);
    ImageLine lout = new ImageLine(pngw.imgInfo);
    int cols = pngr.imgInfo.cols;
    int channels = pngr.imgInfo.channels;
    int[] line = new int[cols * channels];
    int aux;
    for (int row = 0; row < pngr.imgInfo.rows; row++) {
        ImageLine l1 = pngr.readRow(row);
        line = l1.unpack(line, false);
        for (int c1 = 0, c2 = cols - 1; c1 < c2; c1++, c2--) {
            for (int i = 0; i < channels; i++) {
                aux = line[c1 * channels + i];
                line[c1 * channels + i] = line[c2 * channels + i];
                line[c2 * channels + i] = aux;
            }
        }
        lout.pack(line, false);
        pngw.writeRow(lout, row);
    }
    pngr.end();
    pngw.copyChunksLast(pngr, copyPolicy);
    pngw.end();
    // // print unknown chunks, just for information
    List<PngChunk> u = ChunkHelper.filterList(pngr.getChunksList()
            .getChunks(), new ChunkPredicate() {
        public boolean match(PngChunk c) {
            return ChunkHelper.isUnknown(c);
        }
    });
    if (!u.isEmpty())
        System.out.println("Unknown chunks:" + u);
}

所以基本上我只是镜像一个 apng 文件,它被转换为一个 png 文件,这是第一帧。 那么有人可以告诉我如何获取剩余的帧并将它们保存为 png 文件吗? 任何帮助或提示都会被应用

【问题讨论】:

    标签: java png apng


    【解决方案1】:

    实际上PNGJ 库(我是作者)不支持 APNG 标准(题外话:我决定反对它,因为我不喜欢 APGN 方法并且因为它不符合我的库的想法:逐行加载“巨大”数据-IDAT-;并直接将块(元数据)加载到内存中;APGN通过将帧存储在块中来滥用PGN标准)。

    您可以随时尝试使用它来做您想做的事,但不能以优雅和健壮的方式。这是一个例子。除了丑陋之外,这不适用于部分帧(小于 APNG 支持的完整图像的帧),也不适用于叠加,也不适用于调色图像(后面的问题将是最容易修复的) (已修复,希望如此)。

    这是用http://philip.html5.org/tests/apng/028.png测试的

    import java.io.File;
    import java.io.FileOutputStream;
    
    import ar.com.hjg.pngj.FileHelper;
    import ar.com.hjg.pngj.ImageLine;
    import ar.com.hjg.pngj.PngHelperInternal;
    import ar.com.hjg.pngj.PngReader;
    import ar.com.hjg.pngj.PngWriter;
    import ar.com.hjg.pngj.chunks.*;
    
    public class ApngSplit {
    
        private static final String PREFIX = "apngf";
    
        /** reads a APNG file and tries to split it into its frames */
        public static void process(File orig) throws Exception {
            PngReader pngr = FileHelper.createPngReader(orig);
            File dest = new File(orig.getParent(), PREFIX + "0_" + orig.getName());
            PngWriter pngw = FileHelper.createPngWriter(dest, pngr.imgInfo, true);
            System.out.println("writing default frame " + pngw.getFilename());
            pngr.setChunkLoadBehaviour(ChunkLoadBehaviour.LOAD_CHUNK_ALWAYS);
            pngr.setMaxBytesMetadata(Integer.MAX_VALUE);
            pngr.setMaxTotalBytesRead(Long.MAX_VALUE);
            pngr.setSkipChunkIds(new String[] {});
            int copyPolicy = ChunkCopyBehaviour.COPY_PALETTE | ChunkCopyBehaviour.COPY_ALL_SAFE;
            pngw.copyChunksFirst(pngr, copyPolicy);
            int cols = pngr.imgInfo.cols;
            int channels = pngr.imgInfo.channels;
            for (int row = 0; row < pngr.imgInfo.rows; row++) {
                ImageLine l1 = pngr.readRow(row);
                pngw.writeRow(l1, row);
            }
            pngr.end();
            pngw.copyChunksLast(pngr, copyPolicy);
            pngw.end();
            processExtra2(orig, pngr.getChunksList());
        }
    
        private static void processExtra2(File orig, ChunksList chunks) throws Exception {
            int numframe = 0;
            FileOutputStream os = null;
            boolean afterIdat = false;
            for (PngChunk chunkApng : chunks.getChunks()) {
                if (chunkApng.id.equals("IDAT"))
                    afterIdat = true;
                if (chunkApng.id.equals("fcTL") && afterIdat) {
                    numframe++;
                    if (os != null)
                        endPng(chunks, os);
                    File dest = new File(orig.getParent(), PREFIX + numframe + "_" + orig.getName());
                    System.out.println("writing seq " + numframe + " : " + dest);
                    os = new FileOutputStream(dest);
                    beginPng(chunks, os);
                }
                if (chunkApng.id.equals("fdAT")) {
                    ChunkRaw crawf = chunkApng.createRawChunk();
                    int seq = PngHelperInternal.readInt4fromBytes(crawf.data, 0);
                    ChunkRaw crawi = new ChunkRaw(crawf.len - 4, ChunkHelper.b_IDAT, true);
                    System.arraycopy(crawf.data, 4, crawi.data, 0, crawi.data.length);
                    crawi.writeChunk(os);
                }
            }
            if (os != null)
                endPng(chunks, os);
        }
    
        private static void endPng(ChunksList chunks, FileOutputStream fos) throws Exception {
            chunks.getById1(PngChunkIEND.ID).createRawChunk().writeChunk(fos);
            fos.close();
        }
    
        private static void beginPng(ChunksList chunks, FileOutputStream fos) throws Exception {
            fos.write(new byte[] { -119, 80, 78, 71, 13, 10, 26, 10 }); // signature
            chunks.getById1(PngChunkIHDR.ID).createRawChunk().writeChunk(fos);
            PngChunk plte = chunks.getById1(PngChunkPLTE.ID);
            if (plte != null)
                plte.createRawChunk().writeChunk(fos);
        }
    
        public static void main(String[] args) throws Exception {
            process(new File("C:/temp/029.png"));
        }
    
    }
    

    【讨论】:

    • 非常感谢您提供的代码,由于大小,我自己从不赞成 apng 文件,但要让您的员工了解:P 很好,我已经能够工作了对于任何少于 50-60 帧的 apng,它的工作原理就像一个冠军,但如果我超出这个范围,图像似乎已损坏并且信息丢失或完全消失,当我尝试查看图像有什么问题时,它表示缺少图像数据或无效的压缩数据。知道为什么会这样吗?
    • 我需要查看有问题的图像。您还可以使用漂亮的工具tweakpng 查看图像内部,看看问题是否出现在块结构中的某些特定模式(例如,每帧不止一个 fdat 块等)
    • 好吧,你发现了,我拥有的所有无法提取的 apng 图像每帧都有多个 fdat 块,所以我应该尝试将所有 fdat 块放在一个框架并使它们合而为一?对不起,如果我听起来像个菜鸟,因为实际上我是在处理图像方面
    • 代码已修复,试试吧。我误解了 fdat 序列号的含义。
    • 不客气。我再次更新了代码,不那么丑陋了,现在应该可以使用调色板了(但我还没有测试过)
    【解决方案2】:

    一些可能有帮助的资源:

    1. 这些示例展示了如何读取 apng 文件,但它是 C 语言并使用 libpng: https://sourceforge.net/projects/apng/files/libpng/examples/

    2. 这里有一些 Java 代码,但它只能创建 APNG 文件,不能读取它们: https://www.reto-hoehener.ch/japng/index.html

    3. 以下是用于读取和显示 APNG 文件的 JavaScript 代码: https://github.com/davidmz/apng-canvas

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-12
      • 1970-01-01
      • 1970-01-01
      • 2013-09-15
      • 2013-08-16
      • 2015-09-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多