1. 程式人生 > >Android效能優化之圖片壓縮優化

Android效能優化之圖片壓縮優化

1 分類
Android圖片壓縮結合多種壓縮方式,常用的有尺寸壓縮、質量壓縮、取樣率壓縮以及通過JNI呼叫libjpeg庫來進行壓縮。
參考此方法:Android-BitherCompress

備註:對於資源圖片直接使用:tiny壓縮

2 質量壓縮
(1)原理:保持畫素的前提下改變圖片的位深及透明度,(即:通過演算法摳掉(同化)了圖片中的一些某個些點附近相近的畫素),達到降低質量壓縮檔案大小的目的。

注意:它其實只能實現對file的影響,對載入這個圖片出來的bitmap記憶體是無法節省的,還是那麼大。因為bitmap在記憶體中的大小是按照畫素計算的,也就是width*height,對於質量壓縮,並不會改變圖片的真實的畫素(畫素大小不會變)。

(2)使用場景:將圖片壓縮後將圖片上傳到伺服器,或者儲存到本地。根據實際需求來。

(3)原始碼示例
---------------------
作者:陳李冠
來源:CSDN
原文:https://blog.csdn.net/chenliguan/article/details/54409442
版權宣告:本文為博主原創文章,轉載請附上博文連結!

/**
     * 3.質量壓縮
     * 設定bitmap options屬性,降低圖片的質量,畫素不會減少
     * 第一個引數為需要壓縮的bitmap圖片物件,第二個引數為壓縮後圖片儲存的位置
     * 設定options 屬性0-100,來實現壓縮
     *
     * @param bmp
     * @param file
     
*/ public static void qualityCompress(Bitmap bmp, File file) { // 0-100 100為不壓縮 int quality = 20; ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 把壓縮後的資料存放到baos中 bmp.compress(Bitmap.CompressFormat.JPEG, quality, baos); try { FileOutputStream fos
= new FileOutputStream(file); fos.write(baos.toByteArray()); fos.flush(); fos.close(); } catch (Exception e) { e.printStackTrace(); } }

3 尺寸壓縮

(1)原理:通過減少單位尺寸的畫素值,正真意義上的降低畫素。1020*8880–

(2)使用場景:快取縮圖的時候(頭像處理)

(3)原始碼示例

