簡體   English   中英

如何從流中讀取圖像?

[英]How to read an image from stream?

有人可能會建議BufferedImage是用Java處理圖像的最佳選擇。 雖然很方便,但在閱讀巨大的圖像時,它往往最終會出現:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

增加VM大小不是解決方案,因為在我的情況下,某些輸入文件非常龐大。

所以我正在尋找如何從流中逐步讀取圖像的方式。

我懷疑來自ImageIO ImageIO.createImageInputStream()可能符合要求,但我不確定如何使用它來逐步讀取 另外,JDK的rt.jar上有類PNGMetadataPNGImageReader似乎很有用,但我沒有找到它們用法的簡單示例。

這是要走的路,還是有更好的選擇?

用於讀取和操作圖像的Java API並不像您想象的那樣基於流。 ImageInputStream只是一個便利包裝器,允許從不同的輸入( RandomAccessFileInputStream等)讀取byte和其他基本類型。

我一直在考慮創建一個用於讀取“像素流”的API,以允許在不使用大量內存的情況下鏈接處理過濾器。 但是,從來沒有認真對待這種努力。 如果您想聽到更多想法或有可行的實施,請隨意雇用我。 ;-)

不過,正如我所看到的,您可以通過多種方式實現最終目標,從而能夠處理大型圖像:

  1. 按原樣使用BufferedImage ,使用ImageIO API以較小的部分讀取圖像以節省內存。 由於實現,這對某些格式非常有效,對其他格式效率較低(即默認的JPEGImageReader將在將較小的區域移交給Java堆之前讀取本機內存中的整個圖像,但PNGImageReader可能沒問題)。

    有點像:

     ImageInputStream stream = ImageIO.createImageInputStream(input); ImageReader reader = ImageIO.getImageReaders(stream).next(); // TODO: Test hasNext() reader.setInput(stream); int width = reader.getWidth(0); int height = reader.getHeight(0); ImageReadParam param = reader.getDefaultReadParam(); for (int y = 0; y < height; y += 100) { for (int x = 0; x < width; x += 100) { param.setSourceRegion(new Rectangle(x, y, 100, 100)); // TODO: Bounds check // Read a 100 x 100 tile from the image BufferedImage region = reader.read(0, param); // ...process region as needed... } } 
  2. 立即讀取整個圖像,進入內存映射緩沖區。 隨意嘗試一些我為此目的而做的實驗課 (使用nio )。 讀取將比讀取純存儲器圖像慢,並且處理速度也會變慢。 但是,如果您一次對圖像的較小區域進行計算,則可能與內存中的某些優化一樣快。 我已經使用這些類將> 1 GB圖像讀入32 MB JVM(實際內存消耗當然要大得多)。

    再次,這是一個例子:

     ImageInputStream stream = ImageIO.createImageInputStream(input); ImageReader reader = ImageIO.getImageReaders(stream).next(); // TODO: Test hasNext() reader.setInput(stream); int width = reader.getWidth(0); int height = reader.getHeight(0); ImageTypeSpecifier spec = reader.getImageTypes(0).next(); // TODO: Test hasNext(); BufferedImage image = MappedImageFactory.createCompatibleMappedImage(width, height, spec) ImageReadParam param = reader.getDefaultReadParam(); param.setDestination(image); image = reader.read(0, param); // Will return same image as created above // ...process image as needed... 
  3. 某些格式(如未壓縮的TIFF,BMP,PPM等)會保留文件中的像素,使其可以直接對其進行內存映射以對其進行操作。 需要一些工作,但應該是可能的。 TIFF還支持可能有用的瓷磚。 我將此選項作為練習,隨意使用我上面鏈接的課程作為起點或靈感。 ;-)

  4. JAI可能有一些可以幫助你的東西。 我不是一個忠實的粉絲,因為甲骨文有許多未解決的錯誤和缺乏愛心和發展。 但值得一試。 認為他們也支持基於tileable和基於磁盤的RenderedImage 再次,我將留下這個選項供您進一步探索。

內存問題肯定與解碼過程本身無關,而是將整個映像作為BufferedImage存儲在內存中。 可以逐步讀取PNG圖像,但是:

  • 這與“組塊”中的組織只有輕微的關系,因為PNG文件是逐行編碼的,因此它們可以在原理上逐行讀取。

  • 在隔行掃描PNG的情況下,上述假設會中斷 - 但人們不應期望以隔行掃描格式存儲巨大的PNG圖像

  • 雖然一些PNG庫允許逐行(逐行)解碼(例如: libpng ),但標准Java API並不能滿足您的要求。

我遇到了這個問題,最后我編寫了自己的Java庫: PNGJ 它非常成熟,它允許逐行讀取PNG圖像,最大限度地減少內存消耗,並以相同的方式編寫它們(甚至可以讀取隔行掃描的PNG,但在這種情況下,內存問題不會消失。)如果您只需要做一些“本地”圖像處理(根據當前值和鄰居修改每個像素值,並將其寫回)這應該有所幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM