I want to load large images (18000 x 18000) to my application. If i use BufferedImage
with type int_rgb
, I need around 1235mb of heap memory to load. This is a very high amount of memory, and end users will likely have less ram (1GB or less).
我想将大图像(18000 x 18000)加载到我的应用程序中。如果我使用类型为int_rgb的BufferedImage,我需要大约1235mb的堆内存来加载。这是一个非常大的内存量,最终用户可能会有更少的内存(1GB或更少)。
On my development PC, when I load the image from MyEclipse IDE, it throws an out of memory Exception
. When i pack my code to an executable jar and run it on my PC external of Eclipse, it still throws an exception.
在我的开发PC上,当我从MyEclipse IDE加载图像时,它会抛出一个内存不足的异常。当我将我的代码打包到一个可执行jar并在Eclipse外部的PC上运行时,它仍然会抛出异常。
How do I load such a large image into my application using buffered image without using 1235mb of memory? Is there a trick, like splitting the image into smaller portions like image segmentation?
如何在不使用1235mb内存的情况下使用缓冲图像将如此大的图像加载到我的应用程序中?有没有一个技巧,比如将图像分成像图像分割这样的较小部分?
I found this thread on SO, but it not useful for me; I want to load the image into BufferedImage
and then draw it on a Panel
using the Graphics
class.
我在SO上找到了这个帖子,但它对我没用;我想将图像加载到BufferedImage中,然后使用Graphics类在Panel上绘制它。
3 个解决方案
#1
6
You can read and display fragments of the image using ImageReadParam from ImageIO
package. Here is a basic example that illustrates how to read a single fragment using ImageReadParam
without reading the whole image:
您可以使用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();
}
});
}
}
The result looks like this:
结果如下:
#2
2
Generally, you'd need to do something like this:
通常,您需要执行以下操作:
- Break the image into manageable size image files and store them on disk with your application.
- When displaying a particular part of this image, only the load and display image fragments that overlap your viewport.
- As you pan around the image, update the loaded and displayed image fragments appropriately.
- Either let the unnecessary image fragments get collected by the GC or load new ones in such a way that they overwrite older ones. (This last argues for identically-sized image fragments that load into pooled memory buffers.)
将图像分解为可管理的大小图像文件,并将其与应用程序一起存储在磁盘上。
显示此图像的特定部分时,仅显示与视口重叠的加载和显示图像片段。
在平移图像时,适当更新已加载和显示的图像片段。
要么让GC收集不必要的图像片段,要么加载新的图像片段以覆盖旧的片段。 (这最后争论加载到池化内存缓冲区中的相同大小的图像片段。)
#3
1
A complete solution is BigBufferedImage. It can load and store much larger images than the memory limit. The trick is that its buffer is replaced with a file based implementation of DataBuffer. It stores the image on the hard drive. No RAM is used. This can prevent OutOfMemoryException.
完整的解决方案是BigBufferedImage。它可以加载和存储比内存限制更大的图像。诀窍是它的缓冲区被基于文件的DataBuffer实现替换。它将图像存储在硬盘驱动器上。没有使用RAM。这可以防止OutOfMemoryException。
Create a BigBufferedImage from an image file:
从图像文件创建BigBufferedImage:
BigBufferedImage image = BigBufferedImage.create(
inputFile, tempDir, TYPE_INT_RGB);
Create an empty BigBufferedImage:
创建一个空的BigBufferedImage:
BigBufferedImage image = BigBufferedImage.create(
tempDir, width, height, TYPE_INT_RGB);
Render the part of the image:
渲染图像的一部分:
part = image.getSubimage(x, y, width, height);
For more information about big image handling read this article.
有关大图像处理的更多信息,请阅读本文。
#1
6
You can read and display fragments of the image using ImageReadParam from ImageIO
package. Here is a basic example that illustrates how to read a single fragment using ImageReadParam
without reading the whole image:
您可以使用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();
}
});
}
}
The result looks like this:
结果如下:
#2
2
Generally, you'd need to do something like this:
通常,您需要执行以下操作:
- Break the image into manageable size image files and store them on disk with your application.
- When displaying a particular part of this image, only the load and display image fragments that overlap your viewport.
- As you pan around the image, update the loaded and displayed image fragments appropriately.
- Either let the unnecessary image fragments get collected by the GC or load new ones in such a way that they overwrite older ones. (This last argues for identically-sized image fragments that load into pooled memory buffers.)
将图像分解为可管理的大小图像文件,并将其与应用程序一起存储在磁盘上。
显示此图像的特定部分时,仅显示与视口重叠的加载和显示图像片段。
在平移图像时,适当更新已加载和显示的图像片段。
要么让GC收集不必要的图像片段,要么加载新的图像片段以覆盖旧的片段。 (这最后争论加载到池化内存缓冲区中的相同大小的图像片段。)
#3
1
A complete solution is BigBufferedImage. It can load and store much larger images than the memory limit. The trick is that its buffer is replaced with a file based implementation of DataBuffer. It stores the image on the hard drive. No RAM is used. This can prevent OutOfMemoryException.
完整的解决方案是BigBufferedImage。它可以加载和存储比内存限制更大的图像。诀窍是它的缓冲区被基于文件的DataBuffer实现替换。它将图像存储在硬盘驱动器上。没有使用RAM。这可以防止OutOfMemoryException。
Create a BigBufferedImage from an image file:
从图像文件创建BigBufferedImage:
BigBufferedImage image = BigBufferedImage.create(
inputFile, tempDir, TYPE_INT_RGB);
Create an empty BigBufferedImage:
创建一个空的BigBufferedImage:
BigBufferedImage image = BigBufferedImage.create(
tempDir, width, height, TYPE_INT_RGB);
Render the part of the image:
渲染图像的一部分:
part = image.getSubimage(x, y, width, height);
For more information about big image handling read this article.
有关大图像处理的更多信息,请阅读本文。