/**
     * 4.尺寸壓縮(通過縮放圖片畫素來減少圖片佔用記憶體大小)
     *
     * @param bmp
     * @param file
     */

    public static void sizeCompress(Bitmap bmp, File file) {
        // 尺寸壓縮倍數,值越大,圖片尺寸越小
        int ratio = 8;
        // 壓縮Bitmap到對應尺寸
        Bitmap result = Bitmap.createBitmap(bmp.getWidth() / ratio, bmp.getHeight() / ratio, Config.ARGB_8888);
        Canvas canvas = new Canvas(result);
        Rect rect = new Rect(0, 0, bmp.getWidth() / ratio, bmp.getHeight() / ratio);
        canvas.drawBitmap(bmp, null, rect, null);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 把壓縮後的資料存放到baos中
        result.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(baos.toByteArray());
            fos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

4 取樣率壓縮
(1)原理:設定圖片的取樣率,降低圖片畫素

(2) 好處:是不會先將大圖片讀入記憶體,大大減少了記憶體的使用,也不必考慮將大圖片讀入記憶體後的釋放事宜。

(3)問題:因為取樣率是整數,所以不能很好的保證圖片的質量。如我們需要的是在2和3取樣率之間,用2的話圖片就大了一點,但是用3的話圖片質量就會有很明顯的下降,這樣也無法完全滿足我的需要。

(4)原始碼示例

/**
     * 5.取樣率壓縮(設定圖片的取樣率,降低圖片畫素)
     *
     * @param filePath
     * @param file
     */
    public static void samplingRateCompress(String filePath, File file) {
        // 數值越高,圖片畫素越低
        int inSampleSize = 8;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = false;
//          options.inJustDecodeBounds = true;//為true的時候不會真正載入圖片,而是得到圖片的寬高資訊。
        //取樣率
        options.inSampleSize = inSampleSize;
        Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 把壓縮後的資料存放到baos中
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        try {
            if (file.exists()) {
                file.delete();
            } else {
                file.createNewFile();
            }
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(baos.toByteArray());
            fos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

5 JNI終極壓縮
5.1 Android影象處理引擎的缺漏
為什麼IOS拍照1M的圖片要比安卓拍照排出來的5M的圖片還要清晰。都是在同一個環境下,儲存的都是JPEG?

(1)歷程
95年 JPEG處理引擎,用於最初的在PC上面處理圖片的引擎。
05年 skia開源的引擎, 開發了一套基於JPEG處理引擎的第二次開發。便於瀏覽器的使用。
07年安卓用的skia引擎(閹割版),谷歌拿了skia,去掉一個編碼演算法—哈夫曼演算法。採用定長編碼演算法。但是解碼還是保留了哈夫曼演算法,導致了圖片處理後文件變大了。
(2)原因
當時由於CPU和記憶體在手機上都非常吃緊 效能差,由於哈夫曼演算法非常吃CPU,被迫用了其他的演算法。
(3)優化方案
繞過安卓Bitmap API層,來自己編碼實現—-修復使用哈夫曼演算法。

5.2 哈夫曼演算法
哈夫曼樹詳解點選—資料結構與演算法之二叉樹+遍歷+哈夫曼樹

(1)ARGB:一個畫素點包涵四個資訊:alpha,red,green,blue
(2)如何得到每一個字母出現的權重?需要去掃描整個資訊(圖片資訊–每一個畫素包括ARGB),要大量計算,很耗CPU,1280*800畫素*4。
(2)

5.3 JNI開發步驟(大概步驟,具體實現未定)
(1)準備工作

1)http://www.ijg.org/下載JPEG引擎使用的庫---libjpeg庫,
基於該引擎來做一定的開發----自己實現編碼,JNI開發。
(2)匯入庫檔案libjpegbither.so
(3)匯入標頭檔案
(4)寫mk檔案——Android.mk、Applicatoin.mk
(5)寫程式碼——C++:XX.cpp、C:XX.c

(2)開發過程

1)將android的bitmap解碼,並轉換成RGB資料
一個圖片資訊---畫素點(argb),alpha去掉
(2)JPEG物件分配空間以及初始化
(3)指定壓縮資料來源
(4)獲取檔案資訊
(5)為壓縮設定引數,比如影象大小、型別、顏色空間
  boolean arith_code;       
  /* TRUE=arithmetic coding, FALSE=Huffman */6)開始壓縮——jpeg_start_compress()
(7)壓縮結束——jpeg_finish_compress()
(8)釋放資源

5.4 原始碼示例

/**
     * 1.JNI終極壓縮(通過JNI圖片壓縮把Bitmap儲存到指定目錄)
     *
     * @param image    bitmap物件
     * @param filePath 要儲存的指定目錄
     * @Description: 通過JNI圖片壓縮把Bitmap儲存到指定目錄
     */
    public static void jniUltimateCompress(Bitmap image, String filePath) {
        // 質量壓縮方法,這裡100表示不壓縮,把壓縮後的資料存放到baos中
        int quality = 20;
        // JNI呼叫儲存圖片到SD卡 這個關鍵
        NativeUtil.saveBitmap(image, quality, filePath, true);
    }

    /**
     * 1.JNI基本壓縮(不儲存Bitmap)
     *
     * @param bit      bitmap物件
     * @param fileName 指定儲存目錄名
     * @param optimize 是否採用哈弗曼表資料計算 品質相差5-10倍
     * @Description: JNI基本壓縮
     */
    public static void jniBasicCompress(Bitmap bit, String fileName, boolean optimize) {
        saveBitmap(bit, DEFAULT_QUALITY, fileName, optimize);
    }

/**
     * 呼叫native方法
     *
     * @param bit
     * @param quality
     * @param fileName
     * @param optimize
     * @Description:函式描述
     */
    private static void saveBitmap(Bitmap bit, int quality, String fileName, boolean optimize) {
        compressBitmap(bit, bit.getWidth(), bit.getHeight(), quality, fileName.getBytes(), optimize);
    }

    /**
     * 呼叫底層 bitherlibjni.c中的方法
     *
     * @param bit
     * @param w
     * @param h
     * @param quality
     * @param fileNameBytes
     * @param optimize
     * @return
     * @Description:函式描述
     */
    private static native String compressBitmap(Bitmap bit, int w, int h, int quality, byte[] fileNameBytes,
                                                boolean optimize);

    /**
     * 載入lib下兩個so檔案
     */
    static {
        System.loadLibrary("jpegbither");
        System.loadLibrary("bitherjni");
    }

