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終極方法。