【问题标题】:Processing large TIFF files and memory allocation处理大型 TIFF 文件和内存分配
【发布时间】:2017-06-26 15:19:29
【问题描述】:

我一直在开发一个应用程序,该应用程序采用可能非常大的 TIFF 文件并将其拆分为多个较小的文件。为此,它需要遍历所有页面(BufferedImage 对象)并执行一些操作来确定是否应在此处启动新文件或此特定页面是已创建文件的一部分。

显然我无法将整个文件加载到内存中——这就是为什么我只使用ImageIO 读取它的一页。我用方法创建了一个 util 类:

public static BufferedImage getSinglePageFromTiffFile(File file, int pageIndex) throws IOException {
    ImageInputStream is = ImageIO.createImageInputStream(file);
    ImageReader reader;
    try {
        reader = ImageIO.getImageReaders(is).next();
        reader.setInput(is);
        return reader.read(pageIndex);
    } finally {
        if(is != null) is.close();
    }
}

public static int getNumPages(File file) throws IOException {
    ImageInputStream is = ImageIO.createImageInputStream(file);
    ImageReader reader;
    try {
        reader = ImageIO.getImageReaders(is).next();
        reader.setInput(is);
        return reader.getNumImages(true);
    } finally {
        if(is != null) is.close();
    }
}

要将页面写入文件,我使用 ImageWriter 类,如下所示:

int pagesQty = ImageUtils.getNumPages(documentToSplit);
    int currentPageIndex = 0;

    final ImageWriter writer = ImageIO.getImageWritersByFormatName(resultsExtension).next();
    final ImageWriteParam writeParams = writer.getDefaultWriteParam();
    writeParams.setCompressionMode(ImageWriteParam.MODE_COPY_FROM_METADATA);

    BufferedImage page = ImageUtils.getSinglePageFromTiffFile(file, currentPageIndex);

    while(currentPageIndex < pagesQty) {
        OutputStream outStream = null;
        ImageOutputStream imgOutStream = null;

        final File newDocFile = new File(pathName);

        try {
            outStream = new FileOutputStream(newDocFile);
            imgOutStream = ImageIO.createImageOutputStream(outStream);

            writer.setOutput(imgOutStream);
            writer.prepareWriteSequence(null);

            writer.writeToSequence(new IIOImage(page, null, null), writeParams);
            currentPageIndex++;

            while(currentPageIndex < pagesQty) {
                page = ImageUtils.getSinglePageFromTiffFile(documentToSplit, currentPageIndex);

                if(NEWPAGE) {
                    writer.endWriteSequence();
                    break;
                }

                writer.writeToSequence(new IIOImage(page, null, null), writeParams);
            }

        } finally {
            if(imgOutStream != null) imgOutStream.close();
            if(outStream != null) outStream.close();
        }
    }
}

我对这种方法的保留适用于内存使用。在处理文件时,最多分配了 2GB 内存。平均大约 1 - 1.5GB。有没有办法在内存使用方面更有效地执行这些操作?

【问题讨论】:

标签: java memory tiff javax.imageio


【解决方案1】:

通过将 TIFF 页面读取为 BufferedImages,您实际上解压缩了存储的图像,这可能需要大量内存,具体取决于图像的大小:每个像素将占用 3 (RGB) 或 4 (ARGB) 字节,因此,一张 10000 x 10000 像素的图像将占用大约 300 或 400 MB。

根据分配给您的 Java 进程的内存量,以及垃圾收集何时启动,您的进程实际上可能会积累大量已用内存。

由于主要的内存消耗可能来自解压缩的图像 (BufferedImage),因此减少内存使用的最有效方法是不解压缩单个图像来提取它们。我不知道如何用纯 Java 做到这一点,但是有第三方库可以做到这一点。其中之一是iCafe,它声称:

将多页 TIFF 图像拆分为单独的 TIFF 图像,无需解压缩图像

我将此库用于其他图像格式(例如创建动画 GIF),但尚未将其用于 TIFF,但我认为它绝对值得一试。在它的 Wiki-Page 上,它提供了以下 sn-p 来拆分多页 TIFF:

import com.icafe4j.io.RandomAccessInputStream;
import com.icafe4j.io.FileCacheRandomAccessInputStream;
import com.icafe4j.util.FileUtils;

public class TestTIFFTweaker {

    public static void main(String[] args) throws Exception {
        FileOutputStream fin = new FileInputStream(args[0]);
        RandomAccessInputStream rin = new FileCacheRandomAccessInputStream(fin);
        TIFFTweaker.splitPages(rin, FileUtils.getNameWithoutExtension(new File(args[0])));
        rin.close();
        fin.close(); // Need to close the underlying stream explicitly!!!
    }
}

【讨论】:

    猜你喜欢
    • 2015-09-19
    • 2012-05-31
    • 1970-01-01
    • 2017-04-02
    • 2015-10-10
    • 1970-01-01
    • 2018-07-03
    • 2011-05-02
    • 2020-08-10
    相关资源
    最近更新 更多