6 混合終極方法

(1)原理:三種方式結合使用實現指定圖片記憶體大小,清晰度達到最優。

(2)使用場景:大圖壓縮,同時對圖片質量要求較高。

(3)原始碼示例

/**
     * 2.混合終極方法(尺寸、質量、JNI壓縮)
     *
     * @param image    bitmap物件
     * @param filePath 要儲存的指定目錄
     * @Description: 通過JNI圖片壓縮把Bitmap儲存到指定目錄
     */
    public static void mixCompress(Bitmap image, String filePath) {
        // 最大圖片大小 1000KB
        int maxSize = 1000;
        // 獲取尺寸壓縮倍數
        int ratio = NativeUtil.getRatioSize(image.getWidth(), image.getHeight());
        // 壓縮Bitmap到對應尺寸
        Bitmap result = Bitmap.createBitmap(image.getWidth() / ratio, image.getHeight() / ratio, Config.ARGB_8888);
        Canvas canvas = new Canvas(result);
        Rect rect = new Rect(0, 0, image.getWidth() / ratio, image.getHeight() / ratio);
        canvas.drawBitmap(image, null, rect, null);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 質量壓縮方法,這裡100表示不壓縮,把壓縮後的資料存放到baos中
        int quality = 100;
        result.compress(Bitmap.CompressFormat.JPEG, quality, baos);
        // 迴圈判斷如果壓縮後圖片是否大於最大值,大於繼續壓縮
        while (baos.toByteArray().length / 1024 > maxSize) {
            // 重置baos即清空baos
            baos.reset();
            // 每次都減少10
            quality -= 10;
            // 這裡壓縮options%,把壓縮後的資料存放到baos中
            result.compress(Bitmap.CompressFormat.JPEG, quality, baos);
        }
        // JNI呼叫儲存圖片到SD卡 這個關鍵
        NativeUtil.saveBitmap(result, quality, filePath, true);
        // 釋放Bitmap
        if (result != null && !result.isRecycled()) {
            result.recycle();
            result = null;
        }
    }

    /**
     * 計算縮放比
     *
     * @param bitWidth  當前圖片寬度
     * @param bitHeight 當前圖片高度
     * @return
     * @Description:函式描述
     */
    public static int getRatioSize(int bitWidth, int bitHeight) {
        // 圖片最大解析度
        int imageHeight = 1920;
        int imageWidth = 1080;
        // 縮放比
        int ratio = 1;
        // 縮放比,由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可
        if (bitWidth > bitHeight && bitWidth > imageHeight) {
            // 如果圖片寬度比高度大,以寬度為基準
            ratio = bitWidth / imageHeight;
        } else if (bitWidth < bitHeight && bitHeight > imageHeight) {
            // 如果圖片高度比寬度大,以高度為基準
            ratio = bitHeight / imageHeight;
        }
        // 最小比率為1
        if (ratio <= 0)
            ratio = 1;
        return ratio;
    }

7 多種方法對比
7.1 當圖片記憶體大於1MB
(1)原圖記憶體是3.22MB
(2)截圖如下:

(3)效果:
尺寸壓縮後圖片太模糊;
混合終極方法壓縮效果更佳,與jni終極方法壓縮記憶體區別不大;
質量壓縮後圖片與jni終極方法壓縮後圖片效果接近,略顯模糊;
(4)總結:可以考慮使用混合終極方法。

7.2 當圖片記憶體小於1MB
(1)原圖記憶體是109.94KB
(2)截圖如下:

(3)效果:

尺寸壓縮後圖片太模糊;
混合終極方法壓縮後記憶體反而增加了一半;
質量壓縮後圖片和jni終極方法壓縮後圖片效果接近,但是記憶體更大;
jni終極方法壓縮後圖片效果與原圖相差不大,記憶體也不大。

(4)總結:可以考慮使用jni終極方法。

8 參考連結

Android圖片壓縮(質量壓縮和尺寸壓縮)&Bitmap轉成字串上傳