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

Posted mclmdream

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了相机照相以及处理图片的压缩相关的知识,希望对你有一定的参考价值。

我们知道在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);  
   }  

以上是关于相机照相以及处理图片的压缩的主要内容,如果未能解决你的问题,请参考以下文章

Android调用照相机和百度地图开发,百度地图显示界面覆盖了相机界面,求大神解答?

Android 图像选取 图片剪裁 照相选图 照相裁剪 大全 6-19更新

修正iOS从照相机和相册中获取的图片 方向

Android 实现拍照功能,并将图片保存到本地存储

Threejs - 相机? 什么是相机? 照相机?

raw是啥意思