使BufferedImage使用更少的RAM?

Ele*_*hoy 14 java memory image awt

我有java程序从硬盘驱动器读取jpeg文件,并将其用作各种其他东西的背景图像.图像本身存储在一个BufferImage对象中,如下所示:

BufferedImage background
background = ImageIO.read(file)
Run Code Online (Sandbox Code Playgroud)

这很有效 - 问题在于BufferedImage对象本身是巨大的.例如,一个215k的jpeg文件成为一个BufferedImag4兆的e对象并进行更改.有问题的应用程序可以加载一些相当大的背景图像,但是jpegs永远不会超过一两或两个,用于存储的内存BufferedImage可以快速超过100兆字节.

我假设这一切都是因为图像作为原始RGB数据存储在ram中,而不是以任何方式压缩或优化.

有没有办法让它以更小的格式将图像存储在ram中?我处在CPU方面比RAM更松弛的情况下,因此将图像对象的大小向下移向jpeg压缩的轻微性能损失将非常值得.

S73*_*17H 11

我的一个项目我只是在动态地从ImageStream中读取图像时对其进行下采样.下采样将图像的尺寸减小到所需的宽度和高度,同时不需要昂贵的尺寸调整计算或修改磁盘上的图像.

因为我将图像下采样到较小的尺寸,所以它还显着降低了显示它所需的处理能力和RAM.为了进行额外的优化,我也在瓦片中渲染缓冲的图像......但这有点超出了本讨论的范围.请尝试以下方法:

public static BufferedImage subsampleImage(
    ImageInputStream inputStream,
    int x,
    int y,
    IIOReadProgressListener progressListener) throws IOException {
    BufferedImage resampledImage = null;

    Iterator<ImageReader> readers = ImageIO.getImageReaders(inputStream);

    if(!readers.hasNext()) {
      throw new IOException("No reader available for supplied image stream.");
    }

    ImageReader reader = readers.next();

    ImageReadParam imageReaderParams = reader.getDefaultReadParam();
    reader.setInput(inputStream);

    Dimension d1 = new Dimension(reader.getWidth(0), reader.getHeight(0));
    Dimension d2 = new Dimension(x, y);
    int subsampling = (int)scaleSubsamplingMaintainAspectRatio(d1, d2);
    imageReaderParams.setSourceSubsampling(subsampling, subsampling, 0, 0);

    reader.addIIOReadProgressListener(progressListener);
    resampledImage = reader.read(0, imageReaderParams);
    reader.removeAllIIOReadProgressListeners();

    return resampledImage;
  }

 public static long scaleSubsamplingMaintainAspectRatio(Dimension d1, Dimension d2) {
    long subsampling = 1;

    if(d1.getWidth() > d2.getWidth()) {
      subsampling = Math.round(d1.getWidth() / d2.getWidth());
    } else if(d1.getHeight() > d2.getHeight()) {
      subsampling = Math.round(d1.getHeight() / d2.getHeight());
    }

    return subsampling;
  }
Run Code Online (Sandbox Code Playgroud)

要从File获取ImageInputStream,请使用:

ImageIO.createImageInputStream(new File("C:\\image.jpeg"));
Run Code Online (Sandbox Code Playgroud)

如您所见,此实现也尊重图像原始宽高比.您可以选择注册IIOReadProgressListener,以便您可以跟踪到目前为止已读取了多少图像.这对于显示进度条非常有用,例如,如果通过网络读取图像...但不是必需的,您只需指定null即可.

为什么这与您的情况特别相关?它永远不会将整个图像读入内存,就像您需要它一样,以便它可以以所需的分辨率显示.对于巨大的图像非常有效,即使是那些10英寸MB的磁盘也是如此.


小智 1

磁盘上 JPG 文件的大小完全无关
文件的像素尺寸为。如果您的图像为 15 兆像素,则预计它需要大量 RAM 才能加载原始未压缩版本。
将图像尺寸重新调整为您需要的大小,这是您可以做的最好的事情,而无需使用不太丰富的色彩空间表示。

  • 源文件有多大并不重要。就像尝试读取已压缩的 1GB TXT 文件一样,与 zip 文件大小无关。一个磁盘文件大小与显示未压缩图像所需的内存量无关。 (4认同)