zoukankan      html  css  js  c++  java
  • 图片之压缩总结

     

    2.inSampleSize优化

    1.Android中图片的压缩方法

    ===

     2.inSampleSize优化

    废话不多说先贴上常用的方法吧

     public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
            final int width = options.outWidth;
            final int height = options.outHeight;
            int inSampleSize = 1;
            if (height > reqHeight || width > reqWidth) {
                //计算图片高度和我们需要高度的最接近比例值
                final int heightRatio = Math.round((float) height / (float) reqHeight);
                //宽度比例值
                final int widthRatio = Math.round((float) width / (float) reqWidth);
                //取比例值中的较大值作为inSampleSize
                inSampleSize = heightRatio > widthRatio ? heightRatio : widthRatio;
            }
    
            return inSampleSize;
        }
    

    inSampleSize的默认值和最小值为1(当小于1时,解码器将该值当做1来处理),且在大于1时,该值只能为2的幂(当不为2的幂时,解码器会取与该值最接近的2的幂)。例如,当inSampleSize为2时,一个20001000的图片,将被缩小为1000500,相应地,它的像素数和内存占用都被缩小为了原来的1/4:

    但是由于我们平时拍的照片都是长图,这种算法用在浏览相册图片的时候会导致压缩太过严重,导致照片不是很清楚
    都是在1080 * 1776 (nexus 5)上load一张满屏图

    widthheightRatio
    1536 2048 1
    1944 2592 2
    3264 2448 3
    8264 2446 8

    可以看出来如果我们图片是宽度大于高度的图片,图片ratio就会很大导致图片压缩太严重在显示可缩放的图片的时候,图片质量过于差。

        public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
            final int width = options.outWidth;
            final int height = options.outHeight;
            int inSampleSize = 1;
    
            if (height > reqHeight || width > reqWidth) {
                //使用需要的宽高的最大值来计算比率
                final int suitedValue = reqHeight > reqWidth ? reqHeight : reqWidth;
                final int heightRatio = Math.round((float) height / (float) suitedValue);
                final int widthRatio = Math.round((float) width / (float) suitedValue);
                inSampleSize = heightRatio > widthRatio ? heightRatio : widthRatio;//用最大
            }
            return inSampleSize;
        }
    

    使用上面优化过后的方法后

    widthheightRatio
    1536 2048 1
    1944 2592 1
    3264 2448 2
    8264 2446 4

    在宽大于高的时候对于图片质量有了较大的提升

    使用这种算法不适宜用于一些不太在意图片质量,或者不支持缩放图片的地方。正常情况下还是可以用官方的方法。或者在方法中判断宽和高如果宽大于高使用下面的方法计算SampleSize

    再贴一个SDK文档的算法,还没有用过~

    public static int computeSampleSize(BitmapFactory.Options options,
            int minSideLength, int maxNumOfPixels) {
        int initialSize = computeInitialSampleSize(options, minSideLength,maxNumOfPixels);
        int roundedSize;
        if (initialSize <= 8) {
            roundedSize = 1;
            while (roundedSize < initialSize) {
                roundedSize <<= 1;
            }
        } else {
            roundedSize = (initialSize + 7) / 8 * 8;
        }
        return roundedSize;
    
    }
    
    private static int computeInitialSampleSize(BitmapFactory.Options options,
            int minSideLength, int maxNumOfPixels) {
        double w = options.outWidth;
        double h = options.outHeight;
        int lowerBound = (maxNumOfPixels == -1) ? 1 :
                (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
        int upperBound = (minSideLength == -1) ? 128 :
                (int) Math.min(Math.floor(w / minSideLength),
                Math.floor(h / minSideLength));
        if (upperBound < lowerBound) {
            // return the larger one when there is no overlapping zone.
            return lowerBound;
        }
    
        if ((maxNumOfPixels == -1) &&  (minSideLength == -1)) {
            return 1;
        } else if (minSideLength == -1) {
            return lowerBound;
        } else {
            return upperBound;
        }
    } 
    //使用方式
    opts.inSampleSize = computeSampleSize(opts, -1, 128*128);  
    
    

    延伸:一个Bitmap到底占用多大内存?系统给每个应用程序分配多大内存?
    · Bitmap占用的内存为:像素总数 * 每个像素占用的内存。在Android中,Bitmap有四种像素类型:ARGB_8888、ARGB_4444、ARGB_565、ALPHA_8,他们每个像素占用的字节数分别为4、2、2、1。因此,一个20001000的ARGB_8888类型的Bitmap占用的内存为20001000*4=8000000B=8MB。
    · Android根据设备屏幕尺寸和dpi的不同,给系统分配的单应用程序内存大小也不同,具体如下表(表格取自Android 4.4 Compatibility Definition Document (CDD)):

    屏幕尺寸DPI应用内存
    small / normal / large ldpi / mdpi 16MB
    small / normal / large tvdpi / hdpi 32MB
    small / normal / large xhdpi 64MB
    small / normal / large 400dpi 96MB
    small / normal / large xxhdpi 128MB
    xlarge mdpi 32MB
    xlarge tvdpi / hdpi 64MB
    xlarge xhdpi 128MB
    xlarge 400dpi 192MB
    xlarge xxhdpi 256MB

     

     1.Android中图片的压缩方法

    Bitmap

    1. Android中图片是以Bitmap(位图)形式存在,位图常见的文件格式有:.bmp .jpg .png .gif 。
    2. Bitmap的大小计算 = 图片的长度图片的宽度单位像素所占用的字节数。
    3. Bitmap的优缺点
    • 优点:色彩变化丰富,可以改变任何形状的区域色彩显示效果。
    • 缺点:放大和缩小都会引起像素的增加和缩小,这样会使图片出现失真或者锯齿形。另一个缺点就是像素越高数据量越大占用的内存越大。

    常用压缩方法

    质量压缩
    1. 质量压缩是不会改变图片的宽高(像素),是去改变图片的位深和透明度的。
    2. png是无损压缩的,所以质量压缩对png是不起作用的
    /**
         * 质量压缩
         * 降低图片的质量,不会减少图片的像素,改变图片的位深和透明度,没有改变像素大小,所以不会减少占据的内存大小。
         */
        private void qualityCompress(Bitmap.CompressFormat format, int quality, String type) {
            try {
                File file = new File(Environment.getExternalStorageDirectory(), "test_" + quality + format + "_" + type + ".jpg");
                //得到一个文件输入流
                mFileOutputStream = new FileOutputStream(file);
    
                Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_pic);
                // 图片的格式  quality是压缩完之后保存图片设置的质量参数  png是无损压缩,quality参数对png无效
                bitmap.compress(format, quality, mFileOutputStream);
    
            } catch (FileNotFoundException exception) {
                exception.printStackTrace();
            } finally {
                if (mFileOutputStream != null) {
                    try {
                        mFileOutputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    采样率压缩
    1. 降低图片的像素。数值越高,图片像素越低。
    /**
         * 尺寸压缩中的采样率压缩
         * 改变了像素,减少了图片占用的内存,同样照片的清晰度也降低了。
         * inSampleSize = 2 内存将减少 1/4  内存大小 = 图片宽/inSampleSize * 图片高/inSampleSize * 单位像素占用的字节大小
         */
        private void loadImage() {
            BitmapFactory.Options options = new BitmapFactory.Options();
            //设置为true 并会将图片加载到内存中,但是可以获取到图片的宽和高 通过options
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeResource(getResources(), R.drawable.ic_pic, options);
            int outWidth = options.outWidth;
            int outHeight = options.outHeight;
            Log.d(TAG, "loadImage: width = " + outWidth);
            Log.d(TAG, "loadImage: height = " + outHeight);
            //这个地方根据获取到大小和想要显示的大小做缩放
            options.inSampleSize = calculateInSampleSize(options, 200, 200);
            Log.d(TAG, "loadImage: inSampleSize = " + options.inSampleSize);
            //设置为false 这回再去解码图片可以将其读取到内存中了。
            options.inJustDecodeBounds = false;
            mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_pic, options);
            File file = new File(Environment.getExternalStorageDirectory(), "text_" + "just_100_no_inSampleSize.jpg");
            FileOutputStream fileOutputStream = null;
            try {
                fileOutputStream = new FileOutputStream(file);
                mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } finally {
                if (fileOutputStream != null) {
                    try {
                        fileOutputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
    
    //        mImageView.setImageBitmap(mBitmap);
    
        }
    

    即:如果inJustDecoedBounds设置为true的话,解码bitmap时可以只返回其高、宽和Mime类型,而不必为其申请内存,从而节省了内存空间。
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
    int imageHeight = options.outHeight;
    int imageWidth = options.outWidth;
    String imageType = options.outMimeType;

        /**
         * 计算图片的缩放比例
         *
         * @param options
         * @param reqHeight
         * @param reqWidth
         */
        private int calculateInSampleSize(BitmapFactory.Options options, int reqHeight, int reqWidth) {
            int height = options.outHeight;
            int width = options.outWidth;
            //缩放的比例
            int inSampleSize = 1;
            if (height > reqHeight || width > reqWidth) {
                int halfHeight = height / 2;
                int halfWidth = width / 2;
                while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
                    inSampleSize *= 2;
                }
            }
            return inSampleSize;
    
        }
    
    缩放法压缩
    1. 减小图片的像素,生成的图片file的大小也变小了。
    /**
         * 尺寸压缩中的 按缩放比压缩 这里的缩放比是事先定义好的 和采样率相比 采样率是根据给定的预期大小去计算缩放比
         *
         * @param mBitmap
         * @param mFile
         */
        private void compressBitmapToFile(Bitmap mBitmap, File mFile) {
            //设置压缩倍数
            int ratio = 2;
    
            //压缩Bitmap到对应的尺寸 压缩格式 ARGB_8888 4字节 一个像素需要4个字节来存储
            Bitmap resultBitmap = Bitmap.createBitmap(mBitmap.getWidth() / ratio, mBitmap.getHeight() / ratio, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(resultBitmap);
            Rect rect = new Rect(0, 0, mBitmap.getWidth() / ratio, mBitmap.getHeight() / ratio);
            canvas.drawBitmap(mBitmap, null, rect, null);
    
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            File file = new File(Environment.getExternalStorageDirectory(), "test_compress_100.jpg");
            resultBitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream);
            try {
                mFileOutputStream1 = new FileOutputStream(file);
                mFileOutputStream1.write(byteArrayOutputStream.toByteArray());
                mFileOutputStream1.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (mFileOutputStream1 != null) {
                    try {
                        mFileOutputStream1.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        } 
  • 相关阅读:
    推自己的镜像到网易云
    supervisord常见问题
    supervisord的配置
    一文解读SDN (转)
    一文解读ZooKeeper (转)
    使用 Docker 和 Nginx 打造高性能的二维码服务 (转)
    一文解读Docker (转)
    一文解读分布式架构 (转)
    一文解读分布式事务 (转)
    一文读懂工业大数据 (转)
  • 原文地址:https://www.cnblogs.com/awkflf11/p/12611801.html
Copyright © 2011-2022 走看看