图片二次采样和压缩

图片的存在形式

1.文件形式(即以二进制形式存在于硬盘上)

2.流的形式(即以二进制形式存在于内存中)

3.Bitmap形式

这三种形式的区别: 文件形式和流的形式对图片体积大小并没有影响,也就是说,如果你手机SD卡上的如果是100K,那么通过流的形式读到内存中,也一定是占100K的内存,注意是流的形式,不是Bitmap的形式,当图片以Bitmap的形式存在时,其占用的内存会瞬间变大

1,检测图片三种形式大小的方法:

文件形式: file.length()

流的形式: 讲图片文件读到内存输入流中,看它的byte数

Bitmap: bitmap.getByteCount()

二.常见的压缩方式

将图片保存到本地时进行压缩, 即将图片从Bitmap形式变为File形式时进行压缩,

特点是: File形式的图片确实被压缩了, 但是当你重新读取压缩后的file为 Bitmap是,它占用的内存并没有改变

图片二次采样的原因

二次采样就是为了避免图片加载时的OOM异常。

1.第一次采样

第一次采样我主要是想要获得图片的压缩比例,假如说我有一张图片是200200,那么我想把这张图片的缩略图显示在一个5050的ImageView上,那我的压缩比例应该为4,接下来我应该这么做,我先加载图片的边界到内存中,这个加载操作并不会耗费多少内存,加载到内存之后,我就可以获得这张图片的宽高参数,然后根据图片的宽高,再结合控件的宽高计算出缩放比例。

2.第二次采样

在第一次采样的基础上,我来进行二次采样。二次采样的时候,我把第一次采样后算出来的结果作为一个参数传递给第BitmapFactory,这样在加载图片的时候系统就不会将整张图片加载进来了,而是只会加载该图片的一张缩略图进来,这样不仅提高了加载速率,而且也极大的节省了内存,而且对于用户来说,他也不会有视觉上的差异

BitmapFactory.Options的作用:

1.防止内存溢出;

2.节省内存开销;

3.系统更流畅;

BitmapFactory.decodeByteArray方法对压缩后的byte[]解码后,得到的Bitmap大小依然和未压缩过一样

如果你想要显示的Bitmap占用的内存少一点,还是需要去设置加载的像素长度和宽度(变成缩略图)

BitmapFactory.Options的重要属性:

1.injustDecodeBounds;

设为true,那么BitmapFactory并不会真的返回一个Bitmap给你,它仅仅会把它的宽,高取回来给你,这样就不会占用太多的内存,也就不会那么频繁的发生OOM。

设置为false,BitmapFactory返回bitmap;

2.outWidth&outHeight;

bitmap图像的宽和高;

3.inSampleSize;

获取采样率

inSampleSize大于1时,图像高、宽分别以2的inSampleSize次方分之一缩小

inSampleSize小于等于1时,图像高、宽不变;

4.inpreferredConfig:

ALPHA_8: 每个像素用占8位,存储的是图像的透明值,占1个字节;

RGB_565:每个像素用占16位,分别为5-R,6-G,5-B通道,占2个字节;

ARGB-4444:每个像素占16位,即每个通道用4位表示,占2个字节;

ARGB_8888:每个像素占32位,每个通道用8位表示,占4个字节;

5.inDither:

是否进行图像抖动处理;

6.inMutable:

如果设置为true,将返回一个mutable的bitmap,可用于修改BitmapFactory加载而来的bitmap.

7.inScale:

是否需要放缩位图

图片二次采样的Java代码的实现

