Java 圖像處理(一)

  曾幾何時,Java圖像處理已經被認為是太過雞肋,就連Java的創始公司,在java圖像處理方面也是淺嘗輒止,可能相比較C++,Java在這方面的處理,確實差強人意。

不過Java類庫中有一個叫JAI的庫,全程是java advanced image—Java高級預想處理,其實個人覺得這個庫非常豐富,奈何JAI只發行了2個版本就停止維護了,

到現在也沒有找到源碼,資料更是少的可憐。鑒於上述原因,本人將開始記錄JAI以及Java 二維圖像相關知識,本文主要介紹Java圖像的主要類以及圖像基礎知識。

  Java中我們對圖像的操縱一般使用BufferImage,比如我們一般會使用一下方法,將一個圖片文件讀取到bufferimage

BufferedImage image = ImageIO.read(new File("D:\\Gis開發\\數據\\影像數據\\tiff\\china.tif"));

        這個是將已經存在的圖片文件,讀取到BufferedImage中,其實大家都知道圖像之所以會呈現出各種顏色,無外乎就是像素值、顏色模型、樣本模型這三個重要參數來體現的。其實Java的圖像類

BufferedImage也無外乎這三個重要參數。
比如我們通過BufferedImage構造參數就可以發現,其中一個構造參數是
public BufferedImage (ColorModel cm,
                      WritableRaster raster,
                      boolean isRasterPremultiplied,
                      Hashtable<?,?> properties)

 這個構造參數中,ColorModel是顏色模型,raster是柵格數據,它是由像素值和樣本模型構成的,這個我們可以通過它的構造函數看到

protected WritableRaster(SampleModel sampleModel,
                         DataBuffer dataBuffer,
                         Point origin) 

其中sampleModel是樣例模型,dataBuffer是像素值數組。

下面介紹以上三個要素:

  • ColorModel

Java中ColorModel的實現類主要有ComponentColorModel、IndexColorModel等,我們先來看看ComponentColorModel構造函數

public ComponentColorModel (ColorSpace colorSpace,
                                boolean hasAlpha,
                                boolean isAlphaPremultiplied,
                                int transparency,
                                int transferType) 

其中colorSpace很重要,顏色空間其實決定了最終這些像素值是如何呈現在我們硬件上的,比如我們的電腦顯示器,印刷等。

比如常見以下顏色空間:

RGB彩色模型
@Native public static final int TYPE_RGB = 5; 
灰度模型
@Native public static final int TYPE_GRAY = 6
CMYK彩色模型
@Native public static final int TYPE_CMYK = 9;

比如我們通常的彩色圖是用 TYPE_RGB,創建灰度圖用TYPE_GRAY,以及TYPE_CMYK顏色模型。

hasAlpha:是否有透明通道,比如常見的png(32),就有alpha通道  

isAlphaPremultiplied:  這是處理透明的一個參數(相對深奧,後面會詳細研究)

transparency:  透明類型    其中1表示完全不透明  2表示完全透明或不透明  3表示介於兩者之間,也就是透明度可調(一般選擇3)

transferType:   就是像素的數據類型,跟下面的dataType我認為是一樣的

  • SampleModel
Java中sampleModel的實現類主要有ComponentSampleModel以及它的子類PixelInterleavedSampleModel,我們來看看它的構造函數
public ComponentSampleModel(int dataType,
                                int w, int h,
                                int pixelStride,
                                int scanlineStride,int bandOffsets[])

其中datatype:數據類型,即就是像素值的表示單位,比如常見的RGB三通道,使用TYPE_BYTE來表示,即就是每個通道8位,用0-255來表示,常見的DEM地形數據,也會直接使用TYPE_SHORT或者TYPE_FLOAT來定義。

w:  圖片寬度 

h:  圖片高度

pixelStride:像素步幅,其實就是我們的像素在寬和高方向的間隔設置,通常設置為1,表示每個像素都會填充一個值,如果設置為2,則表示每2個位置設置一個像素值,這樣其中的databuffer數組就會相應的縮減。

scanlineStride:  線性步幅,如果pixelStride為1,則scanlineStride為width。

bandOffsets:波段偏移量,一般都是0,比如RGB數據,一般都是new int[] {0,0,0}

  • 像素值

像素值其實就是表示顏色的數字,這裡提示一點,比如RGB數據,如果數據類型是TYPE_BYTE,如果圖片是10×10大小,則這個DataBufferByte的數組大小就是10*10*3。

下面我們來自定義一個圖片:

byte[] rasterBuffer = new byte[10*10*3];
DataBufferByte dataBuffer = new DataBufferByte(rasterBuffer, 10*10*3);
ColorSpace space = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ComponentSampleModel sampleModle = new ComponentSampleModel(DataBuffer.TYPE_BYTE,10, 10, 1, 10*3, new int[] {0,1,2});
int[] bits = {8,8,8};
ComponentColorModel colorModel = new ComponentColorModel(space, bits, false, false, Transparency.TRANSLUCENT,DataBuffer.TYPE_BYTE);
WritableRaster raster = Raster.createWritableRaster(sampleModle,dataBuffer,new Point(0,0));
BufferedImage image = new BufferedImage(colorModel,raster,false,null); 

 其實在日常開發中,我們經常會遇到ComponentColorModel,但是偶爾也會遇到IndexColorModel,這兩個顏色模型有什麼區別呢?

 自己找了一個這樣的數據,解析後發現,如下操作

從這個輸出可以看出,圖像的是IndexColorModel和普通ComponentColorModel是一樣的,唯一不同的是rgb的數組大小不一樣,下面我們來看看對應的samplemodel

 可以看出雖然colorModel有3通道,但實際samplemodel的band只有一個,也就是說實際只有一個samplebands

這也就解是了原本按照三通道的樣本,該數據的databuffer應該是7162*5968*3    而實際它的databuffer的size只有

7162*5968,也就是上圖中的data的size大小,這跟我們平時看到的ComponentColorModel的data不一樣,也就是用一位byte就表示了三個通道的顏色分量。

 擴展

 一般對於圖像顯示而言,我們拿到每個通道的顏色分量,其實還是需要轉為顯示器等輸出設備可以識別的,這個我們可以通過ColorModel的getRGB()方法,我們來看下這個方法

public int getRGB(int pixel) {
if (numComponents > 1) {
throw new
IllegalArgumentException("More than one component per pixel");
}
if (signed) {
throw new
IllegalArgumentException("Component value is signed");
}

return (getAlpha(pixel) << 24)
| (getRed(pixel) << 16)
| (getGreen(pixel) << 8)
| (getBlue(pixel) << 0);
}

 可以看出是通過三通道的分量的位運算獲得最後的像素值,而IndexColorModel的getRGB()我們來看下

final public int getRGB(int pixel) {
        return rgb[pixel & pixel_mask];
    }

明顯只有一個像素來計算最後的整數像素值。

 

總結

      好了今天就簡單介紹Java圖像處理的基礎操作,後續還將持續介紹一些實用的圖像處理方法,下一篇將介紹Java中圖像的放射變換,歡迎大家持續關注。