android开发笔记 -摄像头开发

2016/8/28:摄像头开发


1. 控制摄像头在应用程序内拍摄照片

  • 使用Intent拍摄照片

    为在应用程序中拍摄照片,最简单的方法就是使用 MediaStore.ACTION_IMAGE_CAPTURE动作触发一个Intent:

        //请求码
        public static final int TAKE_PICTURE = 100;
        startActivityForResult(new Intent(MediaStore.ACTION_IMAGE_CAPTURE),TAKE_PICTURE);

    这将启动一个Camera应用程序来拍摄照片,不需要你重写原生Camera应用程序,就可以提供全套的摄像头功能。

    NOTE:

    用户对拍摄的照片满意后,该照片就会通过onActivityResult处理程序收到的Intent返回给应用程序

    获取缩略图

    默认情况下,拍摄的照片将会作为一个缩略图返回,通过返回的Inetnt的data extra 可以访问原始位图

    获取完整图像

    要获得完整图像,必须指定一个用于存储该图像的目标文件,该文件将会被编码为一个URI,并在启动Inent中使用MediaStore.EXTRA_OUTPUT extra 传入该URI

    //目标输出文件URI
    private Uri outputFileUri ;
    
    public void getOriginCapture() {
        //获取输出文件
        File file = new File(Environment.getExternalStorageDirectory(), "test.jpg");
        Log.e("output_file", file.toString());
       outputFileUri = Uri.fromFile(file);
    
        //生成Intent
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
        //启动摄像头拍照
        startActivityForResult(intent, TAKE_PICTURE);
    
    }
    
    然后摄像头拍摄的完整图像就会保存到指定位置。Activity结果回调中不会返回缩略图,接受的Intent的数据将是null。

    使用getParcelableExtra()来实现两个目的:

    1.当返回了缩略图提示,提取一个缩略图;

    2.当拍摄了完整的照片时,解码保存的图片;

    代码:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    
        if (requestCode == TAKE_PICTURE) {
            //检查结果是否包含缩略图
            if (data != null) {
                if (data.hasExtra("data")) {
                    Bitmap thumbnail = data.getParcelableExtra("data");
                    iv.setImageBitmap(thumbnail);
                }
            } else {//如果不是缩略图的话,接受的Intent 即data == null,说明目标存储在目标输出URI中
                //计算待填充的view的大小。
                int width = iv.getWidth();
                int height = iv.getHeight();
    
                BitmapFactory.Options ops = new BitmapFactory.Options();
                ops.inJustDecodeBounds = true;//只加载基本图片的信息。
                Bitmap boundBitmap = BitmapFactory.decodeFile(outputFileUri.getPath(), ops);
    
                int xRate = ops.outWidth / width;
                int yRate = ops.outHeight / height;
                //获取到最总缩放倍数  r 越大, 图片越小
                // 一般是2的倍数 r = 2 ,缩小到原图的1/4.-->依次类推
                int r = Math.min(xRate, yRate);
                ops.inSampleSize = r;
    
                ops.inJustDecodeBounds = false;
                ops.inPurgeable = true;
    
                Bitmap bitmap = BitmapFactory.decodeFile(outputFileUri.getPath(), ops);
    
                iv.setImageBitmap(bitmap);
    
    
            }
    
    
        }
    
    
    }

    我们可以将图片保存到媒体库中,方便其他应用程序调用。

    NOTE:

    在Manifest中添加读、写外部权限:

        //从外部存储读取的权限
       <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
       //向外部存储写入的权限
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    

2.直接控制摄像头

  • 2.1 . 声明权限

    //为了直接访问摄像头,需要在应用程序的manifest文件中添加CAMERA权限
    <uses-permission android:name="android.permission.CAMERA"/>
  • 2.2 .使用摄像头

    使用Camera类可以调整摄像头设置、指定图片首选项和拍摄照片。要访问摄像头,需要使用Camera类的静态方法open方法。

    NOTE:这里是android.hardware.Camera ,不是 android.graphics.Camera。

        import android.hardware.Camera:
    
        //打开并初始化摄像头。
        //此时可以修改配置,配置预览Surface和拍摄照片
        Camera camera = Camera.open();

    使用完摄像头,需要调用release来释放camera资源。

        //释放摄像头资源
        camera.release();   
    • 2.2.1:摄像头属性

      通过调用Camera对象的getParameters方法可以得到CamearParameter对象,然后可以使用该对象存储摄像头设置。

          /**
           * 初始化摄像头。
           */
          private void initCamera() {
      
              //打开摄像头并初始化,获取一个实例。
              camera = Camera.open();
      
              //获取摄像头属性集
              Camera.Parameters parameters = camera.getParameters();
      
              //使用摄像头可以获取摄像头的许多属性和对焦的场景。
              //android 2.2(API level 8)中引入的getFocalLength 和 get[Horizontal/Vertical]ViewAngle方法
              //分别可以得到焦距和相关的水平或垂直视角
              float focalLength = parameters.getFocalLength();
      
              float horizontalViewAngle = parameters.getHorizontalViewAngle();
      
              float verticalViewAngle = parameters.getVerticalViewAngle();
      
      
              //android 2.3(API level 9) 中引入 getFocusDistances()方法,用于估算镜头和当前被对焦的物体之间的距离。
              //这个方法并不返回值,而是填充一个与近、远和最佳距离对应的浮点数组。
      
              float[] focusDistances = new float[3];
              parameters.getFocusDistances(focusDistances);
              //近
              float near = focusDistances[Camera.Parameters.FOCUS_DISTANCE_NEAR_INDEX];
              //最佳
              float optimal = focusDistances[Camera.Parameters.FOCUS_DISTANCE_OPTIMAL_INDEX];
              //远
              float far = focusDistances[Camera.Parameters.FOCUS_DISTANCE_FAR_INDEX];
      
              Log.e("printer", sb.toString());
      
          }
      

      结果图

      android开发笔记 -摄像头开发_第1张图片

    • 2.2.2. 摄像头设置和图像参数

      要更改摄像头设置,需要使用set*方法来修改Parameters参数。Android 2.0 引入了大量的摄像头参数,每个参数都有对应的setter和getter方法。在修改任何摄像头的参数之前,确定设备商的摄像头实现支持这种修改该十分重要。

      修改完成之后,需要使用摄像头的setParameters方法来应用修改。

              camera.setParameters(parameters);

      如果要替换原生Camera应用程序,下面的大部分参数都很有用。

      NOTE:可以使用他们自定义实时视频的显示方式,从而为增强现实应用程序自定义实时流.

      Camera.Parameters 方法名【set/get】 方法说明
      SceneMode 使用一个SCENE_MODE_*静态常量返回或设置所拍摄的场景的类型。每个场景模式都为特定的场景类型(聚会、海滩、落日等)优化了摄像头参数的配置。
      FLASHMODE 使用FLASH_MODE_*静态常量返回或设置当前的闪光模式(通常为“打开”、“关闭”、“红眼消除”、“闪光灯”模式)
      WhiteBalance 使用WHITE_BALANCE_*静态常量返回或设置白平衡矫正来矫正场景。在设置白平衡之前,用【getSupportedWhiteBalance】方法来确认那些设置可用。
      AutoWhiteBalanceLock android 4.0引入。当使用自动白平衡时,启用自动白平衡锁会暂停颜色矫正算法,从而确保连续拍摄多张照片使用相同的颜色平衡设置。当拍摄全景照片或者为高动态光照渲染图象使用包围曝光时,这种方法特别有用。使用【isAutoWhiteBalanceLockSupported】方法可以确认设备是否支持这种功能。
      ColorEffect 使用一个Effect_*静态常量返回或设置应用到图像的特殊颜色效果。可用的颜色效果(包括 深褐色调、多色调分色印或者黑板效果)随设备和平台而异。
      FocusMode 使用 FOCUS_MODE_*静态常量或者设置摄像头尝试对焦的方式。(连续自动对焦在android 4.0引入)使用【getSupportedFocusModes】方法可以找出可用的模式
      Antibanding 使用一个ANTIBANDING_*静态常量或设置用来降低条带效果的屏幕刷新频率。使用【getSupprotedAntibanding】方法可以找出可用的频率。

      还可以使用Camera.Parameters来读取或指定图像、缩略图和摄像头预览的大小、质量的格式参数。

      Camera.Parameters 方法名【set/get】 方法说明
      JPEG和缩略图质量 使用【setJpegQuality】和【setJpegThumbnailQuality】方法,并传入0~100之间的整数,其中100表示最佳质量。
      图像、预览和缩略图大小 分别使用【setPictureSize】,【setPreviewSize】,【setJpegThumbnailSize】参数指定图像‘预览和缩略图的高度和宽度。对于每种情况,分别使用【getSupportedPictureSizes】,【getSupportedPreviewSizes】,【getSupportedJpegThumbnailSizes】方法来确定有效的值。每个方法返回一个指定了有效高度/宽度的Camera.Size对象的列表。
      图像和预览像素格式 使用PixelFormat类中的要给静态常量调用【setPictureFormat】和【setPreviewFormat】可以设置图像的格式。在使用这两个setter方法之前,可以使用【getSupportedPictureFormats】和【getSupportedPreviewFormats】方法返回支持格式的列表。
      预览帧速率 【setPreviewFpsRange】方法取代了已在Android 2.3(API level 9)中启用的setPreviceFrameRate方法。它可以用来指定预览的首选帧率范围。使用【getSupportedPreviewFpsRange】可以找出所支持的最低和最高帧率。【这两个方法都采用一个整数乘以1000的形式表示帧率,所以帧率的范围24~30 表示为 24000 ~ 30000】

    • 2.2.3:控制自动对焦、对焦区域和测光区域

      如果设备的摄像头支持自动对焦,那么可以通过调用【setFocusMode】方法并传入一个Camera.Parameters.FOCUS_MODE_*常量来指定对焦模式。可用的对焦模式取决于具体的硬件的能力以及硬件上运行的android平台。使用【getSupportedFocusModes】方法可以找出可用的对焦模式。

      为了能够在自动对焦操作完成时得到通知,需要使用autofocus方法启动自动对焦,并指定AutoFocusCallback实现。

      public void method_demo{
      
              Camera.Parameters parameters = camera.getParameters();
                                      if(parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
               parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
              }
      
              //设置自动对焦监听回调。
              camera.autoFocus(new Camera.AutoFocusCallback() {
      
      
                  @Override
                  public void onAutoFocus(boolean success, Camera camera) {
                      Log.e(TAG, "对焦成功:" + (success ? "Successed" : "Failed"));
                  }
              });
      
          }

      Android 4.0(API level 14)引入了另外两个对焦API,用于在对焦图像或者确定场景的白平衡和亮度时指定对焦区域和测光区域。
      并不是所有的设备都支持定义对焦区域,因此需要使用Camera.Parameters的 getMaxNumFocusAreas 方法来确定设备是否支持该功能

          int focusAreaCount = camera.getParameters().getMaxNumFocusAreas();

      该方法返回设备的摄像头能够检测到的最大对焦区域数。如果结果为0,说明设备不支持定义对焦区域。

      通过指定对焦区域,摄像头驱动程序就知道在尝试对焦图片时,不同区域的相对重要程度。此方法通常用于脸部对焦或者让用户手动选择对焦。

      想要定义对焦区域,需要使用setFocusAreas方法,并传入一个Camera.Area对象的列表。每一个Camera Area都有一个矩形,该矩形定义了相对于当前可见场景的对焦区域的边界(在-1000~1000)之间,从左上角开始测量。以及该聚焦区域的重要性的权值所组成。当尝试对焦场景时,摄像头驱动程序会把每个对焦区域的面积与其权值相乘以计算出每个区域的相对权重。

      可以使用setMeteringAreas方法以同样的方式设置测光区域。与对焦区域类似,并不是所有的设备都支持测光区域–使用getMaxNumMeteringAreas来确定摄像头是否支持一个或更多个测光区域。

          //获取最大对焦区域数
          List list = null;
              int focusAreaCount = camera.getParameters().getMaxNumFocusAreas();
              //获取最大测光区域数
              int maxNumMeteringAreas = camera.getParameters().getMaxNumMeteringAreas();
      
              if (focusAreaCount != 0 || maxNumMeteringAreas != 0) {
                  list = new ArrayList<>();
                  Rect rect = new Rect(0, 0, 100, 100);
                  Rect rect1 = new Rect(100, 100, 200, 200);
                  Camera.Area area = new Camera.Area(rect, 1);
                  Camera.Area area1 = new Camera.Area(rect1, 2);
                  list.add(area);
                  list.add(area1);
              }
              if (focusAreaCount != 0)
                  camera.getParameters().setFocusAreas(list);//设置对焦区域
              if (maxNumMeteringAreas != 0)
                  camera.getParameters().setMeteringAreas(list);//设置测光区域

    • 2.2.4: 使用摄像头预览
      在实现自己的摄像头时,需要显示摄像头捕获的内容的一个预览,以便用户可以选择拍摄什么样的照片。不先使用一个预览,是无法使用Camera对象拍摄照片的

      显示摄像头的流式传输视频还意味着将实时视频融入到应用程序中,例如实时增强现实(在实时摄像头数据源之上覆盖动态上下文数据的过程–例如,界标或者感兴趣的地方的详细信息);

      摄像头预览是使用SurfaceHolder显示的,所以要在应用程序中查看实时摄像头流,必须在UI层次中包含一个Surface View.需要实现一个SurfaceHolder.Callback来监听有效表面的构建,然后把该表面传递给Camera对象的setPreviewDisplay方法。

      调用startPreview将开始流式传输,调用stopPreview将结束流式传输。

      //SurfaceHolder.Callback的接口方法定义
          /**
           * A client may implement this interface to receive information about
           * changes to the surface.  When used with a {@link SurfaceView}, the
           * Surface being held is only available between calls to
           * {@link #surfaceCreated(SurfaceHolder)} and
           * {@link #surfaceDestroyed(SurfaceHolder)}.  The Callback is set with
           * {@link SurfaceHolder#addCallback SurfaceHolder.addCallback} method.
           */
          public interface Callback {
              /**
               * This is called immediately after the surface is first created.
               * Implementations of this should start up whatever rendering code
               * they desire.  Note that only one thread can ever draw into
               * a {@link Surface}, so you should not draw into the Surface here
               * if your normal rendering will be in another thread.
               * 
               * @param holder The SurfaceHolder whose surface is being created.
               */
              public void surfaceCreated(SurfaceHolder holder);
      
              /**
               * This is called immediately after any structural changes (format or
               * size) have been made to the surface.  You should at this point update
               * the imagery in the surface.  This method is always called at least
               * once, after {@link #surfaceCreated}.
               * 
               * @param holder The SurfaceHolder whose surface has changed.
               * @param format The new PixelFormat of the surface.
               * @param width The new width of the surface.
               * @param height The new height of the surface.
               */
              public void surfaceChanged(SurfaceHolder holder, int format, int width,
                      int height);
      
              /**
               * This is called immediately before a surface is being destroyed. After
               * returning from this call, you should no longer try to access this
               * surface.  If you have a rendering thread that directly accesses
               * the surface, you must ensure that thread is no longer touching the 
               * Surface before returning from this function.
               * 
               * @param holder The SurfaceHolder whose surface is being destroyed.
               */
              public void surfaceDestroyed(SurfaceHolder holder);
          }

      预览实时摄像头流

      CameraPreviewActivity.java
      “`
      package com.aaron.anew;

      import android.app.Activity;
      import android.hardware.Camera;
      import android.os.Bundle;
      import android.util.Log;
      import android.view.SurfaceHolder;
      import android.view.SurfaceView;
      
      import java.io.IOException;
      
      /**
       * Created by pc on 2016/8/29.
       * 摄像头预览
       */
      public class CameraPreviewActivity extends Activity implements SurfaceHolder.Callback {
      
      
          private static final String TAG = "CameraPreviewActivity";
      
          private Camera camera;
      
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_preview);
      
              init();
      
          }
      
          private void init() {
              SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surface_view);
              SurfaceHolder holder = surfaceView.getHolder();
              holder.addCallback(this);
              holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
              holder.setFixedSize(400, 300);
      
      
          }
      
          @Override
          public void surfaceCreated(SurfaceHolder holder) {
              try {
                  camera.setPreviewDisplay(holder);
                  camera.startPreview();
      
              } catch (IOException e) {
      
                  Log.e(TAG, e.getMessage());
      
              }
          }
      
          @Override
          public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
      
          }
      
          @Override
          public void surfaceDestroyed(SurfaceHolder holder) {
              camera.stopPreview();
          }
      
          @Override
          protected void onResume() {
              super.onResume();
              if (camera == null)
                  camera = Camera.open();
          }
      
          @Override
          protected void onPause() {
              super.onPause();
              if (camera != null)
                  camera.release();
          }
      }
      

      android开发笔记 -摄像头开发_第2张图片


      还可以分配一个PreviewCallback,使其在每个预览帧中触发,以便可以实时操纵或者分析每个预览帧,需要调用Camera对象的setreviewCallback方法,并传入一个重写了onPreviewFrame方法的新的PreviewCallback实现

      onPreviewFrame事件会受到每一帧,其中作为一个Bitmap传入的图像将被表示为一个字节数组。

未完

你可能感兴趣的:(android高级进阶)