【问题标题】:How do I load an enormous image to Java via BufferedImage?如何通过 BufferedImage 将巨大的图像加载到 Java?
【发布时间】: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 上。

【问题讨论】:

    标签: java image image-processing java-2d


    【解决方案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();
                }
            });
        }
    }
    

    结果如下所示:

    【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 我同意你的理论。事实上我很高兴我一直在朝同一个方向思考,但是你能给我一个例子或代码 sn-ps 吗?
    猜你喜欢
    • 1970-01-01
    • 2012-11-03
    • 1970-01-01
    • 2013-10-27
    • 1970-01-01
    • 1970-01-01
    • 2012-02-23
    • 2016-08-20
    • 1970-01-01
    相关资源
    最近更新 更多