1. 程式人生 > >Android圖片壓縮(質量壓縮和尺寸壓縮)

Android圖片壓縮(質量壓縮和尺寸壓縮)

圖片有以下存在形式:

1.檔案形式(即以二進位制形式存在於硬碟上)
2.流的形式(即以二進位制形式存在於記憶體中)
3.Bitmap形式
這三種形式的區別: 檔案形式和流的形式對圖片體積大小並沒有影響,也就是說,如果你手機SD卡上的如果是100K,那麼通過流的形式讀到記憶體中,也一定是佔100K的記憶體,注意是流的形式,不是Bitmap的形式,當圖片以Bitmap的形式存在時,其佔用的記憶體會瞬間變大, 我試過500K檔案形式的圖片載入到記憶體,以Bitmap形式存在時,佔用記憶體將近10M,當然這個增大的倍數並不是固定的(原因在下面提到)。 檢測圖片三種形式大小的方法: 檔案形式: file.length()
流的形式: 講圖片檔案讀到記憶體輸入流中,看它的byte數 Bitmap:    bitmap.getByteCount()
一、質量壓縮
質量壓縮的特點是:  File形式的圖片確實被壓縮了, 但是當你重新讀取壓縮後的file為 Bitmap是,它佔用的記憶體並沒有改變
質量壓縮主要藉助Bitmap中的compress方法實現:
public boolean compress (Bitmap.CompressFormat format, int quality, OutputStream stream)
這個方法用來將特定格式的壓縮圖片寫入輸出流(OutputStream)中,當然例如輸出流與檔案聯絡在一起,壓縮後的圖片也就是一個檔案。如果壓縮成功則返回true,其中有三個引數:
  • format
    是壓縮後的圖片的格式,可取值:Bitmap.CompressFormat .JPEG、~.PNG、~.WEBP。
  • quality的取值範圍為[0,100],值越小,經過壓縮後圖片失真越嚴重,當然圖片檔案也會越小。(PNG格式的圖片會忽略這個值的設定)
  • stream指定壓縮的圖片輸出的地方,比如某檔案。
上述方法還有一個值得注意的地方是:當用BitmapFactory decode檔案時可能返回一個跟原圖片不同位深的圖片,或者丟失了每個畫素的透明值(alpha),比如說,JPEG格式的圖片僅僅支援不透明的畫素。文章android圖片壓縮在文末提到的下面這點可能就是這個原因:

當呼叫bitmap.compress(CompressFormat.JPEG, 100, fos);儲存為圖片時發現圖片背景為黑色,如下圖:


這時只需要改成用png儲存就可以了,bitmap.compress(CompressFormat.PNG, 100, fos);,如下圖:

下面是質量壓縮的程式碼:
  1. publicstaticvoid compressBmpToFile(Bitmap bmp,File file){  
  2.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  3.         int options = 80;//個人喜歡從80開始,
  4.         bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);  
  5.         while (baos.toByteArray().length / 1024 > 100) {   
  6.             baos.reset();  
  7.             options -= 10;  
  8.             bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);  
  9.         }  
  10.         try {  
  11.             FileOutputStream fos = new FileOutputStream(file);  
  12.             fos.write(baos.toByteArray());  
  13.             fos.flush();  
  14.             fos.close();  
  15.         } catch (Exception e) {  
  16.             e.printStackTrace();  
  17.         }  
  18.     }  

這段程式碼來自Android圖片壓縮總結,我根據自己的需求改了改,但是大同小異,所以就直接貼了。 隨著程式碼中的option逐漸變小,我們可以在logcat中列印baos的大小來檢視圖片的大小。我們也可以去掉while的迴圈條件,一直壓縮下去看效果,最終一張照片可能就由原來的3、4M變成了幾百K甚至幾百B了。我在試的過程中將option設定成100,壓縮後偶爾會出現一張3、4M的圖片經過壓縮後竟變成了6、7M,這裡還是有點困惑,不知道為什麼。 隨後,我想把這個壓縮後的圖片(1、200KB)填充到ImageView中時卻失敗了,logcat中提示圖片過大!這就是文章開頭提到的問題,雖然我們通過質量壓縮使File形式的圖片檔案縮小了,但是並沒有改變圖片的寬高,原先是1080*1920解析度的圖片經壓縮後還是1080*1920,而File格式轉換成Bitmap格式進入到記憶體中時,記憶體是根據圖片的畫素數量來給圖片分配記憶體大小的,還是有好幾M,因此填充ImageView失敗。 順便提一下,可以用bitmap.getByteCount()獲取儲存bitmap畫素的記憶體大小,但是KITKAT(Android 4.4版本)以後用getAllocateByteCount()獲取。一般情況下,後者返回值比前者大,比如,當bitmap被重用去decode另外更小的bitmaps時,或者被人為地配置一下屬性值,比如setWidth()、setHeight()、reconfigure()時,如果bitmap不做以上操作,二者的返回值應該是一樣的。(譯文,不太懂) 二、尺寸壓縮 特點: 通過設定取樣率, 減少圖片的畫素, 達到對記憶體中的Bitmap進行壓縮 我們主要通過BitmapFactory中的decodeFile方法對圖片進行尺寸壓縮:
public static Bitmap decodeFile (String pathName, BitmapFactory.Options opts)
其中有兩個引數:
  • pathName是圖片檔案的路徑。
  • opts 就是所謂的取樣率,它裡邊有很多屬性可以設定,我們通過設定屬性來達到根據自己的需要,壓縮出指定的圖片。其中比較常用的屬性有:
boolean inJustDecodeBounds —— 如果設定為true,則只讀取bitmap的寬高,而忽略內容。 int inSampleSize—— 如果>1,呼叫decodeFile方法後,就會得到一個更小的bitmap物件(已壓縮)。比如設定為2,那麼新Bitmap的寬高都會是原Bitmap寬高的1/2,總體大小自然就是原來的1/4了,以此類推。 boolean inPurgeable—— 如果設定為true,壓縮後的圖片畫素佔的記憶體將會在系統清理記憶體的時候被回收掉,當畫素的資訊再次被用到時將會自動重新decode該畫素(比如getPixels()時)。(慎用!重複decode可以會造成UI的卡頓,API level 21 已棄用) boolean inInputShareable—— 與inPurgeable配合使用,如果inPurgeable設定成false,自動忽略此值,如果inPurgeable為true,此值決定是否該bitmap能分享引用給輸入資料(inputstream,array等),或者必須進行深拷貝。API level 21 已棄用。(這是譯文,不太理解!!!) 下面是一段實現的程式碼
private Bitmap sizeCompres(String path, int rqsW, int rqsH) {
        // 用option設定返回的bitmap物件的一些屬性引數
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;// 設定僅讀取Bitmap的寬高而不讀取內容
        BitmapFactory.decodeFile(path, options);// 獲取到圖片的寬高,放在option裡邊
        final int height = options.outHeight;//圖片的高度放在option裡的outHeight屬性中
        final int width = options.outWidth;
        int inSampleSize = 1;
        if (rqsW == 0 || rqsH == 0) {
            options.inSampleSize = 1;
        } else if (height > rqsH || width > rqsW) {
            final int heightRatio = Math.round((float) height / (float) rqsH);
            final int widthRatio = Math.round((float) width / (float) rqsW);
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
            options.inSampleSize = inSampleSize;
        }
        return BitmapFactory.decodeFile(path, options);// 主要通過option裡的inSampleSize對原圖片進行按比例壓縮
    }
上面就是簡單的質量壓縮與尺寸壓縮。 參考文章: