1. 程式人生 > >Java使用imageio 讀寫影象

Java使用imageio 讀寫影象

Java中進行影象I/O(即讀圖片和寫圖片,不涉及到複雜影象處理)有三個方法:

Java Image I/O API,支援常見圖片,從Java 2 version 1.4.0開始就內建了。 主頁:http://java.sun.com/javase/6/docs/technotes/guides/imageio/index.html
JAI 中的 Image I/O Tools,支援更多圖片型別,例如JPEG-LS, JPEG2000, 和 TIFF。 主頁:https://jai-imageio.dev.java.net/。JAI 是一個關於影象處理的框架,很龐大, 其中僅僅jai-imageio是關於影象I/O的,其他的可以不看。
JAI的com.sun.media.jai.codec 也有一定的影象解碼能力
當然,還有眾多的java開源工具包可以讀寫影象,例如JIMI, JMagic等,但JDK目前本身能 夠讀寫圖片,就用JDK的,開發和部署方便,不需要額外下載jar包。

由於JAI是Java新加入的,很多元件不是正式規範,JDK不自帶,因此開發和部署需要額外 安裝,安裝檔案在官網https://jai.dev.java.net/下載得到。
如果你僅僅想讀取常見格式的圖片,不需要用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:imagesmyimage.gif”);
BufferedImage bi = ImageIO.read(f);
Java Image I/O API 會自動探測圖片的格式並呼叫對應的外掛進行解碼,當安裝了一個新 外掛,新的格式會被自動理解,程式程式碼不需要改變。

寫圖片同樣簡單:
BufferedImage bi;
File f = new File(“c:imagesmyimage.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物件:
reader.setInput(iis, true);
接下來,指定圖片區域:
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:imagesmyimage.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 事件監聽

3.8案例

package com.test.image;

import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
/**
 * 讀寫圖片
 * @author taoww
 * 參考:http://www.360doc.com/content/16/0224/16/12500151_537069462.shtml
 *
 */
public class RWImage {
	public static void main(String[] args) {
		//simpleRWImage();
		
		complexRWImage();
	}
	
	
	/**
	 * 讀寫圖片簡單方式
	 */
	public static void simpleRWImage(){
		try {
			//讀取圖片
			File f1 = new File("C:\\Users\\Public\\Pictures\\Sample Pictures\\Chrysanthemum.jpg");
			BufferedImage bi=ImageIO.read(f1);
			//將讀取的圖片寫到本地
			File f2 = new File("C:\\Users\\Public\\Pictures\\Sample Pictures\\lihua.png");
			if(!f2.exists()){
				f2.createNewFile();
			}
			ImageIO.write(bi, "png", f2);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Java Image I/O API 提供了為編寫復 雜程式的能力。
	 * 為了利用API的高階特性,應用程式應當直接使用類ImageReader和 ImageWriter讀寫圖片
	 */
	public static void complexRWImage(){
		try {
			/**********************讀取圖片*********************************/
			File f = new File("C:\\Users\\Public\\Pictures\\Sample Pictures\\Chrysanthemum.jpg");
			Iterator readers = ImageIO.getImageReadersByFormatName("jpg");
			ImageReader reader = (ImageReader)readers.next();
			/*
			 * 獲得了一個ImageReader物件,必須給它是指一個輸入源。
			 * 大部分 ImageReader物件可以從ImageInputStream類輸入源讀取資料,
			 * ImageInputStream是Image I/O API定義的專用輸入源
			*/
			ImageInputStream iis = ImageIO.createImageInputStream(f);
			/*
			 * 一旦有了輸入源,可以把它與一個ImageReader物件關聯起來.
			 * 如果輸入原始檔包含多張圖片,而程式不保證按順序讀取時,第二個引數應該設定為 false。
			 * 對於那些只允許儲存一張圖片的檔案格式,永遠傳遞true是合理的
			*/
			reader.setInput(iis, true);
			/*
			 * 如果需要更多的控制,可以向read()方法傳遞一個ImageReadParam型別的引數。
			 * 一個 ImageReadParam物件可以讓程式更好的利用記憶體。
			 * 它不僅允許指定一個感興趣的區域,還 可以指定一個抽樣因子,用於向下取樣.
			 * */
			ImageReadParam param=reader.getDefaultReadParam();
			int imageIndex=0;
			int halfWidth=reader.getWidth(imageIndex)/2;
			int halfHegiht=reader.getHeight(imageIndex)/2;
			Rectangle rectangle=new Rectangle(0,0,halfWidth,halfHegiht);
			param.setSourceRegion(rectangle);
			BufferedImage bi=reader.read(0, param);
			
			/**********************寫圖片*********************************/
			Iterator writes=ImageIO.getImageWritersByFormatName("png");
			ImageWriter imageWriter=(ImageWriter)writes.next();
			f=new File("C:\\Users\\Public\\Pictures\\Sample Pictures\\lihua.png");
			ImageOutputStream iops=ImageIO.createImageOutputStream(f);
			imageWriter.setOutput(iops);
			imageWriter.write(bi);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
參考地址:http://www.360doc.com/content/16/0224/16/12500151_537069462.shtml