相机照相以及处理图片的压缩

来源:互联网 发布:淘宝店铺长时间不用 编辑:程序博客网 时间:2024/04/29 19:10

我们知道在app处理图片是经常会出现oom,原因就是我们在处理图片的时候图片所占的内存太大导致的,这里就介绍怎么去结局图片占内存过大的方法,当然,也是为了自己以后使用能方便些。
这篇文章也是在网上看了很多大牛的文章之后自己整理出来的,希望有错的地方大家提出来,一起学习一起进步。
首先我们介绍下相机的使用:
大致有三种:
1. 缩略图
2. 获得原始的拍照文件
3. 获取Gallery里面的图片
第一种: 缩略图

 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);        startActivityForResult(intent, 1);

第二种:获得原始的拍照文件

 public void OriginalImageView(View view) {        File file = createImageFile();        outputFileUri = Uri.fromFile(file);        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);        intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);        startActivityForResult(intent, 2);    }

第三种:获取Gallery里面的图片

 public void GalleryImageView(View view) {        Intent intent = new Intent(Intent.ACTION_PICK);        intent.setType("image/*");        intent.setAction(Intent.ACTION_GET_CONTENT);        startActivityForResult(intent, 3);    }

上面只是简单介绍照相,下面是所有的代码:

public class MainActivity extends AppCompatActivity {    private ImageView imageview;    private Uri outputFileUri;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        imageview = (ImageView) findViewById(R.id.imageview);    }    /**     * 缩略图     */    public void PriviewImageView(View view) {        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);        startActivityForResult(intent, 1);    }    /**     * 获得原始的拍照文件     */    public void OriginalImageView(View view) {        File file = createImageFile();        outputFileUri = Uri.fromFile(file);        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);        intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);        startActivityForResult(intent, 2);    }    /**     * 获取Gallery里面的图片     */    public void GalleryImageView(View view) {        Intent intent = new Intent(Intent.ACTION_PICK);        intent.setType("image/*");        intent.setAction(Intent.ACTION_GET_CONTENT);        startActivityForResult(intent, 3);    }    @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        super.onActivityResult(requestCode, resultCode, data);        if (requestCode != RESULT_OK) {            if (requestCode == 1) {/**缩略图*/                Bitmap bitmap = data.getExtras().getParcelable("data");                imageview.setImageBitmap(bitmap);            } else if (requestCode == 2) { /**获得原始的拍照文件*/                Toast.makeText(MainActivity.this, "成功", Toast.LENGTH_SHORT).show();                try {                    Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), outputFileUri);                    imageview.setImageBitmap(bitmap);                } catch (IOException e) {                    e.printStackTrace();                }            } else if (requestCode == 3) {/**获取Gallery里面的图片*/                Toast.makeText(MainActivity.this, data.getData().toString(), Toast.LENGTH_SHORT).show();                getRealPathFromURI(data.getData());/**获取图片真实路径*/            }        }    }    public static File createImageFile() {        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());        String imageFileName = "JPEG_" + timeStamp + "_";        try {            File image = File.createTempFile(imageFileName, ".jpg", Environment.getExternalStorageDirectory());            return image;        } catch (IOException e) {            return null;        }    }    private String getRealPathFromURI(Uri contentURI) {        String result;        Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);        if (cursor == null) {            result = contentURI.getPath();        } else {            cursor.moveToFirst();            int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);            result = cursor.getString(idx);            cursor.close();        }        return result;    }}

其次,在处理图片之前,我们应该了解BitMap和BitmapFactory的一些常用方法,在这里我简单介绍几种常用的。
BitMap:
1.计算图片像素的方法是 bitmap.getWidth()和bitmap.getHeight();
2.bitmap.getByteCount() 是计算它的像素所占用的内存,(bitmap.getWidth()*bitmap.getHeight()*4=bitmap.getByteCount());
3.compress(Bitmap.CompressFormat.JPEG, 100, os),第一个参数是图片的类型,第二个参数是指图片质量(设置为30时是指图片质量被压缩了百分之70,最大100,最小0),第三个参数是指图片流。

