2013-02-28 63 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的更多信息。

相關問題