美文网首页
图片压缩,二次采样

图片压缩,二次采样

作者: lay_wn | 来源:发表于2018-06-22 20:59 被阅读0次

    图片压缩就是为了避免我们内存溢出,所有要对一系列进行压缩二次采样等

    1.什么是OOM?为什么会引起OOM?

    out of Memory(内存溢出)我们都知道Android系统会被每个App分配一个独立的空间,或者说分配一个Dalvik虚拟机(Dalvik是Google公司自己设计用于Android平台的虚拟机。Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的核心组成部分之一。它可以支持已转换为 .dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且[1]每一个Dalvik应用作为一个独立的Linux 进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。)这样每个APP都可以独立运行而不相互影响!而Android对于每个 Dalvik虚拟机都会有一个最大内存限制,如果当前占用的内存加上我们申请的内存资源超过了这个限制 ,系统就会抛出OOM错误!另外,这里别和RAM混淆了,即时当前RAM中剩余的内存有1G多,但是OOM还是会发生!别把RAM(物理内存)和OOM扯到一起!另外RAM不足的话,就是杀应用了,而不是仅仅是OOM了! 而这个Dalvik中的最大内存标准,不同的机型是不一样的

    这里可以获取系统分给你App多少内存

    ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE); Log.e("HEHE","最大内存:" + activityManager.getMemoryClass());

    一、Bitmap:

    Bitmap是Android系统中的图像处理的最重要类之一。用它可以获取图像文件信息,进行图像剪切、旋转、缩放等操作,并可以指定格式保存图像文件。

    常用方法:

    public void recycle()  // 回收位图占用的内存空间,把位图标记为Dead

    public final boolean isRecycled()  //判断位图内存是否已释放

    public final int getWidth() //获取位图的宽度

    public final int getHeight() //获取位图的高度

    public final boolean isMutable() //图片是否可修改

    public int getScaledWidth(Canvas canvas) //获取指定密度转换后的图像的度

    public int getScaledHeight(Canvas canvas) //获取指定密度转换后的图像的度

    public boolean compress(CompressFormat format, int quality, OutputStream stream) //按指定的图片格式以及画质,将图片转换为输出流。

    format:压缩图像的格式,如Bitmap.CompressFormat.PNG或 ·Bitmap.CompressFormat.JPEG

    quality:画质,0-100.0表示最低画质压缩,100以最高画质压缩。对于PNG等无损格式的图片,会忽略此项设置。

    stream: OutputStream中写入压缩数据。

    return: 是否成功压缩到指定的流。

    public static Bitmap createBitmap(Bitmap src)  //以src为原图生成不可变得新图像

    public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) //以src为原图,创建新的图像,指定新图像的高宽以及是否可变。

    public static Bitmap createBitmap(int width, int height, Config config) //创建指定格式、大小的位图

    public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) //以source为原图,创建新的图片,指定起始坐标以及新图像的高宽。

    BitmapFactory.Option可设置参数

    Option 参数类:

    public boolean inJustDecodeBounds //如果设置为true,不获取图片,不分配 内存,但会返回图片的高度宽度信息。

    如果将这个值置为true,那么在解码的时候将不会返回bitmap,只会返回这个bitmap 的尺寸。这个属性的目的是,如果你只想知道一个bitmap的尺寸,但又不想将其加载到内存时。这是一个非常有用的属性。

    public int inSampleSize //图片缩放的倍数

    这个值是一个int,当它小于1的时候,将会被当做1处理,如果大于1,那么就会按照比例(1 / inSampleSize)缩小bitmap的宽和高、降低分辨率,大于1时这个值将会被处置为2的倍数。例如,width=100,height=100,inSampleSize=2,那么就会将bitmap处理为,width=50,height=50,宽高降为1 / 2,像素数降为1 / 4。

    public int outWidth //获取图片的宽度值

    public int outHeight //获取图片的高度值

    表示这个Bitmap的宽和高,一般和inJustDecodeBounds一起使用来获得Bitmap的宽高,但是不加载到内存。

    public int inDensity //用于位图的像素压缩比

    public int inTargetDensity //用于目标位图的像素压缩比(要生成的位图)

    public byte[] inTempStorage  //创建临时文件,将图片存储

    public boolean inScaled //设置为true时进行图片压缩,从inDensity到inTargetDensity

    public boolean inDither  //如果为true,解码器尝试抖动解码

    public Bitmap.Config inPreferredConfig  //设置解码器

    这个值是设置色彩模式,默认值是ARGB_8888,在这个模式下,一个像素点占用4bytes空间,一般对透明度不做要求的话,一般采用RGB_565模式,这个模式下一个像素点占用2bytes。

    public String outMimeType  //设置解码图像

    public boolean inPurgeable //当存储Pixel的内存空间在系统内存不足时是否可以被回收

    public boolean inInputShareable  //inPurgeable为true情况下才生效,是否可以共享一个InputStream

    public boolean inPreferQualityOverSpeed  //为true则优先保证Bitmap质量其次是解码速度

    public boolean inMutable  //配置Bitmap是否可以更改,比如:在Bitmap上隔几个像素加一条线段

    public int inScreenDensity  //当前屏幕的像素密度

    从资源中获取位图的方式有两种:通过BitmapDrawable或者BitmapFactory

    你可以创建一个构造一个BitmapDrawable对象,比如通过流构建BitmapDrawable:

    BitmapDrawable bmpMeizi = new BitmapDrawable(getAssets().open("pic_meizi.jpg")); Bitmap mBitmap = bmpMeizi.getBitmap(); img_bg.setImageBitmap(mBitmap);

    工厂方法:

    public static Bitmap decodeFile(String pathName, Options opts)  //从文件读取图片

    public static Bitmap decodeFile(String pathName)

    public static Bitmap decodeStream(InputStream is)  //从输入流读取图片

    public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)

    public static Bitmap decodeResource(Resources res, int id)  //从资源文件读取图片

    public static Bitmap decodeResource(Resources res, int id, Options opts)

    public static Bitmap decodeByteArray(byte[] data, int offset, int length)  //从数组读取图片

    public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)

    public static Bitmap decodeFileDescriptor(FileDescriptor fd) //从文件读取文件 与decodeFile不同的是这个直接调用JNI函数进行读取 效率比较高

    public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)

    首先了解一下关于Bitmap的Config的理解

    A:透明度 R:红色 G:绿 B:蓝

    Bitmap.Config ARGB_4444:每个像素占四位,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位

    Bitmap.Config ARGB_8888:每个像素占四位,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位

    Bitmap.Config RGB_565:每个像素占四位,即R=5,G=6,B=5,没有透明度,那么一个像素点占5+6+5=16位

    Bitmap.Config ALPHA_8:每个像素占四位,只有透明度,没有颜色。

    bitmap内存大小 = 图片长度 x 图片宽度 x 单位像素占用的字节数

    起决定因素就是最后那个参数了,Bitmap'常见有2种编码方式:ARGB_8888和RGB_565,ARGB_8888每个像素点4个byte,RGB_565是2个byte,一般都采用ARGB_8888这种。那么常见的1080*1920的图片内存占用就是:

    1920 x 1080 x 4 = 7.9M

    常用的压缩方法:

    1.质量压缩

    private void compressQuality() { Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.test); mSrcSize = bm.getByteCount() + "byte"; ByteArrayOutputStream bos = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.JPEG, 100, bos); byte[] bytes = bos.toByteArray(); mSrcBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); }

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

    我们可以看到有个参数:quality,可以调节你压缩的比例,但是还要注意一点就是,质量压缩堆png格式这种图片没有作用,因为png是无损压缩。

    2.采样率压缩

    private void compressSampling() { BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2; mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test, options); }

    采样率压缩其原理其实也是缩放bitamp的尺寸,通过调节其inSampleSize参数,比如调节为2,宽高会为原来的1/2,内存变回原来的1/4.

    3.放缩法压缩

    private void compressMatrix() { Matrix matrix = new Matrix(); matrix.setScale(0.5f, 0.5f); Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.test); mSrcBitmap = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); bm = null; }

    放缩法压缩使用的是通过矩阵对图片进行裁剪,也是通过缩放图片尺寸,来达到压缩图片的效果,和采样率的原理一样

    4.RGB_565压缩

    private void compressRGB565() { BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565; mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test, options); }

    这是通过压缩像素占用的内存来达到压缩的效果,一般不建议使用ARGB_4444,因为画质实在是辣鸡,如果对透明度没有要求,建议可以改成RGB_565,相比ARGB_8888将节省一半的内存开销。

    5.createScaledBitmap

    private void compressScaleBitmap() { Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.test); mSrcBitmap = Bitmap.createScaledBitmap(bm, 600, 900, true); bm = null; }

    将图片的大小压缩成用户的期望大小,来减少占用内存。

    图片二次采样

    private Bitmap getCompressBm(int id, int maxw, int maxh) {

    Bitmap bm = null;

    int iSamplesize = 1;

    //第一次采样

    BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();

    //该属性设置为true只会加载图片的边框进来,并不会加载图片具体的像素点

    bitmapFactoryOptions.inJustDecodeBounds = true;

    bm = BitmapFactory.decodeResource(getResources(), id, bitmapFactoryOptions);

    Log.e("Tag", "onActivityResult: 压缩之前图片的宽:" + bitmapFactoryOptions.outWidth + "--压缩之前图片的高:"

    + bitmapFactoryOptions.outHeight + "--压缩之前图片大小:" + bitmapFactoryOptions.outWidth * bitmapFactoryOptions.outHeight * 4 / 1024 + "kb");

    int iWidth = bitmapFactoryOptions.outWidth;

    int iHeight = bitmapFactoryOptions.outHeight;

    //对缩放比例进行调整,直到宽和高符合我们要求为止

    while (iWidth > maxw|| iHeight > maxh){

    //如果宽高的任意一方的缩放比例没有达到要求,都继续增大缩放比例

    //sampleSize应该为2的n次幂,如果给sampleSize设置的数字不是2的n次幂,那么系统会就近取

    iSamplesize = iSamplesize*2;//宽高均为原图的宽高的1/2 内存约为原来的1/4

    iWidth = iWidth/iSamplesize;

    iHeight = iHeight/iSamplesize;

    }

    //二次采样开始

    //二次采样时我需要将图片加载出来显示,不能只加载图片的框架,因此inJustDecodeBounds属性要设置为false

    bitmapFactoryOptions.inJustDecodeBounds = false;

    bitmapFactoryOptions.inSampleSize = iSamplesize;

    // 设置像素颜色信息

    // bitmapFactoryOptions.inPreferredConfig = Bitmap.Config.RGB_565;

    bm = BitmapFactory.decodeResource(getResources(),id, bitmapFactoryOptions);

    //默认的图片格式是Bitmap.Config.ARGB_8888

    Log.e("Tag", "onActivityResult: 图片的宽:" + bm.getWidth() + "--图片的高:"

    + bm.getHeight() + "--图片大小:" + bm.getWidth() * bm.getHeight() * 4 / 1024 + "kb");

    return bm;//返回压缩后的照片

    }

    相关文章

      网友评论

          本文标题:图片压缩,二次采样

          本文链接:https://www.haomeiwen.com/subject/rwslyftx.html