如何通过 BufferedImage 将巨大的图像加载到 Java?

Posted

技术标签:

【中文标题】如何通过 BufferedImage 将巨大的图像加载到 Java?【英文标题】:How do I load an enormous image to Java via BufferedImage? 【发布时间】:2013-02-28 17:10:46 【问题描述】:

我想将大图像 (18000 x 18000) 加载到我的应用程序中。如果我使用BufferedImage 类型为int_rgb,我需要大约1235mb 的堆内存来加载。这是一个非常大的内存量,最终用户的内存可能会更少(1GB 或更少)。

在我的开发 PC 上,当我从 MyEclipse IDE 加载图像时,它会引发内存不足 Exception。当我将代码打包到可执行 jar 并在 Eclipse 外部的 PC 上运行时,它仍然会引发异常。

如何在不使用 1235mb 内存的情况下使用缓冲图像将如此大的图像加载到我的应用程序中?有没有什么技巧,比如将图像分割成更小的部分,比如图像分割?

我找到了this thread on SO,但它对我没有用;我想将图像加载到BufferedImage,然后使用Graphics 类将其绘制在Panel 上。

【问题讨论】:

【参考方案1】:

您可以使用来自ImageIO 包的ImageReadParam 读取和显示图像片段。这是一个基本示例,说明如何使用ImageReadParam 读取单个片段而不读取整个图像:

import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.swing.*;

public class TestImageChunks 
    private static void createAndShowUI() 
        try 
            URL url = new URL(
                    "http://duke.kenai.com/wave/.Midsize/Wave.png.png");
            Image chunk = readFragment(url.openStream(), new Rectangle(150,
                    150, 300, 250));
            JOptionPane.showMessageDialog(null, new ImageIcon(chunk), "Duke", 
                    JOptionPane.INFORMATION_MESSAGE);
         catch (IOException e) 
            JOptionPane.showMessageDialog(null, e.getMessage(), "Failure",
                    JOptionPane.ERROR_MESSAGE);
            e.printStackTrace();
        
    

    public static BufferedImage readFragment(InputStream stream, Rectangle rect)
            throws IOException 
        ImageInputStream imageStream = ImageIO.createImageInputStream(stream);
        ImageReader reader = ImageIO.getImageReaders(imageStream).next();
        ImageReadParam param = reader.getDefaultReadParam();

        param.setSourceRegion(rect);
        reader.setInput(imageStream, true, true);
        BufferedImage image = reader.read(0, param);

        reader.dispose();
        imageStream.close();

        return image;
    

    public static void main(String[] args) 
        SwingUtilities.invokeLater(new Runnable() 
            public void run() 
                createAndShowUI();
            
        );
    

结果如下所示:

【讨论】:

感谢您宝贵的 cmets,我会试试这个并告诉您。 我成功地实施了您的解决方案并且它奏效了。请在这个问题上帮助我***.com/questions/15438506/…【参考方案2】:

通常,您需要执行以下操作:

将图像分解为可管理大小的图像文件,并将它们与您的应用程序一起存储在磁盘上。 显示此图像的特定部分时,仅加载和显示与您的视口重叠的图像片段。 在您平移图像时,适当地更新加载和显示的图像片段。 要么让 GC 收集不必要的图像片段,要么以覆盖旧图像的方式加载新的图像片段。 (这最后主张加载到池化内存缓冲区中的相同大小的图像片段。)

【讨论】:

我同意你的理论。事实上我很高兴我一直在朝同一个方向思考,但是你能给我一个例子或代码 sn-ps 吗?

以上是关于如何通过 BufferedImage 将巨大的图像加载到 Java?的主要内容,如果未能解决你的问题,请参考以下文章

如何将BufferedImage保存为文件

如何将 BufferedImage 绘制到 JPanel

java:BufferedImage判断图像通道顺序并转RGB/BGR

如何真正快速地从大 Bufferedimage 中获取小图像

将两个 BufferedImage 并排复制到一个图像中

如何编辑 BufferedImage 中的像素?