1. 程式人生 > >java:影象(BufferedImage)色彩空間轉換(灰度)暨獲取影象矩陣資料byte[](sRGB/gray)

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)管理。而WritableRasterjava.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);
        }
    }