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中圖像的放射變換,歡迎大家持續關注。