今天写这个图片压缩,喜欢的就看一下。
先介绍一下图片存在的几种形式:
1.file文件
2.流的形式
3.字符串(base64,便于加密)
4.bitmap---内存的形式
---------------------------------------------------------------------------------------------------------------------------------------------------
图片压缩:分为质量压缩,尺寸压缩,采样率压缩,微信压缩(哈夫曼编码)(前三个不是重点,重点是第四种)
一.质量压缩:设置bitmap options属性,降低图片的质量,像素不会减少
/**
* 1. 质量压缩
设置bitmap options属性,降低图片的质量,像素不会减少
第一个参数为需要压缩的bitmap图片对象,第二个参数为压缩后图片保存的位置
设置options 属性0-100,来实现压缩
* @param bmp
* @param file
*/
public static void compressImageToFile(Bitmap bmp,File file) {
// 0-100 100为不压缩
int options = 20;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 把压缩后的数据存放到baos中
bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
二.尺寸压缩:通过缩放像素减少图片的大小。
/**
*
* 2. 尺寸压缩
通过缩放图片像素来减少图片占用内存大小
* @param bmp
* @param file
*/
public static void compressBitmapToFile(Bitmap bmp, File file){
// 尺寸压缩倍数,值越大,图片尺寸越小
int ratio = 4;
// 压缩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();
}
}
三.采样率压缩:通过降低图片像素来减小图片的大小
/**
* 设置图片的采样率,降低图片像素
* @param filePath
* @param file
*/
public static void compressBitmap1(InputStream filePath, File file){
// 数值越高,图片像素越低
int inSampleSize = 4;
BitmapFactory.Options options = new BitmapFactory.Options();
// options.inJustDecodeBounds = false;
options.inJustDecodeBounds = true;//为true的时候不会真正加载图片,而是得到图片的宽高信息。
//采样率
options.inSampleSize = inSampleSize;
Bitmap bitmap = BitmapFactory.decodeStream(filePath,null ,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();
}
}
四.微信压缩(华为EMUI5.0已经将定常编码改为了哈夫曼编码(猜测,没有看过源码),但是微信是这样子做的)
现在来想一下,IOS拍照1M的图片要比安卓拍照排出来的5M的图片还要清晰(同情景下)?
这就要说到安卓的图片处理引擎了。
95年 JPEG处理引擎,用于最初的在PC上面处理图片的引擎。
05年 skia开源的引擎, 开发了一套基于JPEG处理引擎的第二次开发,便于浏览器的使用。
07年, 安卓诞生,安卓上面用的什么引擎呢?答案是:skia引擎,阉割版。
谷歌拿了skia 思考了半天做了一个决定,去掉一个编码算法---哈夫曼算法。采用定长编码算法。但是解码还是保留了哈夫曼算法。这就导致了图片处理后文件变大了。
而他选择这样做的理由是什么呢?当时由于CPU和内存在手机上都非常吃紧 ,性能差,由于哈夫曼算法非常吃CPU,被迫用了其他的算法。
解释一下:每一个像素包含着ARGB四个信息,即alpha,red,green,blue
现在我们将这四个信息抽象出来,用abcde这五个字母表示一段复杂度为5的信息。在计算机中的表达方式必须按照二进制编码的形式表示。
1.如果通常情况下a,b,c,d,e用以下的方式表达
a:0001
b:0010
c:0011
d:0100
e:0101
大家可以看到,最前面的一位数字都是0,其实是被浪费掉了,
2.所以在定常编码算法下最优的表达方式为:
a:001
b:010
c:011
d:100
e:101
这样做的话就可以节省一位的损耗。(安卓图片处理引擎目前就是使用定常编码算法实现的)
以上是定常编码算法给出的解决方法
3.现在 接下来我们继续进行优化,在哈夫曼算法中我们可以给我们的信息赋予权重。(为信息编码加权)
假设a占据了80%,b占据了10%,c占据了10%,d和e都是0,
a:001(80%)
b:010(10%)
c:011(10%)
d:100
e:101
在这种栗子下,我们可以使用哈夫曼算法进行再次优化为
a:01
b:10
c:11
加权后的abcde:01 10 11 (哈夫曼编码)
定常编码下的abcde: 001 010 011 100 101
加权后的abcde的de呢 当然是没有了,权重为0嘛。
但是问题就在这里了,如何得到每一个字母出现权重呢?如果能够知道每一个权重,那么我们就能够动态的使用最优的编码了。
这里也就是为什么当初谷歌的工程师不使用哈夫曼编码的理由了。
那么如何得到呢?需要去扫描整个信息(即整张图片的信息---每一个像素包括ARGB),要大量的进行计算,非常吃CPU。(可以想象一下,07年你所使用的安卓只能手机)。。。。
一个栗子:1280*720的一张图片需要计算的次数为 720*1280*4(ARGB),这对于当时的安卓手机来说可以是一个灾难。。
以上均是第四种压缩的原理
-------------------如何实现----------=-----------
下载JPEG引擎使用的库---libjpeg库:http://www.ijg.org/
基于该引擎来做一定的开发----自己实现编码。
NDK编写流程:
1.导入库文件libjpegbither.so
2.导入头文件
3.写mk文件
Android.mk
Applicatoin.mk
4.写代码
C++: XX.cpp
C: XX.c
编码思维:
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()
目前 ,已经把项目生成了.so文件 可以直接使用,注意NativeUtil(你使用的)所在包名必须和我的项目NativeUtil所在的包名相同。
传送门:https://github.com/yuyunhai/weixinyasuo