2013-02-28 33 views
7

我想将大图像(18000 x 18000)加载到我的应用程序中。如果我使用BufferedImageint_rgb,我需要大约1235mb的堆内存才能加载。这是非常高的内存量,最终用户可能会有更少的内存(1GB或更少)。如何通过BufferedImage将巨大的图像加载到Java?

在我的开发PC上,当我从MyEclipse IDE加载图像时,它会抛出内存不足Exception。当我将代码打包到可执行jar并在Eclipse外部的PC上运行它时,它仍然会引发异常。

如何在不使用1235mb内存的情况下使用缓冲图像将如此大的图像加载到我的应用程序中?有没有一种技巧,就像将图像分割成像图像分割这样的小部分?

我发现this thread on SO,但它对我无用;我想将图像加载到BufferedImage,然后使用Graphics类在Panel上绘制它。

回答

6

可以读取并使用从ImageReadParamImageIO包图像的显示片段。下面是说明如何读取使用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(); 
      } 
     }); 
    } 
} 

结果看起来像这样:

enter image description here

+1

感谢您的宝贵意见,我会尝试这一点,让你知道。 – Mihir 2013-03-01 06:55:56

+0

我成功实施了您的解决方案并且工作正常。请帮助我在这个问题以及http://stackoverflow.com/questions/15438506/how-do-i-convert-an-enormous-tiff-image-to-png-jpeg-without-out-of-memory -错误 – Mihir 2013-03-15 17:24:15

2

一般情况下,你需要做这样的事情:

  • 图像分解成管理的大小的图像文件,并把它们存储在您的应用程序的磁盘。
  • 当显示此图像的特定部分时,仅显示与视口重叠的加载和显示图像片段。
  • 当您平移图像时,适当地更新已加载和显示的图像片段。
  • 要么让不必要的图片碎片被GC收集,要么加载新的碎片以覆盖旧的碎片。 (这最后认为该加载到合并存储缓冲器相同大小的图像片段。)
+0

我同意你的理论。事实上,我很高兴我一直在朝着同一个方向思考,但你能给我一个例子或代码片段吗? – Mihir 2013-03-01 06:55:22

1

一个完整的解决方案是BigBufferedImage。它可以加载和存储比内存限制大得多的图像。诀窍是它的缓冲区被替换为基于文件的DataBuffer实现。它将图像存储在硬盘上。没有使用RAM。这可以防止OutOfMemoryException。

从图像文件创建BigBufferedImage:

BigBufferedImage image = BigBufferedImage.create(
     inputFile, tempDir, TYPE_INT_RGB); 

创建一个空BigBufferedImage:

BigBufferedImage image = BigBufferedImage.create(
     tempDir, width, height, TYPE_INT_RGB); 

渲染的图像的一部分:

part = image.getSubimage(x, y, width, height); 

有关大图像处理read this article的更多信息。

相关问题