图片之压缩总结

 

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();
                }
            }
        }
    } 
原文地址:https://www.cnblogs.com/awkflf11/p/12611801.html