public class BitmapUtils {  
    /** 
     * @param filePath   要加载的图片路径 
     * @param destWidth  显示图片的控件宽度 
     * @param destHeight 显示图片的控件的高度 
     * @return 
     */  
    public static Bitmap getBitmap(String filePath, int destWidth, int destHeight) {  
        //第一次采样  
        BitmapFactory.Options options = new BitmapFactory.Options();  
        //该属性设置为true只会加载图片的边框进来,并不会加载图片具体的像素点  
        options.inJustDecodeBounds = true;  
inJustDecodeBounds:
如果inJustDecoedBounds设置为true的话,解码bitmap时可以只返回其高、宽和Mime类型,而不必为其申请内存,从而节省了内存空间。



        //第一次加载图片,这时只会加载图片的边框进来,并不会加载图片中的像素点  
        BitmapFactory.decodeFile(filePath, options);  
        //获得原图的宽和高  
        int outWidth = options.outWidth;  
        int outHeight = options.outHeight;  
        //定义缩放比例  
        int sampleSize = 1;  
        while (outHeight / sampleSize > destHeight || outWidth / sampleSize > destWidth) {  
//如果宽高的任意一方的缩放比例没有达到要求,都继续增大缩放比例  
//sampleSize应该为2的n次幂,如果给sampleSize设置的数字不是2的n次幂,那么系统会就近取值  
            sampleSize *= 2;  
        }  
        /********************************************************************************************/  
//至此,第一次采样已经结束,我们已经成功的计算出sampleSize的大小  
  
        //二次采样开始  
//二次采样时我需要将图片加载出来显示,不能只加载图片的框架,因此inJustDecodeBounds属性要设置为false  
        options.inJustDecodeBounds = false;  
        //设置缩放比例  
        options.inSampleSize = sampleSize;  
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;  

inPreferredConfig "
这个值是设置色彩模式,默认值是ARGB_8888,在这个模式下,一个像素点占用4bytes空间,一般对透明度不做要求的话,一般采用RGB_565模式,这个模式下一个像素点占用2bytes。
        //加载图片并返回  
        return BitmapFactory.decodeFile(filePath, options);  
    }  
}  

图片的压缩

图片压缩

Bitmap.compress方法确实可以压缩图片,但压缩的是存储大小,即你放到disk上的大小

android图片压缩总结

总结来看,图片有三种存在形式:硬盘上时是file,网络传输时是stream,内存中是stream或bitmap,所谓 的质量压缩,它其实只能实现对file的影响,你可以把一个file转成bitmap再转成file,或者直接将一个bitmap转成file时,这个最终的file是被压缩过的,但是中间的bitmap并没有被压缩(或者说几乎没有被压缩,我不确定),因为bigmap在内存中的大小是按像素计算的,也就是width * height,对于质量压缩,并不会改变图片的像素,所以就算质量被压缩了,但是bitmap在内存的占有率还是没变小,但你做成file时,它确实变小了;

而尺寸压缩由于是减小了图片的像素,所以它直接对bitmap产生了影响,当然最终的file也是相对的变小了;

图片大小的压缩

private void resizePhoto() {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    
    BitmapFactory.decodeFile(mcurrentPhotoPath, options);
    
    double ratio = Math.max(options.outWidth*1.0d/1024 , options.outHeight*1.0d/1024);
    
    options.inSampleSize = (int)Math.ceil(ratio);
    
    options.inJustDecodeBounds = false;
    
    mphotoImage = BitmapFactory.decodeFile(mcurrentPhotoPath, options);
}

图片质量的压缩

//质量压缩
public static Bitmap compressImage(Bitmap image, int size) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
    int options = 100;
    while ( baos.toByteArray().length / 1024>50) {   //循环判断如果压缩后图片是否大于50kb,大于继续压缩       
        baos.reset();//重置baos即清空baos
        image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
        options -= 10;//每次都减少10
    }
    ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中
    Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片
    return bitmap;
}

质量压缩不会减少图片的像素,它是在保持像素的前提下改变图片的位深及透明度,来达到压缩图片的目的,图片的长,宽,像素都不会改变,那么bitmap所占内存大小是不会变的。

参考链接

二次采样的代码链接:https://blog.csdn.net/qq_37548177/article/details/73920917

图片的压缩(二次采样):https://www.cnblogs.com/bimingcong/p/4943659.html

图片压缩链接:
https://www.jianshu.com/p/c117450031ff

你可能感兴趣的:(图片二次采样和压缩)