java:影象(BufferedImage)色彩空間轉換(灰度)暨獲取影象矩陣資料byte[](sRGB/gray)
ColorConvertOp
java.awt.image
包下面有個類java.awt.image.ColorConvertOp
,類名直譯就是”顏色轉換操作”。
顧名思義,它的作用就是將一個色彩空間(color space)的影象轉換為另一個色彩空間的影象。有了這個神器我們就能輕易的將一張彩色圖你像轉換成灰度(gray)或其他色彩空間影象。
程式碼非常簡單,只要一行。
public BufferedImage toGray(BufferedImage srcImg){
return new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null ).filter(srcImg, null);
}
依此類推,你可以參照ColorConvertOp
的引數說明將影象轉為其他格式。
java.awt.color.ColorSpace中列出了很多支援的色彩空間定義TYPE_RGB
,TYPE_CMYK
,TYPE_HSV
,TYPE_YCbCr
….
Raster.getDataElements
有時我們通過ImageIO得到解碼後的影象資料物件(BufferedImage)以後,需要獲取影象矩陣的裸資料(即一個儲存影象資料的byte陣列)。
BufferedImage中提供了一個getRGB()方法,它返回的是一個ARGB格式int[]陣列(每個int型元素的4個位元組分別代表一個畫素的Alpha,Red,Green,Blue四個通道)
如果你要從這個方法獲取RGB的陣列,你還得自己寫轉換程式碼:
/**
* 返回影象的RGB格式位元組陣列
* @param image
* @return
*/
public static byte[] getMatrixRGB(BufferedImage image){
int w = image.getWidth();
int h = image.getHeight();
int[] intArray = new int[w * h];
byte[] matrixRGB = new byte[w * h * 3];
image.getRGB(0 , 0, w, h, intArray, 0, w);
// ARGB->RGB
for(int i=0,b=0;i<intArray.length;++i){
matrixRGB[b++]=(byte) (matrixRGB[i]&0x000000FF);
matrixRGB[b++]=(byte) ((matrixRGB[i]&0x0000FF00)>>8);
matrixRGB[b++]=(byte) ((matrixRGB[i]&0x00FF0000)>>16);
}
return matrixRGB;
}
好煩吶,我以前就是這麼幹的,真的沒有提供更好的方法嗎?
不是沒有更好的方法,而是我學藝不精沒找到而已。
在仔細研究了BufferedImage的程式碼之後,才明白getRGB()只是BufferedImage為預設 RGB 顏色模型 (TYPE_INT_ARGB
)提供的一個便利性封裝。
通過getRGB()原始碼可以知道BufferedImage物件中真正的影象資料是由成員物件raster
(java.awt.image.WritableRaster
)管理。而WritableRaster
是java.awt.image.Raster
的子類。Raster
中getDataElements方法可以我們所需要的位元組陣列。
還以前面影象轉灰度舉例,如果要從灰度影象中獲取影象矩陣的位元組陣列,程式碼示例如下:
/**
* 獲取灰度影象的位元組陣列
* @param image
* @return
*/
public static byte[] getMatrixGray(BufferedImage image) {
// 轉灰度影象
BufferedImage grayImage = new BufferedImage(width, height,
BufferedImage.TYPE_BYTE_GRAY);
new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null).filter(image, grayImage);
// getData方法返回BufferedImage的raster成員物件
return (byte[]) grayImage.getData().getDataElements(0, 0, image.getWidth(), image.getHeight(), null);
}
注意這裡return語句使用了(byte[])強制型別轉換,因為getDataElements返回的是開啟宣告 java.lang.Object物件。
也就是說getDataElements返回的未必是byte[]型別,為什麼呢?看下面getDataElements方法的說明:
看不懂沒關係,我們可以看到這裡的返回的型別可能是:TYPE_BYTE,TYPE_USHORT,TYPE_INT,TYPE_SHORT,TYPE_FLOAT,TYPE_DOUBLE。並不一定是byte。
那麼問題來了,如何控制返回的陣列型別是byte[]呢?
同樣,我們可以使用前面的ColorConvertOp
物件進行轉換。
比如我們需要得到影象的RGB資料:
/**
* 獲取影象RGB格式資料
* @param image
* @return
*/
public static byte[] getMatrixRGB(BufferedImage image){
if(image.getType()!=BufferedImage.TYPE_3BYTE_BGR){
// 轉sRGB格式
BufferedImage rgbImage = new BufferedImage(
image.getWidth(),
image.getHeight(),
BufferedImage.TYPE_3BYTE_BGR);
new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB), null).filter(image, rgbImage);
// 從Raster物件中獲取位元組陣列
return (byte[]) rgbImage.getData().getDataElements(0, 0, rgbImage.getWidth(), rgbImage.getHeight(), null);
}else{
return (byte[]) image.getData().getDataElements(0, 0, image.getWidth(), image.getHeight(), null);
}
}