zoukankan      html  css  js  c++  java
  • Android性能优化之图片压缩优化

    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终极方法。

    8 参考链接

    Android图片压缩(质量压缩和尺寸压缩)&Bitmap转成字符串上传

  • 相关阅读:
    求给定数组中最大值和其在数组中的索引并输出
    多线程与多进程
    logging模块
    QWidget上下文菜单处理函数
    python中的yield关键字
    菜单栏(QMenuBar)与菜单(QMenu)
    PyQt5布局管理(1)
    QMainFrame类
    QTP11使用DOM XPath以及CSS识别元素对象
    C# 跨线程访问控件
  • 原文地址:https://www.cnblogs.com/Im-Victor/p/10014139.html
Copyright © 2011-2022 走看看