Camera2.0新API下的摄像头预览、原始图像数据获取等

最近刚入职了一家公司,这家公司是做人眼虹膜识别的,所以用到的主要就是Camera相关的知识,公司之前的产品都是基于Camera1,公司就派我去了解研究Camera2。这里我就写下这10天左右我对Camera一窍不通到现在的认识与了解吧。

(注:本文不是从底层讲起,讲Camera2.0的架构之类的,只是讲讲我所掌握了的实用技巧,因为我学习过程中我的很多需求百度不到,写在次一是加深自己印象,而是献给有需要的人)

首先Camera2.0(android.hardware.Camera2)是Android 5.0(API Level 21)推出来用以取代在此版本后过时了Camera(android.hardware.Camera)。Camera2从底层架构就已经与原Camera已经大不相同了。具体的架构和那些官方所述的好处大家可自行百度,网上这样的博客很多。我接下直接说下主要用到的几个类吧。

主要用到的5个类:

  1.  CameraManager:是通过getSystemService(Context.CAMERA_SERVICE);拿到所有相机的管理者。
  2. CameraDevice: 通过CameraManager返回的一个可用摄像头,原过时的Carema(android.hardware.Camera)对应,但是API很不一样了。
  3. 个CameraDevice自己会负责建立CameraCaptureSession以及建立CaptureRequest。
  4. CameraCaptureSession:某个CameraDevice的拍照请求会话,很重要的一个类。
  5. CaptureRequest:一个拍照等的请求对象。
  6. CameraCharacteristics: 摄像头的特征,即这个类里封装着该手机摄像头支持的参数。

它们之间的关系大概就是:首先通过getSystemService(Context.CAMERA_SERVICE)拿到CameraManger,然后通过CameraManger拿到某个摄像头的CameraCharacteristics,得到支持的参数啊CameraCharacteristics以及预览尺寸等信息。然后再通过CameraManger的openCamera拿到一个具体的摄像头对象CameraDevice。该方法有一个参数会要求传递一个回调,当摄像头打开时会该方法:onOpen,然后再在该方法了开启摄像头预览,绑定Surface。大概就这样,直接看Demo吧。

权限

<uses-permission android:name="android.permission.CAMERA"/>
布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <TextureView
        android:id="@+id/textureview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"/>

</LinearLayout>
核心代码

public class Camera2Demo extends Activity implements TextureView.SurfaceTextureListener {

    private TextureView mPreviewView;
    private Handler mHandler;
    private HandlerThread mThreadHandler;
    private Size mPreviewSize;
    private CaptureRequest.Builder mPreviewBuilder;
    @Override
    public void onCreate(Bundle savedInstanceState) {     
        super.onCreate(savedInstanceState);
        setContentView(R.layout.camera);
	
	initView();
        initLooper();
      
    }
    //很多过程都变成了异步的了,所以这里需要一个子线程的looper
    private void initLooper() {
        mThreadHandler = new HandlerThread("CAMERA2");
        mThreadHandler.start();
        mHandler = new Handler(mThreadHandler.getLooper());
    }
    //可以通过TextureView或者SurfaceView
    private void initView() {
        mPreviewView = (TextureView) v.findViewById(R.id.textureview);
        mPreviewView.setSurfaceTextureListener(this);
    }

  
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        try {
            //获得所有摄像头的管理者CameraManager
            CameraManager cameraManager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);
            //获得某个摄像头的特征,支持的参数
            CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics("0");
            //支持的STREAM CONFIGURATION
            StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            //摄像头支持的预览Size数组
            mPreviewSize = map.getOutputSizes(SurfaceTexture.class)[0];
            //打开相机
            cameraManager.openCamera("0", mCameraDeviceStateCallback, mHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        return false;
    }

    // 这个方法要注意一下,因为每有一帧画面,都会回调一次此方法
    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {

    }

    private CameraDevice.StateCallback mCameraDeviceStateCallback = new CameraDevice.StateCallback() {

        @Override
        public void onOpened(CameraDevice camera) {
            try {
                startPreview(camera);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onDisconnected(CameraDevice camera) {

        }

        @Override
        public void onError(CameraDevice camera, int error) {

        }
    };
    // 开始预览,主要是camera.createCaptureSession这段代码很重要,创建会话
    private void startPreview(CameraDevice camera) throws CameraAccessException {
        SurfaceTexture texture = mPreviewView.getSurfaceTexture();

//      这里设置的就是预览大小
        texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());</span>
        Surface surface = new Surface(texture);
        try {
            // 设置捕获请求为预览,这里还有拍照啊,录像等
            mPreviewBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

//      就是在这里,通过这个set(key,value)方法,设置曝光啊,自动聚焦等参数!! 如下举例:
//      mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);



	mImageReader = ImageReader.newInstance(mSurfaceView.getWidth(), mSurfaceView.getHeight(), ImageFormat.JPEG/*此处还有很多格式,比如我所用到YUV等*/, 2/*最大的图片数,mImageReader里能获取到图片数,但是实际中是2+1张图片,就是多一张*/); 
        
        mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mHandler);
       // 这里一定分别add两个surface,一个Textureview的,一个ImageReader的,如果没add,会造成没摄像头预览,或者没有ImageReader的那个回调!!
        mPreviewBuilder.addTarget(surface);
	mPreviewBuilder.addTarget(mImageReader.getSurface());
        mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),mSessionStateCallback, mHandler);
    }

    private CameraCaptureSession.StateCallback mSessionStateCallback = new CameraCaptureSession.StateCallback() {

        @Override
        public void onConfigured(CameraCaptureSession session) {
            try {
                updatePreview(session);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onConfigureFailed(CameraCaptureSession session) {

        }
    };

    private void updatePreview(CameraCaptureSession session) throws CameraAccessException {
        session.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
    }

    private ImageReader.OnImageAvailableListener mOnImageAvailableListener
            = new ImageReader.OnImageAvailableListener() {

        /**
         *  当有一张图片可用时会回调此方法,但有一点一定要注意:
         *  一定要调用 reader.acquireNextImage()和close()方法,否则画面就会卡住!!!!!我被这个坑坑了好久!!!
         *    很多人可能写Demo就在这里打一个Log,结果卡住了,或者方法不能一直被回调。
         **/
        @Override
        public void onImageAvailable(ImageReader reader) {
             Image img = reader.acquireNextImage();
             /**
              *  因为Camera2并没有Camera1的Priview回调!!!所以该怎么能到预览图像的byte[]呢?就是在这里了!!!我找了好久的办法!!!
              **/
             byte[] data = new byte[reader.remain()]; 
              img.close();
            }
         };
 }

             
 
 
 


        怎么用Camera预览拍照,网上的Demo和博客很多,但是Camera2是不存在Camera1中的Preview回调的,也就是要拿到原始预览图像的baye数组或者图像图片,就像上文中红色标注的  onImageAvailable方法里那样做,而且有一些比较容易被坑的地方我也提醒了,让看到人少走点弯路啊。


你可能感兴趣的:(android,摄像头,camera2,原始图像数据获取)