zoukankan      html  css  js  c++  java
  • Android图片压缩

    在我们的业务场景中,一般需要使用客户端采集图片上传至服务器,为了提升性能,我们一般会对图片进行压缩。

    在Android平台上,默认提供的压缩有三种方式:质量压缩和采样压缩。

    一、质量压缩

    质量压缩不改变图片的尺寸,只改变图片的存储体积,即原来是1080*1920的图片压缩后还是分辨率不变,并且压缩前后由File格式转换成Bitmap格式进入内存中,占用的内存并没有改变,因为内存是根据图片的像素来给图片分配内存大小的。

    质量压缩主要借助Bitmap中的compress方法实现:

    public boolean compress (Bitmap.CompressFormat format, int quality, OutputStream stream)

    该方法接收三个参数,其含义分别如下:

    1. format:枚举类型,有三个选项 JPEG, PNG 和 WEBP,表示图片的格式;
    2. quality:图片的质量,取值在 [0,100] 之间,表示图片质量,越大,图片的质量越高;(PNG格式会忽略该值设定 )

    3. stream:一个输出流,通常是我们压缩结果输出的文件的流。

    注意:当调用bitmap.compress(CompressFormat.JPEG, 100, fos);保存为图片时发现图片背景为黑色时,将格式改为png格式就好了。

    二、采样压缩

    通过设置采样率,减少图片的像素,达到对内存中Bitmap进行压缩。

    采样压缩主要通过BitmapFactorry中的decodeFile方法实现:

    public static Bitmap decodeFile (String pathName, BitmapFactory.Options opts)

    该方法接收2个参数:

    1. pathName是图片文件路径
    2. opts是采样率,通过设置采样率属性,来达到根据需要压缩图片的目的。

    标准使用如下:

    // 获取原始图片的尺寸
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    options.inSampleSize = 1;
    BitmapFactory.decodeStream(srcImg.open(), null, options);
    this.srcWidth = options.outWidth;
    this.srcHeight = options.outHeight;
    
    // 进行图片加载,此时会将图片加载到内存中
    options.inJustDecodeBounds = false;
    options.inSampleSize = calInSampleSize();
    Bitmap bitmap = BitmapFactory.decodeStream(srcImg.open(), null, options);

    这里分成了2步实现:

    1.设置inJustDecodeBounds为true来得到图片的宽高,此时图片不会被加载到内存中,也就不会造成OOM。

    2.根据第一步的尺寸,计算inSampleSize,然后将inJustDecodeBounds设置为true,加载采样后的图片至内存中。

      inSampleSize代表压缩后的一个像素点代表原图像素点的几个像素点,例如inSampleSize为2,则压缩后的图片宽高是原图的1/2,像素点是原来的1/4,呈指数型增长。

    图片压缩算法总结:

    实际使用中,我们一般通过先进行采样压缩,再进行质量压缩得到最终图片。

    使用示例:

      private void compressBitmapToFile() {
            Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_image2);
    
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.JPEG,100,baos);
            //图片质量小于100K不压缩(该值计算不太准)
           if( baos.toByteArray().length /1024 > 100){
    
        //采样压缩
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            options.inSampleSize = 1;
            BitmapFactory.decodeResource(getResources(),R.drawable.ic_image3,options);
            //计算缩放值
            options.inSampleSize =  computeSize(options, 720, 1028);
            LogUtil.e("inSampleSize-"+options.inSampleSize);
    
            options.inJustDecodeBounds = false;
            bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_image2,options);
    
            //压缩图片质量 像素值不变
            baos = new ByteArrayOutputStream();
            int options1 = 75;
            bitmap.compress(Bitmap.CompressFormat.JPEG,options1,baos);
            bitmap.recycle();
    /*
            while(baos.toByteArray().length /1024 > 100){
                baos.reset();
                options1 -= 10;
                bitmap.compress(Bitmap.CompressFormat.JPEG,options1,baos);
            }*/
           }
    //将压缩后图片写入文件
            String path = getExternalCacheDir().getAbsolutePath() + "/ic_image.jpg";
            File file = new File(path);
            if(!file.exists()){
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(file);
                fos.write(baos.toByteArray());
                fos.flush();
                fos.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    computeSize代码如下。
     private int computeSize(BitmapFactory.Options options,int reqWidth,int reqHeight) {
            int srcHeight = options.outHeight;
            int srcWidth = options.outWidth;
            srcWidth = srcWidth % 2 == 1 ? srcWidth + 1 : srcWidth;
            srcHeight = srcHeight % 2 == 1 ? srcHeight + 1 : srcHeight;
    
            int inSampleSize = 1;
            if (srcHeight > reqHeight || srcWidth > reqWidth) {
                final int heightRatio = Math.round((float) srcHeight / (float) reqHeight);
                final int widthRatio = Math.round((float) srcWidth / (float) reqWidth);
                inSampleSize = heightRatio <= widthRatio ? heightRatio : widthRatio;//
            }
            if (inSampleSize <= 0) {
                return 1;
            }
            return inSampleSize;
            
            //鲁班压缩代码
           /* 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 >= 1664 && 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));
            }*/
    
            
        }

    旋转图片:

        private Bitmap rotatingImage(Bitmap bitmap) {
            if (srcExif == null) return bitmap;
    
            Matrix matrix = new Matrix();
            int angle = 0;
            int orientation = srcExif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    angle = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    angle = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    angle = 270;
                    break;
            }
    
            matrix.postRotate(angle);
    
            return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        }

    压缩亲测可用,图片压缩对比大致效果如下。

    鲁班算法逻辑见:https://github.com/Curzibn/Luban/blob/master/DESCRIPTION.md

    参考地址:

    https://www.jianshu.com/p/d0f54747d07a

    https://blog.csdn.net/qq_27634797/article/details/79424507

  • 相关阅读:
    confluent-kafka python Producer Consumer实现
    kafka producer.poll producer.flush consumer.poll的区别
    kafka Java创建生产者报错:Invalid partition given with record: 1 is not in the range [0...1)
    Kafka通讯的Java实例
    虚机克隆搭建kafka服务器集群
    kafka报错解决:Broker may not be avaliable
    Kafka+Zookeeper+confluent-kafka搭建
    Kafka学习笔记
    【SpringCloud】 第十篇: 高可用的服务注册中心
    【SpringCloud】 第九篇: 服务链路追踪(Spring Cloud Sleuth)
  • 原文地址:https://www.cnblogs.com/fangg/p/11233566.html
Copyright © 2011-2022 走看看