前言
上次面試被問到圖片壓縮方法咖气,沒有回答上來挨措,只依稀記得有質(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