BitmapFactory:
1.BitmapFactory.Options使用这个对象去处理图片的属性;
2.inJustDecodeBounds如果是true的话只会返回图片的宽和高;
3.inSampleSize是指设置缩放比例,设置为2时变为原来的1/2(这里的1/2值得是宽和高分别是原图的1/2,所以图片是被压缩了4倍);
4.BitmapFactory.decodeFile(path,options),这个方法的path是图片的路径,options就是BitmapFactory.Options的对象。
5.BitmapFactory.decodeFile(path),返回的是这个路径下图片的Bitmap;
6.BitmapFactory.decodeStream(is, null, options),这个方法is是图片的流,null 是指位图,我们可以设置为nullm,options就是BitmapFactory.Options的对象。
7.BitmapFactory.decodeStream(is),将is转化为BitMap对象;

图片压缩有两种方法,一种是质量压缩((不改变图片的尺寸)),一种是尺寸压缩(像素上的压缩也成采样率压缩)。

测试图片在压缩前和压缩后的大小:
FileInputStream fs = new FileInputStream(pathName);
System.out.println(“图片的大小==”+fs.available());
测试图片所占内存大小:
Bitmap bm = BitmapFactory.decodeFile(pathName);;
System.out.println(“内存的大小==”+bm.getByteCount());
System.out.println(“内存大小的另一种求法==”+bm.getWidth() * bm.getHeight()*4 );

  1. 质量压缩
    质量压缩不会减少图片对内存的使用,但是开发app的程序员都知道我们会尽量为用户减少流量,或者将图片保存到本地时进行压缩,所以使用质量压缩会减少图片的大小,这样上传图片时速度快(因为图片变小了),用户体验就会好。
    下面就是方法:
     FileInputStream fs = new FileInputStream(pathName);       System.out.println("图片压缩前的大小=="+fs.available());      public static Bitmap CompressBitmap(Bitmap bitmap){              System.out.println("质量压缩前内存="+bitmap.getByteCount());              ByteArrayOutputStream bo=new  ByteArrayOutputStream();              //通过这里改变压缩类型,其有不同的结果              bitmap.compress(Bitmap.CompressFormat.JPEG, 70, bo); //注意:不要讲jpg格式的图片压缩成png格式的图片,如果设置成png的话图片可能会变大,但是内存不会发生变化            ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());              System.out.println("图片压缩后的大小="+bi.available());              Bitmap  bitmap=BitmapFactory.decodeStream(bis);              System.out.println("质量压缩后内存="+bitmap.getByteCount());        return bitmap;      }        

通过实现我们得出图片压缩前和压缩后的大小确实变了,压缩后的比压缩前的小,但是图片的内存使用没有变。

但是,它不会减少图片的像素。它是在保持像素的前提下改变图片的位深及透明度等,来达到压缩图片的目的。进过它压缩的图片文件大小会有改变,但是导入成bitmap后占得内存是不变的。因为要保持像素不变,所以它就无法无限压缩,到达一个值之后就不会继续变小了。显然这个方法并不适用与缩略图,其实也不适用于想通过压缩图片减少内存的适用,仅仅适用于想在保证图片质量的同时减少文件大小的情况而已
例如:

//最开始使用这个来进行压缩,但是始终压缩不到32k这么小public void Example(){ByteArrayOutputStream baos = new ByteArrayOutputStream();  image.compress(Bitmap.CompressFormat.JPEG, 100 , baos);  int options = 100 ;  while ( baos.toByteArray().length / 1024 > 32 ) {  baos.reset();  image.compress(Bitmap.CompressFormat.JPEG, options, baos);  options -= 10 ;  }  ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());  Bitmap bitmap = BitmapFactory.decodeStream(isBm, null , null );}

2.尺寸压缩(也成采样率压缩)
尺寸压缩会改变图片所占内存的大小,也就是解决我们在加载图片时有时候会出现的OOM。
第一种: 根据图片路径,下面就是方法:

 public static Bitmap CompressBitmapSizePath(String imagePath){        Bitmap bitmap = BitmapFactory.decodeFile(pathName);         System.out.println("内存压缩前=="+bitmap.getByteCount());           BitmapFactory.Options options = new BitmapFactory.Options();          options.inJustDecodeBounds = true; //设置成true我们我是为了得到图片的宽高,这个方法返回的Bitmap对象为空,减少内存的消耗        Bitmap bitmap = BitmapFactory.decodeFile(imagePath,options);  // 这个方法返回的bitmap 为空,因为options.inJustDecodeBounds设置为true        options.inJustDecodeBounds = false;          options.inSampleSize = 2; //原来的1/2         bitmap = BitmapFactory.decodeFile(imagePath,options);          System.out.println("内存压缩后=="+bitmap.getByteCount());         return bitmap;      }

第二种:根据流,下面就是方法:

public static Bitmap CompressBitmapSizeIs(Context context, int resId){         BitmapFactory.Options opt = new BitmapFactory.Options();         opt.inPreferredConfig = Bitmap.Config.RGB_565;          opt.inPurgeable = true;         opt.inInputShareable = true;         opt.inSampleSize = 2;       //获取资源图片         InputStream is = context.getResources().openRawResource(resId);         return BitmapFactory.decodeStream(is,null,opt);     }  

第三中:根据Bitmap,以下就是方法:

 public Bitmap CompressBitmapSizeBitMap(Bitmap image, float pixelW, float pixelH) {          ByteArrayOutputStream os = new ByteArrayOutputStream();          image.compress(Bitmap.CompressFormat.JPEG, 100, os);          if( os.toByteArray().length / 1024>1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出                  os.reset();//重置baos即清空baos                image.compress(Bitmap.CompressFormat.JPEG, 50, os);//这里压缩50%,把压缩后的数据存放到baos中            }            ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());            BitmapFactory.Options newOpts = new BitmapFactory.Options();            //开始读入图片,此时把options.inJustDecodeBounds 设回true了            newOpts.inJustDecodeBounds = true;          newOpts.inPreferredConfig = Config.RGB_565;          Bitmap bitmap = BitmapFactory.decodeStream(is, null, newOpts);            newOpts.inJustDecodeBounds = false;            int w = newOpts.outWidth;            int h = newOpts.outHeight;            float hh = pixelH;// 设置高度为240f时,可以明显看到图片缩小了          float ww = pixelW;// 设置宽度为120f,可以明显看到图片缩小了          //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可            int be = 1;//be=1表示不缩放            if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放                be = (int) (newOpts.outWidth / ww);            } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放                be = (int) (newOpts.outHeight / hh);            }            if (be <= 0) be = 1;            newOpts.inSampleSize = be;//设置缩放比例            //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了            is = new ByteArrayInputStream(os.toByteArray());            bitmap = BitmapFactory.decodeStream(is, null, newOpts);          //压缩好比例大小后再进行质量压缩  //      return compress(bitmap, maxSize); // 这里再进行质量压缩的意义不大,反而耗资源,删除          return bitmap;      }  

使用中可以根据自己的需要自己选择,最后给大家提供一个完美的压缩图片尺寸的方法,也是我在网上找到的:

//根据路径获得突破并压缩返回bitmap用于显示    public static Bitmap getSmallBitmap(String filePath,int reqWidth, int reqHeight) {        final BitmapFactory.Options options = new BitmapFactory.Options();        options.inJustDecodeBounds = true;  //只返回图片的大小信息        BitmapFactory.decodeFile(filePath, options);        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);        options.inJustDecodeBounds = false;        return BitmapFactory.decodeFile(filePath, options);    }//计算图片的缩放值    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {        final int height = options.outHeight;        final int width = options.outWidth;        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 = heightRatio < widthRatio ? heightRatio : widthRatio;        }        return inSampleSize;    }

最后,使用完BitMap是我们一般要:
bmp.recycle() ; //回收图片所占的内存
system.gc() //提醒系统及时回收 ,回收内存。
另外,一般不要不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存,以下提供一个优化方法(也就是尺寸压缩中的第二个方法,只是一个优化):

public static Bitmap readBitMap(Context context, int resId){         BitmapFactory.Options opt = new BitmapFactory.Options();         opt.inPreferredConfig = Bitmap.Config.RGB_565;          opt.inPurgeable = true;         opt.inInputShareable = true;         opt.inSampleSize = 2;       //获取资源图片         InputStream is = context.getResources().openRawResource(resId);         return BitmapFactory.decodeStream(is,null,opt);     }  
0 0
原创粉丝点击