因為做個遊戲,需要一些圖檔資源,而擷取到的圖檔資源都是jpg格式的,不是透明的,例如下面樣式的:

為了取出其中的藍光部分,透明化黑色背景,我開始了圖檔處理探索之路。
這篇文章的内容包含以下部分:
1.RGB是什麼?
2.擷取圖檔指定位置的RGB值
3.圖檔的灰化處理
4.修改圖檔的透明通道alpha
————————————————————————————————————————————————————————————————————
RGB是什麼?
這裡簡單說一下,RGB即是red,green,blue的縮寫,即紅綠藍三種顔色。可以通過這三種顔色按一定比例混合,可以形成任何顔色。
圖檔是由許許多多個像素組成,每一像素是一種顔色,有rgb按一定比例混合而成,平常rbg的取值範圍為0~255,當red,green,blue的值都為0時,這個像素的顔色就為黑色,都為255時就是白色,當他們的值相同時,混合色變相為灰色。是以,一個像素點可以表示的顔色的個數為 255 * 255 * 255個,是非常多的。
這裡不多做介紹,百科還是很詳細的。
擷取圖檔指定位置的RGB值
使用java擷取一個像素的RGB,需要使用BufferedImage這個類,這個類提供修改圖檔資料的方法。
1
4 public voidgetImagePixel(String image) {5
6 int[] rgb = new int[3];7 File file = newFile(image);8 BufferedImage bi = null;9 try{10 bi =ImageIO.read(file);11 } catch(IOException e) {12
13 e.printStackTrace();14 }15 int width =bi.getWidth();16 int height =bi.getHeight();17 int minX =bi.getMinX();18 int minY =bi.getMinY();19 for(int y = minY; y < height; y++) {20 for(int x = minX; x < width; x++) {21 //擷取包含這個像素的顔色資訊的值, int型
22 int pixel =bi.getRGB(x, y);23 //從pixel中擷取rgb的值
24 rgb[0] = (pixel & 0xff0000) >> 16; //r
25 rgb[1] = (pixel & 0xff00) >> 8; //g
26 rgb[2] = (pixel & 0xff); //b
27 System.out.print("("+rgb[0] + "," + rgb[1] + "," + rgb[2] + ")");28 }29 System.out.println();30 }31
32 }
表示一個像素的顔色資料的格式有很多,不過常見格式就是4byte形式,即32位資料。看下圖
如果支援alpha通道,則最高的8位表示alpha的值,剩下的分别表示r,g,b的值,分别8位。還有其他的資料格式,這裡不做介紹啦。
現在應該可以看懂上邊的代碼了吧。
rgb[0]即是r的值,在24~16之間,是以pixel取此區間的值,再右移16位就取得了其值。g,b的值同理。
圖檔的灰化處理
圖檔的灰化處理,很常用,在圖檔識别之前,最常用。圖像灰化,直覺上看就是把多彩的圖像黑白化,本來用r,g,b三個值來表示一個pixel的顔色,現在用一個值來表示pixel的顔色,這樣,檢測圖檔中圖形的邊界就友善許多啦。現在,介紹幾種灰化方法。
1,以r,g,b中的其中一個值,作為灰階值
2. 以r,g,b中的最大值或最小值,作為灰階值
3.以r,g,b的平均值作為灰階值
4.以rgb的權重值作為灰階值
5.用java本身的灰化類型
下面寫個以r值為灰階值的代碼:
public voidtransformGray_R(String imagePath, String path) {
BufferedImage image;try{
image= ImageIO.read(newFile(imagePath));for(int y = image.getMinY(); y < image.getHeight(); y++) {for(int x = image.getMinX(); x < image.getWidth(); x ++) {int pixel =image.getRGB(x, y);int r = (pixel >> 16) & 0x00ff;
pixel= (r & 0x000000ff) | (pixel & 0xffffff00); //用r的值設定b的值
pixel = ((r<<8) & 0x0000ff00) | (pixel & 0xffff00ff);//用r的值設定g的值
image.setRGB(x, y, pixel);
}
}try{
ImageIO.write(image,"jpg", newFile(path));
}catch(IOException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}catch(IOException e1) {//TODO Auto-generated catch block
e1.printStackTrace();
}
}
看一下效果:
效果還可以,與ps的紅色通道圖檔一模一樣啊。
2,3方法就不示範啦。2方法還是很好使的,特别是處理像開頭那種顔色單一的圖檔時。
下面說一下4方法
//權重法
public voidtransformGrayJiaQuan (String imagePath, String path) {try{
BufferedImage image= ImageIO.read(newFile(imagePath));int width =image.getWidth();int height =image.getHeight();for(int y = image.getMinY(); y < height; y++) {for(int x = image.getMinX(); x < width ; x ++) {int pixel =image.getRGB(x, y);int r = (pixel >> 16) & 0xff;int g = (pixel >> 8) & 0xff;int b = pixel & 0xff;//權重法的核心,權重法是用圖檔的亮度作為灰階值的
int grayValue = (int) (0.30f * r + 0.59f * g + 0.11f *b );//int grayValue = (int) (0.21f * 4 + 0.71f * g + 0.08f * b);//還可以使用這個系數的權重法
pixel = (grayValue << 16) & 0x00ff0000 | (pixel & 0xff00ffff);
pixel= (grayValue << 8) & 0x0000ff00 | (pixel & 0xffff00ff);
pixel= (grayValue) & 0x000000ff | (pixel & 0xffffff00);
image.setRGB(x, y, pixel);
}
}
ImageIO.write(image,"jpg", newFile(path));
}catch(IOException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}
效果:
相對于r值灰化的效果,清晰了好多,但亮度明顯小了。
第5種方法:
public voidtransformGray(String imagePath, String path) {
File file= newFile(imagePath);try{
BufferedImage image=ImageIO.read(file);int width =image.getWidth();int height =image.getHeight();
BufferedImage grayImage= new BufferedImage(width, height,BufferedImage.TYPE_BYTE_GRAY);//這裡的圖像類型換了
for(int y = image.getMinY(); y < height; y++) {for(int x = image.getMinX(); x < width; x++) {int rgb =image.getRGB(x, y);
grayImage.setRGB(x, y, rgb);
}
}
File newFile= newFile(path);
ImageIO.write(grayImage,"jpg", newFile);
}catch(IOException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}
看效果
可以下載下傳下來自己比對一番
修改圖檔的透明通道alpha
上面說了那麼多,是為了我的終極目的,就是把黑色的背景透明化,保留我想要的顔色。
alpha的值也是在0~255之間,當alpha為0時,則圖檔完全透明,為255時不透明,是以其值越小越透明,由此可知,保留想要的顔色,透明掉不想要的顔色是很簡單的。
處理方法和灰化方法一樣,這次修改的是alpha值。通用的方法是權重法,也可以根據圖檔顔色的類型選擇合适的方法。
下面代碼以權重值為alpha值,權重值得到的圖檔的亮度,是以,越黑的地方,亮度越小,其alpha值最小,越透明。是以,此方法是可行的。
public voidclearBackground(String imagePath, String dstPath) {
ImageIcon icon= newImageIcon(imagePath);
Image srcImage=icon.getImage();
BufferedImage dstImage= newBufferedImage(srcImage.getWidth(icon.getImageObserver()), srcImage.getHeight(icon.getImageObserver()), BufferedImage.TYPE_4BYTE_ABGR);
Graphics gr=dstImage.getGraphics();
gr.drawImage(srcImage,0,0,icon.getImageObserver());int height =dstImage.getHeight();int width =dstImage.getWidth();for(int y = dstImage.getMinY(); y < height; y++) {for(int x = dstImage.getMinX(); x < width; x++) {int pixel =dstImage.getRGB(x, y);int r = (pixel & 0x00ff0000) >> 16;int g = (pixel >> 8) & 0xff;int b = pixel & 0xff;int liangDu = (int)(0.21f * 4 + 0.71f * g + 0.08f * b);//擷取亮度//以亮度作為alpha值
pixel = (liangDu << 24) & 0xff000000 | (pixel & 0x00ffffff); //alpha值在24~32
dstImage.setRGB(x, y, pixel);
}
}try{
ImageIO.write(dstImage,"png", newFile(dstPath));//不要忘記更改圖檔格式為png格式,jpg不支援透明
}catch(IOException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
}
看第一張圖處理後的效果:
其實這張圖以b值為基準,處理效果更好,但權重法更常用。
————————————————————————————————————————————————————————————————————————————
這幾天處理圖檔擷取到知識,在此總結一下。