您的位置:首页 > 移动开发 > 微信开发

安卓图片的压缩,尺寸,质量,采样率和微信压缩

2017-01-02 17:45 477 查看
我叫小马,我在坚持写一些东西,希望互相学习.我的博客是http://blog.csdn.NET/maqianli23

本博客地址:http://blog.csdn.net/maqianli23/article/details/53980798

---------------------------------------------------------------------------------------------------------------------------------------------------

今天写这个图片压缩,喜欢的就看一下。

先介绍一下图片存在的几种形式:

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息