【问题标题】:Loading large images as thumbnails without memory issues in Java?在Java中加载大图像作为缩略图而没有内存问题?
【发布时间】:2011-08-17 23:47:12
【问题描述】:

我试图让用户从他们的硬盘加载图像,并在 GUI 中以缩略图列表的形式直观地呈现这些图像(带有添加到 JList 的图标的 JPanel)。我目前正在使用 ImageIO.read() 来获取 BufferedImage 并为每个图像使用 getScaledInstance (听说你不应该使用它)。

它适用于小图像,但加载超过四张照片(5000x3000 或类似),我得到“java.lang.OutOfMemoryError: Java heap space”。没有保存对完整大小的 BufferedImage 的引用,所以我认为垃圾收集器会处理它并只保留缩放后的图像(这不应该占用太多内存),但它看起来不像。我也踩了 getRuntime().gc() 和 System.gc() ,但没有效果。

有什么好的方法可以从文件中加载缩放的图像,而不会遇到内存错误?显然,很多软件都可以做到这一点,但也许不是在 Java 中。外部库没问题。


当前代码:

BufferedImage unscaledImage = ImageIO.read(imageFile);

int unscaledHeight = unscaledImage.getHeight();
int unscaledWidth = unscaledImage.getWidth();

int imageRatio = unscaledHeight/unscaledWidth;

if (imageRatio >= 1) {
    return new ImageIcon(unscaledImage.getScaledInstance(width,-1,Image.SCALE_FAST));
} else {
    return new ImageIcon(unscaledImage.getScaledInstance(-1,height,Image.SCALE_FAST));
}

【问题讨论】:

    标签: java memory-management image-processing thumbnails


    【解决方案1】:

    问题在于使用 BufferedImage 本身。当文件被读入内存时,您将用完堆空间。根据您的用途,您可以使用图像阅读器,也可以增加堆的大小。

    我建议您使用图像阅读器。例如,要获得一个图像阅读器,您的代码将是这样的:

      // Create an image input stream on the image
        ImageInputStream iis = ImageIO.createImageInputStream(o);
    
        // Find all image readers that recognize the image format
        Iterator iter = ImageIO.getImageReaders(iis);
        if (!iter.hasNext()) {
            // No readers found
            return null;
        }
    
        // Use the first reader
        ImageReader reader = (ImageReader)iter.next();
    

    发件人:http://www.exampledepot.com/egs/javax.imageio/DiscType.html

    如果您拥有 ImageReader,您可以通过调用 reader.getAspectRatio() 来获取宽高比

    不过,我不确定您如何从 ImageReader 转到缩略图。

    【讨论】:

    • 优秀。在使用 ImageReader.read(0, ImageReadParam) 获取 BufferedImage 之前,使用 ImageReadParam.getSourceSubSampling(wratio, hratio, 0, 0) 对其进行缩放非常有效。
    • @SWEn0thing。很酷,我必须记住如何获取缩略图。
    • 我也需要添加以下调用。 ImageReadParam 参数 = reader.getDefaultReadParam(); reader.setInput(iis, true, true); params.setSourceSubsampling(width, height, 0, 0);
    【解决方案2】:

    JVM 有一个缓存图像的坏习惯。解决它的一种方法是:

    1. 创建一个指向图像的InputStream
    2. 将整个图像数据读入byte[](使用标准 I/O API - 外部imageio 等)。
    3. byte[] 创建一个ByteArrayInputStream
    4. 使用ByteArrayInputStream 作为ImageIO.read(InputStream) 的来源。

    因为JVM不知道图片字节是从什么资源获取的,所以无法唯一标识图片,不会缓存。


    请注意,我找不到任何支持我所说的文档。这只是过去的经验。

    【讨论】:

      猜你喜欢
      • 2011-09-21
      • 1970-01-01
      • 2012-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-31
      • 1970-01-01
      相关资源
      最近更新 更多