Android 圖片壓縮

前言

上次面試被問到圖片壓縮方法咖气,沒有回答上來挨措,只依稀記得有質(zhì)量壓縮、尺寸壓縮崩溪,卻說不出個所以然來浅役。所以今天研究了一下,Android的圖片壓縮方式伶唯,經(jīng)過百度和查看谷歌文檔觉既,暫時知道了一共有質(zhì)量壓縮、尺寸壓縮(或者說裁剪)乳幸、采樣率壓縮瞪讼、設(shè)置圖片格式、通過JIN調(diào)用libjpeg庫壓縮

1.質(zhì)量壓縮

保持像素的前提下改變圖片的位深及透明度(即:通過算法抹掉(同化)圖片中的一些某點附近 相近的像素)達(dá)到降低質(zhì)量壓縮文件的目的反惕。 使用場景:將圖片壓縮后將圖片上傳到服務(wù)器尝艘,或者保存到本地演侯,根據(jù)實際需求

    /**
     * 質(zhì)量壓縮
     * 設(shè)置bitmap options屬性姿染,降低圖片的質(zhì)量,像素不會減少
     * 第一個參數(shù)為需要壓縮的bitmap圖片對象秒际,第二個參數(shù)為壓縮后圖片保存的位置
     * 設(shè)置options 屬性0-100悬赏,來實現(xiàn)壓縮(因為png是無損壓縮,所以該屬性對png是無效的)
     *
     * @param bmp
     * @param file
     */
    public static void qualityCompress(Bitmap bmp, File file) {
        // 0-100 100為不壓縮
        int quality = 20;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 把壓縮后的數(shù)據(jù)存放到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();
        }
    }

復(fù)制代碼

2.尺寸壓縮

通過減少單位尺寸的像素值娄徊,真正意義上的降低像素(通過縮放圖片像素來減少圖片占用內(nèi)存大忻銎摹) 使用場景:緩存縮略圖的時候(頭像處理)

    /**
     * 尺寸壓縮(通過縮放圖片像素來減少圖片占用內(nèi)存大小)
     *
     * @param bmp
     * @param file
     */

    public static void sizeCompress(Bitmap bmp, File file) {
        // 尺寸壓縮倍數(shù),值越大寄锐,圖片尺寸越小
        int ratio = 8;
        // 壓縮Bitmap到對應(yīng)尺寸
        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();
        // 把壓縮后的數(shù)據(jù)存放到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();
        }
    }
復(fù)制代碼

3.采樣率壓縮

設(shè)置圖片的采樣率兵多,降低圖片像素 好處:是不會先將大圖片讀入內(nèi)存,大大減少了內(nèi)存的使用橄仆,也不必考慮將大圖片讀入內(nèi)存后的釋放事宜剩膘。 問題:因為采樣率是整數(shù),所以不能很好的保證圖片的質(zhì)量盆顾。如我們需要的是在2和3采樣率之間怠褐,用2的話圖片就大了一點,但是用3的話圖片質(zhì)量就會有很明顯的下降您宪,這樣也無法完全滿足我的需要奈懒。 GitHub上有名的圖片壓縮算法采用的就是采樣率壓縮奠涌,他的核心算法就是計算這個采樣值

 private int computeSize() {
    srcWidth = srcWidth % 2 == 1 ? srcWidth + 1 : srcWidth;
    srcHeight = srcHeight % 2 == 1 ? srcHeight + 1 : srcHeight;

    int longSide = Math.max(srcWidth, srcHeight);
    int shortSide = Math.min(srcWidth, srcHeight);

    float scale = ((float) shortSide / longSide);
    if (scale <= 1 && scale > 0.5625) {
      if (longSide < 1664) {
        return 1;
      } else if (longSide < 4990) {
        return 2;
      } else if (longSide > 4990 && longSide < 10240) {
        return 4;
      } else {
        return longSide / 1280 == 0 ? 1 : longSide / 1280;
      }
    } else if (scale <= 0.5625 && scale > 0.5) {
      return longSide / 1280 == 0 ? 1 : longSide / 1280;
    } else {
      return (int) Math.ceil(longSide / (1280.0 / scale));
    }
  }
復(fù)制代碼

