让 BufferedImage 使用更少的 RAM?
我有一个java程序,它从硬盘读取jpeg文件并将其用作各种其他东西的背景图像。图像本身存储在 BufferImage 对象中,如下所示:
BufferedImage background
background = ImageIO.read(file)
这非常有效 - 问题是 BufferedImage 对象本身非常巨大。例如,一个 215k jpeg 文件会变成一个 4 megs 的 BufferedImag 对象并发生变化。相关应用程序可能会加载一些相当大的背景图像,但尽管 jpeg 永远不会超过一兆字节,但用于存储 BufferedImage 的内存可能很快就会超过 100 兆字节。
我认为这一切都是因为图像作为原始 RGB 数据存储在 RAM 中,没有以任何方式压缩或优化。
有没有办法让它以较小的格式将图像存储在内存中?我所处的情况是,CPU 方面比 RAM 方面有更多的闲置空间,因此,为了使图像对象的大小恢复到 jpeg 压缩,稍微降低性能是值得的。
I have java program that reads a jpegfile from the harddrive and uses it as the background image for various other things. The image itself is stored in a BufferImage
object like so:
BufferedImage background
background = ImageIO.read(file)
This works great - the problem is that the BufferedImage
object itself is enormous. For example, a 215k jpeg file becomes a BufferedImag
e object that's 4 megs and change. The app in question can have some fairly large background images loaded, but whereas the jpegs are never more than a meg or two, the memory used to store the BufferedImage
can quickly exceed 100s of megabytes.
I assume all this is because the image is being stored in ram as raw RGB data, not compressed or optimized in any way.
Is there a way to have it store the image in ram in a smaller format? I'm in a situation where I have more slack on the CPU side than RAM, so a slight performance hit to get the image object's size back down towards the jpeg compression would be well worth it.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我的项目之一只是在从 ImageStream 中动态读取图像时对图像进行下采样。下采样将图像的尺寸减小到所需的宽度和宽度。高度,同时不需要昂贵的调整大小计算或修改磁盘上的图像。
由于我将图像下采样到较小的尺寸,因此还显着降低了显示图像所需的处理能力和 RAM。为了进行额外的优化,我也在图块中渲染缓冲图像......但这有点超出了本讨论的范围。尝试以下操作:
要从文件获取 ImageInputStream,请使用:
如您所见,此实现也尊重图像原始长宽比。您可以选择注册 IIOReadProgressListener,以便可以跟踪到目前为止已读取的图像量。例如,如果通过网络读取图像,这对于显示进度条很有用...不过不是必需的,您可以只指定 null。
为什么这与您的情况特别相关?它永远不会将整个图像读入内存,只是根据您的需要将其读入内存,以便以所需的分辨率显示图像。对于巨大的图像非常有效,即使是磁盘上 10 MB 的图像。
One of my projects I just down-sample the image as it is being read from an ImageStream on the fly. The down-sampling reduces the dimensions of the image to a required width & height whilst not requiring expensive resizing computations or modification of the image on disk.
Because I down-sample the image to a smaller size, it also significantly reduces the processing power and RAM required to display it. For extra optimization, I render the buffered image in tiles also... But that's a bit outside the scope of this discussion. Try the following:
To get the ImageInputStream from a File, use:
As you can see, this implementation respects the images original aspect ratio as well. You can optionally register an IIOReadProgressListener so that you can keep track of how much of the image has been read so far. This is useful for showing a progress bar if the image is being read over a network for instance... Not required though, you can just specify null.
Why is this of particular relevance to your situation? It never reads the entire image into memory, just as much as you need it to so that it can be displayed at the desired resolution. Works really well for huge images, even those that are 10's of MB on disk.
确切地说...假设一个 1920x1200 JPG 在内存中可以容纳 300 KB,在(典型的)RGB + alpha 中,每个组件 8 位(因此每个像素 32 位)它应在内存中占用:
所以你的 300 KB 文件变成需要近 9 MB RAM 的图片(请注意,根据您从 Java 使用的图像类型以及 JVM 和操作系统,这有时可能是 GFX 卡 RAM)。
如果你想使用一张图片作为 1920x1200 桌面的背景,你可能不需要比内存中更大的图片(除非你想要一些特殊效果,比如 sub-rgb 抽取/颜色抗锯齿/ ETC。)。
因此,您必须做出选择:
我通常选择第 2 个选项,因为减小硬盘上的文件大小意味着您会丢失细节(1920x1200 图片)不如 3940x2400 下的“相同”详细:缩小尺寸会“丢失信息”)。
现在,Java 在处理这么大的图片时有点糟糕(无论是从性能角度、内存使用角度还是质量角度 [*])。以前,我会从 Java 调用 ImageMagick 首先调整磁盘上图片的大小,然后加载调整后的图像(比如适合我的屏幕大小)。
现在有 Java 桥/API 可以直接与 ImageMagick 连接。
[*] 首先,不可能使用 Java 的内置 API 缩小图像的速度和质量与 ImageMagick 提供的图像一样好。
Exactly... Say a 1920x1200 JPG can fit in, say, 300 KB while in memory, in a (typical) RGB + alpha, 8 bits per component (hence 32 bits per pixel) it shall occupy, in memory:
so your 300 KB file becomes a picture needing nearly 9 MB of RAM (note that depending on the type of images you're using from Java and depending on the JVM and OS this may sometimes be GFX-card RAM).
If you want to use a picture as a background of a 1920x1200 desktop, you probably don't need to have a picture bigger than that in memory (unless you want to some special effect, like sub-rgb decimation / color anti-aliasing / etc.).
So you have to choices:
I typically go with number 2 because reducing file size on hard disk means you're losing details (a 1920x1200 picture is less detailed than the "same" at 3940x2400: you'd be "losing information" by downscaling it).
Now, Java kinda sucks big times at manipulating pictures that big (both from a performance point of view, a memory usage point of view, and a quality point of view [*]). Back in the days I'd call ImageMagick from Java to resize the picture on disk first, and then load the resized image (say fitting my screen's size).
Nowadays there are Java bridges / APIs to interface directly with ImageMagick.
[*] There is NO WAY you're downsizing an image using Java's built-in API as fast and with a quality as good as the one provided by ImageMagick, for a start.
您必须使用 BufferedImage 吗?您能否编写自己的
Image
实现,将 jpg 字节存储在内存中,并根据需要转换为 BufferedImage,然后丢弃?这与一些显示感知逻辑一起应用(在将图像存储为 jpg 之前使用 JAI 重新缩放图像),将使其比每次解码大 jpg 更快,并且占用空间比您当前拥有的更小(处理内存要求除外) 。
Do you have to use
BufferedImage
? Could you write your ownImage
implementation that stores the jpg bytes in memory, and coverts to a BufferedImage as necessary and then discards?This applied with some display aware logic (rescale the image using JAI before storing in your byte array as jpg), will make it faster than decoding the large jpg every time, and a smaller footprint than what you currently have (processing memory requirements excepted).
使用imgscalr:
http://www.imgscalr.org thebuzzmedia.com/software/imgscalr-java-image-scaling-library/
为什么?
代码:
另外,在转换后对较大的图像使用
image.flush()
以帮助提高内存利用率。Use imgscalr:
http://www.thebuzzmedia.com/software/imgscalr-java-image-scaling-library/
Why?
Code:
Also, use
image.flush()
on your larger image after conversion to help with the memory utilization.磁盘上 JPG 文件的大小完全无关。
文件的像素尺寸为。如果您的图像为 15 兆像素,则预计它需要大量 RAM 才能加载原始未压缩版本。
将图像尺寸重新调整为您需要的大小,这是您可以做的最好的事情,而无需使用不太丰富的色彩空间表示。
File size of the JPG on disk is completely irrelevant.
The pixel dimensions of the file are. If your image is 15 Megapixels expect it to require crap load of RAM to load a raw uncompressed version.
Re-size your image dimensions to be just what you need and that is the best you can do without going to a less rich colorspace representation.
您可以将图像的像素复制到另一个缓冲区,看看它占用的内存是否比 BufferedImage 对象少。大概是这样的:
You could copy the pixels of the image to another buffer and see if that occupies less memory then the BufferedImage object. Probably something like this: