1. 程式人生 > >java圖片處理---Javax.imageIO包的用法

java圖片處理---Javax.imageIO包的用法

Java中進行影象I/O(即讀圖片和寫圖片,不涉及到複雜影象處理)有三個方法:
1. Java Image I/O API,支援常見圖片,從Java 2 version 1.4.0開始就內建了。
主頁:http://java.sun.com/javase/6/docs/technotes/guides/imageio/index.html
2. JAI 中的 Image I/O Tools,支援更多圖片型別,例如JPEG-LS, JPEG2000, 和 TIFF。
JAI 是一個關於影象處理的框架,很龐大,
其中僅僅jai-imageio是關於影象I/O的,其他的可以不看。
3. JAI的com.sun.media.jai.codec 也有一定的影象解碼能力

當然,還有眾多的java開源工具包可以讀寫影象,例如JIMI, JMagic等,但JDK目前本身能
夠讀寫圖片,就用JDK的,開發和部署方便,不需要額外下載jar包。

如果你僅僅想讀取常見格式的圖片,不需要用JAI這麼高階這麼龐大的東西,
用Java Image I/O API即可。

下面重點介紹 Java Image I/O API。

Java Image I/O API 主要在 javax.imageio 下面。JDK已經內建了常見圖片格式的外掛,
但它提供了外掛體系結構,第三方也可以開發外掛支援其他圖片格式。

下面這段程式碼可以展示,JDK內建支援的圖片格式。
import javax.imageio.*;
import java.util.Arrays;

public class HelloWorld {
public static void main(String args[]) {
String readFormats[] = ImageIO.getReaderFormatNames();
String writeFormats[] = ImageIO.getWriterFormatNames();
System.out.println(“Readers: ” + Arrays.asList(readFormats));
System.out.println(“Writers: ” + Arrays.asList(writeFormats));
}
}

主頁上有一個文件,Java Image I/O API Guide,很通俗易懂,可以讓你快速上手。以下
內容主要來自這個文件的第3章。

第3章 編寫影象I/O程式
3.1 讀寫圖片

javax.imageio.ImageIO類提供了一組靜態方法進行最簡單的影象I/O操作。
讀取一個標準格式(GIF, PNG, or JPEG)的圖片很簡單:
File f = new File(“c:\images\myimage.gif”);
BufferedImage bi = ImageIO.read(f);

Java Image I/O API 會自動探測圖片的格式並呼叫對應的外掛進行解碼,當安裝了一個新
外掛,新的格式會被自動理解,程式程式碼不需要改變。

寫圖片同樣簡單:
BufferedImage bi;
File f = new File(“c:\images\myimage.png”);
ImageIO.write(im, “png”, f);

3.2 更進一步
上一節談到的方法對於簡單程式已經足夠了。不過,Java Image I/O API 提供了為編寫復
雜程式的能力。為了利用API的高階特性,應用程式應當直接使用類ImageReader 和
ImageWriter。

3.3 ImageReader 類
與其用ImageIO類來進行所有的解碼操作,不如用ImageIO類去得到一個ImageReader物件,
再用這個物件去進行讀操作:
Iterator readers = ImageIO.getImageReadersByFormatName(“gif”);
ImageReader reader = (ImageReader)readers.next();

ImageReader物件也可以基於檔案內容、檔案字尾或MIME型別獲得。這個用於查詢和初始
化ImageReader物件的機制用到了javax.imageio.spi.ImageReaderSpi類,它可以在不用初
始化外掛的情況下獲得外掛的資訊。”service provider interfaces” (SPIs)將會在下一
章詳細討論。一旦獲得了一個ImageReader物件,必須給它是指一個輸入源。大部分
ImageReader物件可以從ImageInputStream類輸入源讀取資料,ImageInputStream是Image
I/O API定義的專用輸入源。

獲得一個ImageInputStream 是簡單的。給定一個File或InputStream,一個
ImageInputStream物件可以通過呼叫如下函式產生:
Object source; // File or InputStream
ImageInputStream iis = ImageIO.createImageInputStream(source);

一旦有了輸入源,可以把它與一個ImageReader物件關聯起來:
reader.setInput(iis, true);

如果輸入原始檔包含多張圖片,而程式不保證按順序讀取時,第二個引數應該設定為
false。對於那些只允許儲存一張圖片的檔案格式,永遠傳遞true是合理的。

當ImageReader物件有了輸入源後,我們就可以獲取圖片資訊而不用把整張圖片資料都讀入
記憶體。例如,呼叫reader.getImageWidth(0)可以讓我們獲得檔案中第一張圖片的寬度。一
個好的外掛會試圖解碼檔案的必要部分,去獲得圖片的寬度,而不用讀取任何一個畫素。

為讀取圖片,可以呼叫reader.read(imageIndex), imageIndex是檔案(當包含多張圖片時)
中圖片的索引。這與上一節呼叫ImageIO.read()產生的結果相同。

3.3.1 ImageReadParam
如果需要更多的控制,可以向read()方法傳遞一個ImageReadParam型別的引數。一個
ImageReadParam物件可以讓程式更好的利用記憶體。它不僅允許指定一個感興趣的區域,還
可以指定一個抽樣因子,用於向下取樣。

例如,為了只解碼圖片的左上角的1/4,程式可以先獲取一個合適的ImageReadParam物件:
ImageReadParam param = reader.getDefaultReadParam();

接下來,指定圖片區域:
import java.awt.Rectangle;
int imageIndex = 0;
int half_width = reader.getImageWidth(imageIndex)/2;
int half_height = reader.getImageHeight(imageIndex)/2;
Rectangle rect = new Rectangle(0, 0, half_width, half_height);
param.setSourceRegion(rect);

最後,讀取圖片:
BufferedImage bi = reader.read(imageIndex, param);

結果是一張新圖片,寬和高都只有原圖片的一半。

另一個例子,為了讀取每三個畫素中的一個,產生一個原圖片1/9大小的圖片,可以用
ImageReadParam指定抽樣因子:
param = reader.getDefaultImageParam();
param.setSourceSubsampling(3, 3, 0, 0);
BufferedImage bi3 = reader.read(0, param);

3.3.2 IIOParamController
外掛有時會提供一個IIOParamController類,這是可選的。略。

3.3.3 讀多圖片檔案
ImageReader 中所有與圖片打交道的方法都有一個imageIndex 引數,這個引數用於讀取多
圖片檔案中的一張。

ImageReader.getNumImages()返回多圖片檔案中的圖片個數。這個方法有一個boolean引數,
allowSearch。有的圖片格式,典型的GIF,沒有提供任何獲取檔案中的圖片個數方法,除
非讀取整個進行解析。這樣代價很高,因此設定allowSearch為false可以讓方法直接返回
-1,而不是實際的圖片個數。如果此引數是true,則該方法總會返回檔案中實際的圖片個
數。

即使在不知道檔案中圖片個數的情況下,仍可以呼叫read(imageIndex); 如果索引值過大,
該方法會丟擲IndexOutOfBoundsException異常。因此,程式可以遞增索引去獲取圖片,
直到異常。

3.3.4 讀縮圖 
有的圖片格式允許一個(或多個)小的預覽圖,與主圖片一起儲存在檔案中。這些
“縮圖”對於快速識別圖片很有用,不用解碼整個圖片。

程式可以呼叫如下程式碼,探測一張圖片有多少張縮圖:
reader.getNumThumbnails(imageIndex);

如果存在縮圖,可以呼叫如下程式碼獲取:
int thumbailIndex = 0;
BufferedImage bi;
bi = reader.readThumbnail(imageIndex, thumbnailIndex);

3.4 ImageWriter 類 
就像我們可以用ImageIO 的一個方法獲取某種圖片格式的ImageReader物件一樣,我們也可
以獲取ImageWriter物件:
Iterator writers = ImageIO.getImageWritersByFormatName(“png”);
ImageWriter writer = (ImageWriter)writers.next();

一旦獲取了一個ImageWriter物件,必須給它設定一個輸出源ImageOutputStream。
File f = new File(“c:\images\myimage.png”);
ImageOutputStream iOS = ImageIO.createImageOutputStream(f);
writer.setOutput(ios);

最後,可以把圖片寫入到輸出源:
BufferedImage bi;
writer.write(bi);

3.4.1 寫多圖片檔案
IIOImage類用於儲存圖片,縮圖或元資訊的引用。下一節將討論Metadata,目前,我們
簡單地給Metadata相關引數傳遞null。
ImageWriter 類有一個方法write(),用於從IIOImage建立一個新檔案,還有一個方法
writeInsert(),用於向一個已存在檔案新增一個IIOImage物件。通過呼叫這兩者,可以創
建一個多圖片檔案:
BufferedImage first_bi, second_bi;
IIOImage first_IIOImage = new IIOImage(first_bi, null, null);
IIOImage second_IIOImage = new IIOImage(second_bi, null, null);
writer.write(null, first_IIOImage, null);
if (writer.canInsertImage(1)) {
writer.writeInsert(1, second_IIOImage, null);
} else {
System.err.println(“Writer can’t append a second image!”);
}

3.5 處理 Metadata 
所有與畫素無關的資訊,都屬於在Metadata。javax.imageio.metadata 包含了用於訪問
Metadata的類和介面。

Image I/O API 將stream metadata 和image metadata區別對待。stream metadata與一個
檔案中儲存了多張圖片有關,image metadata只與單個圖片有關。如果一個檔案只包含一張
圖片,那麼就只存在image metadata。

可以通過呼叫ImageReader.getStreamMetadata 和 getImageMetadata(int imageIndex)來
獲取metadata。這些方法會返回一個實現了IIOMetadata介面的物件,該物件會被向上轉化
為ImageReader型別,

3.6 編碼轉換

3.7 事件監聽