魯班框架的流程大致是這樣的:1.通過建造者模式傳入圖片的地址,或者圖片的uri磷杏,或者圖片流 2.把圖片轉(zhuǎn)為流溜畅,添加到一個list 3.判斷圖片格式,是否需要旋轉(zhuǎn)等 4.計算采樣率也就是options.inSampleSize = computeSize(); 5.壓縮

 File compress() throws IOException {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = computeSize();

    Bitmap tagBitmap = BitmapFactory.decodeStream(srcImg.open(), null, options);
    ByteArrayOutputStream stream = new ByteArrayOutputStream();

    if (Checker.SINGLE.isJPG(srcImg.open())) {
      tagBitmap = rotatingImage(tagBitmap, Checker.SINGLE.getOrientation(srcImg.open()));
    }
    tagBitmap.compress(focusAlpha ? Bitmap.CompressFormat.PNG : Bitmap.CompressFormat.JPEG, 60, stream);
    tagBitmap.recycle();

    FileOutputStream fos = new FileOutputStream(tagImg);
    fos.write(stream.toByteArray());
    fos.flush();
    fos.close();
    stream.close();

    return tagImg;
  }
復(fù)制代碼

4.通過JIN調(diào)用libjpeg庫壓縮

Android圖像處理引擎的漏洞 通常IOS拍的照片1M左右還比Android拍出來的照片5M的還要清晰极祸,都是在同一個環(huán)境下达皿,保存的都是JPEG

  • (1)歷程 1995年 JPEG處理引擎,用于最初的在PC上面處理圖片的引擎贿肩。 2005年 skia開源的引擎, 開發(fā)了一套基于JPEG處理引擎的第二次開發(fā)峦椰。便于瀏覽器的使用。 2007年安卓用的skia引擎(閹割版)汰规,谷歌拿了skia汤功,去掉一個編碼算法—哈夫曼算法。采用定長編碼算法溜哮。但是解碼還是保留了哈夫曼算法滔金,導(dǎo)致了圖片處理后文件變大了。
  • (2)原因 當(dāng)時由于CPU和內(nèi)存在手機上都非常吃緊 性能差茂嗓,由于哈夫曼算法非常吃CPU餐茵,被迫用了其他的算法。
  • (3)優(yōu)化方案 繞過安卓Bitmap API層述吸,來自己編碼實現(xiàn)—-修復(fù)使用哈夫曼算法忿族。

JNI開發(fā)步驟

  • 準(zhǔn)備工作
(1)http://www.ijg.org/下載JPEG引擎使用的庫---libjpeg庫,
基于該引擎來做一定的開發(fā)----自己實現(xiàn)編碼蝌矛,JNI開發(fā)道批。
(2)導(dǎo)入庫文件libjpegbither.so
(3)導(dǎo)入頭文件
(4)寫mk文件——Android.mk、Applicatoin.mk
(5)寫代碼——C++:XX.cpp入撒、C:XX.c
復(fù)制代碼

  • 開發(fā)過程
(1)將android的bitmap解碼隆豹,并轉(zhuǎn)換成RGB數(shù)據(jù)
   一個圖片信息---像素點(argb),alpha去掉
(2)JPEG對象分配空間以及初始化
(3)指定壓縮數(shù)據(jù)源
(4)獲取文件信息
(5)為壓縮設(shè)置參數(shù)茅逮,比如圖像大小璃赡、類型、顏色空間
   boolean arith_code;       
  /* TRUE=arithmetic coding, FALSE=Huffman */
(6)開始壓縮——jpeg_start_compress()
(7)壓縮結(jié)束——jpeg_finish_compress()
(8)釋放資源
復(fù)制代碼

  • 代碼
    /**
     * 1.JNI終極壓縮(通過JNI圖片壓縮把Bitmap保存到指定目錄)
     *
     * @param image    bitmap對象
     * @param filePath 要保存的指定目錄
     * @Description: 通過JNI圖片壓縮把Bitmap保存到指定目錄
     */
    public static void jniUltimateCompress(Bitmap image, String filePath) {
        // 質(zhì)量壓縮方法献雅,這里100表示不壓縮碉考,把壓縮后的數(shù)據(jù)存放到baos中
        int quality = 20;
        // JNI調(diào)用保存圖片到SD卡 這個關(guān)鍵
        NativeUtil.saveBitmap(image, quality, filePath, true);
    }

    /**
     * 1.JNI基本壓縮(不保存Bitmap)
     *
     * @param bit      bitmap對象
     * @param fileName 指定保存目錄名
     * @param optimize 是否采用哈弗曼表數(shù)據(jù)計算 品質(zhì)相差5-10倍
     * @Description: JNI基本壓縮
     */
    public static void jniBasicCompress(Bitmap bit, String fileName, boolean optimize) {
        saveBitmap(bit, DEFAULT_QUALITY, fileName, optimize);
    }

