Android客户端获取相册图片多张一起上传服务器时,如果不进行图片处理,可能会导致内存泄漏问题。这时图片压缩就至关重要了。但是又不能影响图片的清晰度。
一.质量压缩
详见:Bitmap详解(下)_jianning-wu的博客-CSDN博客
二.二分法压缩
1.思路
1.如果 传入图片字节大小,比要压缩的字节大小还小 直接返回不需要压缩。
2.否则 执行压缩
首先读取100%的图片ByteArrayOutputStream对象 然后获取其字节的大小
//首先读取100%的图片ByteArrayOutputStream对象
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream);
//然后获取其字节的大小
byteArrayOutputStream.size()
如果100%的图片ByteArrayOutputStream对象的字节大小大于要压缩的值 需要压缩
获取0%的图片ByteArrayOutputStream对象
通过二分法不断找到[0,100]的某个位置 然后拿到这个最佳位置position 最终还是通过
bitmap.compress(Bitmap.CompressFormat.JPEG, position, byteArrayOutputStream);方法
获取ByteArrayOutputStream对象对象 然后获取ByteArrayOutputStream对象的字节流返回即可
result = byteArrayOutputStream.toByteArray();
2.代码
/**
* 图片质量压缩,采用二分法
*
* @param bytes 原图字节流
* @param maxByteSize 压缩图片最大字节 K为单位
* 大致原理:bitmap.compress(Bitmap.CompressFormat.JPEG, midPosition, byteArrayOutputStream);方法
* 不断变化第二个参数 来压缩获取ByteArrayOutputStream对象 然后比较ByteArrayOutputStream对象的大小和要压缩的字节大小
*/
private byte[] compressByQuality(final byte[] bytes, final long maxByteSize) {
if (bytes == null || bytes.length == 0) return null;
//压缩图片最大字节
long maxSize = maxByteSize * 1024;
//图片字节流的大小
int srcSize = bytes.length;
//图片字节流的大小 小于等于 压缩图片最大字节 不需要压缩
if (srcSize <= maxByteSize) return bytes;
//图片字节流的大小 大于 压缩图片最大字节 二分法压缩
//1.根据传入的图片字节流获取Bitmap
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, srcSize);
//2.读取100%质量的图片流ByteArrayOutputStream对象
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream);
//3.result最终的图片字节数
byte[] result;
//如果 100%质量的图片流ByteArrayOutputStream对象的大小 还比要压缩的大小 小于等于 直接返回100%质量的图片流ByteArrayOutputStream对象
if (byteArrayOutputStream.size() <= maxSize) {
result = byteArrayOutputStream.toByteArray();
} else {//需要压缩
//4.收回掉100%质量的图片流ByteArrayOutputStream对象
byteArrayOutputStream.reset();
//5.获取0%质量的图片流ByteArrayOutputStream对象
bitmap.compress(Bitmap.CompressFormat.JPEG, 0, byteArrayOutputStream);
//如果 0%质量的图片流ByteArrayOutputStream对象的大小 还比要压缩的大小 大于等于 直接返回0%质量的图片流ByteArrayOutputStream对象
if (byteArrayOutputStream.size() < maxSize) {// 二分法寻找最佳质量
int startPosition = 0;
int endPosition = 100;
int midPosition = 0;
while (startPosition < endPosition) {
midPosition = (startPosition + endPosition) / 2;
byteArrayOutputStream.reset();
bitmap.compress(Bitmap.CompressFormat.JPEG, midPosition, byteArrayOutputStream);
int len = byteArrayOutputStream.size();
if (len == maxSize) {//找到位置对应的字节流等于要压缩的字节流 退出while循环
break;
} else if (len > maxSize) {//中间位置的字节流大于要压缩的字节流 在前半部分找
endPosition = midPosition - 1;
} else {//中间位置的字节流小于等于要压缩的字节流 在后半部分找
startPosition = midPosition + 1;
}
}
if (endPosition == midPosition - 1) {
byteArrayOutputStream.reset();
bitmap.compress(Bitmap.CompressFormat.JPEG, startPosition, byteArrayOutputStream);
}
}
result = byteArrayOutputStream.toByteArray();
}
//最后回收掉Bitmap
bitmap.recycle();
//返回图片字节流
return result;
}
三.LuBan压缩
1.效果与对比
2.代码
private void lubanMethod(File file){
Luban.with(this)//上下文对象
.load(file)//原图路径
.ignoreBy(100)//不压缩的阈值 单位为K 即图片小于此阈值时不压缩
.setFocusAlpha(false)//设置是否保留透明通道
.setTargetDir(setLuBanPath())//设置缓存压缩图片路径
.filter(new CompressionPredicate() {
@Override
public boolean apply(String path) {
return !(TextUtils.isEmpty(path));//图片路径不为空 返回true压缩
}
})//设置开启压缩条件
.setCompressListener(new OnCompressListener() {
@Override
public void onStart() {
}
@Override
public void onSuccess(File file) {
addthumbArg(file);
Log.d("TAG","压缩成功后的路径:"+file.getAbsolutePath());
}
@Override
public void onError(Throwable e) {
}
})//压缩回调接口
.setRenameListener(new OnRenameListener() {
@Override
public String rename(String filePath) {
String result=savenum+".jpg";
savenum++;
return result;
}
})//压缩前重命名接口
.launch();//压缩
}
3.效果
压缩前
压缩后
4.原文链接:https://github.com/Curzibn/Luban
5.代码详情:https://github.com/wujianning/LuBanDemo
6.算法
<1> 前言
Luban
是图片压缩工具,通过参考或者自创压缩规则推求极致的压缩效果 目前的版本压缩效果主要参考微信。因为微信用户量最大,如果压缩后的图片越接近微信则越被用户接受。
<2> 说明
目前的Luban
只是压缩结果接近微信,自身的算法只是为了达到这个效果而设计的。与微信并无任何联系,也不敢妄称是微信的算法。
<3> 算法步骤
(1) 判断图片比例值,是否处于以下区间内。
[1, 0.5625) 即图片处于 [1:1 ~ 9:16) 比例范围内
[0.5625, 0.5) 即图片处于 [9:16 ~ 1:2) 比例范围内
[0.5, 0) 即图片处于 [1:2 ~ 1:∞) 比例范围内
(2) 判断图片最长边是否过边界值。
[1, 0.5625) 边界值为:1664 * n(n=1), 4990 * n(n=2), 1280 * pow(2, n-1)(n≥3)
[0.5625, 0.5) 边界值为:1280 * pow(2, n-1)(n≥1)
[0.5, 0) 边界值为:1280 * pow(2, n-1)(n≥1)
(3) 计算压缩图片实际边长值,以第2步计算结果为准,超过某个边界值则:width / pow(2, n-1),height/pow(2, n-1)
(4) 计算压缩图片的实际文件大小,以第2、3步结果为准,图片比例越大则文件越大。
size = (newW * newH) / (width * height) * m;
[1, 0.5625) 则 width & height 对应 1664,4990,1280 * n(n≥3),m 对应 150,300,300;
[0.5625, 0.5) 则 width = 1440,height = 2560, m = 200;
[0.5, 0) 则 width = 1280,height = 1280 / scale,m = 500;注:scale为比例值
(5) 判断第4步的size是否过小
[1, 0.5625) 则最小 size 对应 60,60,100
[0.5625, 0.5) 则最小 size 都为 100
[0.5, 0) 则最小 size 都为 100
(6) 将前面求到的值压缩图片 width, height, size 传入压缩流程,压缩图片直到满足以上数值