/**
     * 調(diào)用native方法
     *
     * @param bit
     * @param quality
     * @param fileName
     * @param optimize
     * @Description:函數(shù)描述
     */
    private static void saveBitmap(Bitmap bit, int quality, String fileName, boolean optimize) {
        compressBitmap(bit, bit.getWidth(), bit.getHeight(), quality, fileName.getBytes(), optimize);
    }

    /**
     * 調(diào)用底層 bitherlibjni.c中的方法
     *
     * @param bit
     * @param w
     * @param h
     * @param quality
     * @param fileNameBytes
     * @param optimize
     * @return
     * @Description:函數(shù)描述
     */
    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");
    }

復(fù)制代碼

5.圖片格式

Android目前常用的圖片格式有png,jpeg和webp惩琉,

png:無損壓縮圖片格式豆励,支持Alpha通道,Android切圖素材多采用此格式

jpeg:有損壓縮圖片格式,不支持背景透明良蒸,適用于照片等色彩豐富的大圖壓縮技扼,不適合logo

webp:是一種同時提供了有損壓縮和無損壓縮的圖片格式,派生自視頻編碼格式VP8嫩痰,從 谷歌官網(wǎng) 來看剿吻,無損webp平均比png小26%,有損的webp平均比jpeg小25%~34%串纺,無損webp支持Alpha通道丽旅,有損webp在一定的條件下同樣支持,有損webp在Android4.0(API 14)之后支持纺棺,無損和透明在Android4.3(API18)之后支持

采用webp能夠在保持圖片清晰度的情況下榄笙,可以有效減小圖片所占有的磁盤空間大小

本文非原創(chuàng), 原文鏈接: http://www.reibang.com/p/1f69238b8874

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市祷蝌,隨后出現(xiàn)的幾起案子茅撞,更是在濱河造成了極大的恐慌,老刑警劉巖巨朦,帶你破解...
    沈念sama閱讀 212,029評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件米丘,死亡現(xiàn)場離奇詭異,居然都是意外死亡糊啡,警方通過查閱死者的電腦和手機拄查,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棚蓄,“玉大人堕扶,你說我怎么就攤上這事⊙⑴保” “怎么了挣柬?”我有些...
    開封第一講書人閱讀 157,570評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長睛挚。 經(jīng)常有香客問我,道長急黎,這世上最難降的妖魔是什么扎狱? 我笑而不...
    開封第一講書人閱讀 56,535評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮勃教,結(jié)果婚禮上淤击,老公的妹妹穿的比我還像新娘。我一直安慰自己故源,他們只是感情好污抬,可當(dāng)我...
    茶點故事閱讀 65,650評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般印机。 火紅的嫁衣襯著肌膚如雪矢腻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,850評論 1 290
  • 那天射赛,我揣著相機與錄音多柑,去河邊找鬼。 笑死楣责,一個胖子當(dāng)著我的面吹牛竣灌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播秆麸,決...
    沈念sama閱讀 39,006評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼初嘹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了沮趣?” 一聲冷哼從身側(cè)響起削樊,我...
    開封第一講書人閱讀 37,747評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎兔毒,沒想到半個月后漫贞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,207評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡育叁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,536評論 2 327
  • 正文 我和宋清朗相戀三年迅脐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片豪嗽。...
    茶點故事閱讀 38,683評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡谴蔑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出龟梦,到底是詐尸還是另有隱情隐锭,我是刑警寧澤,帶...
    沈念sama閱讀 34,342評論 4 330
  • 正文 年R本政府宣布计贰,位于F島的核電站钦睡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏躁倒。R本人自食惡果不足惜荞怒,卻給世界環(huán)境...
    茶點故事閱讀 39,964評論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望秧秉。 院中可真熱鬧褐桌,春花似錦、人聲如沸象迎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至啦撮,卻和暖如春谭网,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背逻族。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評論 1 266
  • 我被黑心中介騙來泰國打工蜻底, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人聘鳞。 一個月前我還...
    沈念sama閱讀 46,401評論 2 360
  • 正文 我出身青樓薄辅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親抠璃。 傳聞我的和親對象是個殘疾皇子站楚,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,566評論 2 349

推薦閱讀更多精彩內(nèi)容