Android Camera2 开发文档说明

Posted 山海城

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Camera2 开发文档说明相关的知识,希望对你有一定的参考价值。

参考:
Camera v2文档1:https://www.jianshu.com/p/73fed068a795
Camera v2文档2:https://blog.csdn.net/qq_38106356/article/details/77996319
github参考代码:https://github.com/googlesamples/android-Camera2Basic
参数调节关键字翻译集合,常用关键字解析文档:
https://blog.csdn.net/qq_29333911/article/details/79400617
https://blog.csdn.net/sadamoo/article/details/50370702
官方文档:https://developer.android.com/reference/android/hardware/camera2/CaptureRequest.html

 

一、前言:
    Android 5.0(21)之后android.hardware.Camera就被废弃了,取而代之的是全新的android.hardware.Camera2。
    Android 5.0对拍照API进行了全新的设计,新增了全新设计的Camera v2 API,这些API不仅大幅提高了Android系统拍照的功能,还能支持RAW照片输出,甚至允许程序调整相机的对焦模式、曝光模式、快门等。

常识
------------相机参数:快门
快门就是相当于是一个门,在拍照的时候光通过这个门时间。快门速度越快,进光越少,那么拍照的速度就会越快。在运动中我们的快门速度通常会设置的快一点的,快门快的可以将运动的人拍的清晰,而且可以拍到运动中我们人眼看不到的动作

------------相机参数:光圈
光圈值越小,光圈越大,相当于相机的门较大,进入的光较多。在光弱的时候我通常是加大光圈的,加大光圈就是在手机上把光圈值调小就可了。光圈越大,焦点之外的清晰范围越小,焦点之外的模糊程度就越大。这里所说的模糊是虚焦模糊,也就是没对上焦的模糊。

------------相机参数 :感光度
感光度大部分都是自动的,相机的感光度顾名思义就是相片的亮度。在亮度低的地方我们通常要调高感光度的,同时也要减少曝光时间的,感光度高的时候需要更长的时间来提高相片的亮度的,一般在黑暗中提高感光度的同时一般都要提高曝光时间,曝光时间是通过快门调的,这时候就需要将快门时间调大点就可以。
感光度达到一定数值后(根据相机性能不同而不同),其值越高,画面中的噪点就越多,物体细节就越粗糙,画面的颗粒感就越重。进而会直接影响到画面细节表现,并且产生色彩偏差的可能性就越大。

 


二、Camera v2主要类说明
1、CameraManager:摄像头管理器。这是一个全新的系统管理器,专门用于检测系统摄像头、打开系统摄像头。
2、CameraCharacteristics:硬件摄像头特性。该对象通过CameraManager来获取,用于描述特定摄像头硬件所支持的各种特性。调用CameraManager的getCameraCharacteristics(cameraID)方法即可获取指定摄像头的相关特性。
3、CameraDevice:当前打开的摄像头类。该类的功能类似于早期的Camera类
4、CameraCaptureSession:这是一个非常重要的API,当程序需要预览、拍照时,都需要先通过该类的实例创建Session。而且不管预览还是拍照,也都是由该对象的方法进行控制的,其中控制预览的方法为setRepeatingRequest();控制拍照的方法为capture()。
5、CameraMetadata:控制相机和带有相机参数的基础类,它的子类是: CameraCharacteristics,CaptureRequest,CaptureResult。


三、使用
1、权限,6.0以后添加动态权限的获取:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

 

2、获取相机硬件属性
CameraManager manager = (CameraManager) ctx.getSystemService(Context.CAMERA_SERVICE);
        try
            //获取可用摄像头列表
            for (String cameraId : manager.getCameraIdList())
                //获取相机的相关参数
                CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
                // 不使用前置摄像头。
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT)
                    continue;
               
                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                if (map == null)
                    continue;
               
                // 检查闪光灯是否支持。
                Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
                mFlashSupported = available == null ? false : available;

        //CameraID
                mCameraId = cameraId;
                Log.e(TAG," 相机可用 ");
                return;
           
        catch (CameraAccessException e)
            e.printStackTrace();
        catch (NullPointerException e)
            //不支持Camera2API
       
   

 

3、打开指定相机(mCameraId)
1)打开相机
    CameraManager manager = (CameraManager) ctx.getSystemService(Context.CAMERA_SERVICE);
        try
            //打开相机预览
            manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
        catch (CameraAccessException e)
            e.printStackTrace();
        catch (InterruptedException e)
            throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
       


2)回调,获取当前相机设备的java对象mCameraDevice。
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback()

        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice)
            mCameraDevice = cameraDevice;
            //创建CameraPreviewSession
            createCameraPreviewSession();
       

        @Override
        public void onDisconnected(@NonNull CameraDevice cameraDevice)
            cameraDevice.close();
            mCameraDevice = null;
       

        @Override
        public void onError(@NonNull CameraDevice cameraDevice, int error)
            cameraDevice.close();
            mCameraDevice = null;
       

    ;


3)mBackgroundHandler定义: onResume中调用startBackgroundThread;onPause中调用stopBackgroundThread
    /**
     * Starts a background thread and its @link Handler.
     */
    private void startBackgroundThread()
        mBackgroundThread = new HandlerThread("CameraBackground");
        mBackgroundThread.start();
        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
   

    /**
     * Stops the background thread and its @link Handler.
     */
    private void stopBackgroundThread()
        mBackgroundThread.quitSafely();
        try
            mBackgroundThread.join();
            mBackgroundThread = null;
            mBackgroundHandler = null;
        catch (InterruptedException e)
            e.printStackTrace();
       
   

 


4、关联surface和相机mCameraDevice:创建预览;设置当前相机预览时的参数;发送请求,无限次获取图像

    /**
     * 为相机预览创建新的CameraCaptureSession
     */
    private void createCameraPreviewSession()


        try
            //CaptureRequest用的是builder模式,创建一个针对预览的CaptureRequest,设置其预览的surface
            mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mPreviewRequestBuilder.addTarget(mSurfaceHolder.getSurface());
            //创建一个CameraCaptureSession来进行相机预览。在安卓端和相机硬件端建立通道,进行信息的交换
            mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceHolder.getSurface()),
                    new CameraCaptureSession.StateCallback()

                        @Override
                        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession)
                            // 相机已经关闭
                            if (null == mCameraDevice)
                                return;
                           
                            // 会话准备好后,我们开始显示预览
                            mCaptureSession = cameraCaptureSession;
                            try
                                // 自动对焦应
                                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                                // 闪光灯
                                setAutoFlash(mPreviewRequestBuilder);
                                // 开启相机预览并添加事件
                                mPreviewRequest = mPreviewRequestBuilder.build();
                                //发送请求,无限次的重复获取图像
                                mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
                                Log.e(TAG," 开启相机预览并添加事件");
                            catch (CameraAccessException e)
                                e.printStackTrace();
                           
                       

                        @Override
                        public void onConfigureFailed(
                                @NonNull CameraCaptureSession cameraCaptureSession)
                            Log.e(TAG," onConfigureFailed 开启预览失败");
                       
                    , null);
        catch (CameraAccessException e)
            Log.e(TAG," CameraAccessException 开启预览失败");
            e.printStackTrace();
       
   


private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback()

        private void process(CaptureResult result)
            //拍摄完全完成并且成功,拍摄图像数据可用时回调
       

        @Override
        public void onCaptureProgressed(@NonNull CameraCaptureSession session,
                                        @NonNull CaptureRequest request,
                                        @NonNull CaptureResult partialResult)
      //拍摄进行中,但拍摄图像数据部分可用时回调
            process(partialResult);
       

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                       @NonNull CaptureRequest request,
                                       @NonNull TotalCaptureResult result)
        //拍摄失败时回调
            process(result);
       

    ;


如上,分三部:
1)创建CameraCaptureSession会话对象:全新的API引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送 Capture 请求,而摄像头会返回 CameraMetadata。
    createCaptureSession(List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler) :
    第一参数就是我们需要输出到的Surface列表,这里我们可以输出到一个SurfaceView中或者TextureView中。
    第二参数是对创建过程的一个回调方法,当onConfigured回调的时候说明CameraCaptureSession创建成功了。

2)创建CaptureRequest请求,并设置当前相机预览时的参数:
    createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); 这里我们发送的TEMPLATE_PREVIEW也就是告诉相机我们只需要预览。其他参数:
    TEMPLATE_RECORD 创建适合录像的请求。
    TEMPLATE_PREVIEW 创建一个适合于相机预览窗口的请求。
    TEMPLATE_STILL_CAPTURE 创建适用于静态图像捕获的请求
    TEMPLATE_VIDEO_SNAPSHOT 在录制视频时创建适合静态图像捕获的请求。


3)上面两个创建完毕后,调用mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);  发情求开启预览,并无限次的重复请求获取图像。mCaptureCallback作为回调不断输出数据

 

 

 

5、拍照
1)对焦
   /**
     * 将焦点锁定为静态图像捕获的第一步。(对焦)
     */
    private void lockFocus()
        try
            // 相机对焦
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                    CameraMetadata.CONTROL_AF_TRIGGER_START);
            // 修改状态
            mState = STATE_WAITING_LOCK;
        //CameraCaptureSession发送对焦请求
            mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
                    mBackgroundHandler);
        catch (CameraAccessException e)
            e.printStackTrace();
       
   


2)对对焦是否成功进行监听,在mCaptureCallback中对回调进行处理
    /**
     * 处理与JPEG捕获有关的事件
     */
    private CameraCaptureSession.CaptureCallback mCaptureCallback
            = new CameraCaptureSession.CaptureCallback()

        //处理
        private void process(CaptureResult result)
            switch (mState)
                case STATE_PREVIEW:
                    //预览状态
                    break;
               

                case STATE_WAITING_LOCK:
                    //等待对焦
                    Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
                    if (afState == null)
                        captureStillPicture();
                    else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
                            CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState)
                        // CONTROL_AE_STATE can be null on some devices
                        Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                        if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED)
                            mState = STATE_PICTURE_TAKEN;
                            //对焦完成
                            captureStillPicture();  
                        else
                            runPrecaptureSequence();
                       
                   
                    break;
               
                
                
               
           
       


3)向CameraCaptureSession发送请求拍照:

创建拍照请求mCaptureRequest对象;设置参数;发请求拍照:
    /**
     *
     * 拍摄静态图片。
     */
    private void captureStillPicture()
        try
            if ( null == mCameraDevice)
                return;
           
            // 这是用来拍摄照片的CaptureRequest.Builder。
            final CaptureRequest.Builder captureBuilder =
                    mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            captureBuilder.addTarget(mImageReader.getSurface());

            // 使用相同的AE和AF模式作为预览。
            captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            setAutoFlash(captureBuilder);
            // 方向
            int rotation = this.getWindowManager().getDefaultDisplay().getRotation();
            captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));

            CameraCaptureSession.CaptureCallback CaptureCallback
                    = new CameraCaptureSession.CaptureCallback()
                @Override
                public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                               @NonNull CaptureRequest request,
                                               @NonNull TotalCaptureResult result)
                    showToast("Saved: " + mFile);
                    Log.d(TAG, mFile.toString());
                    unlockFocus();
               
            ;
            //停止连续取景
            mCaptureSession.stopRepeating();
            //捕获图片
            mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);
        catch (CameraAccessException e)
            e.printStackTrace();
       
   


4)拍照后,拿到数据进行保存:
------------这里要说回在创建CameraCaptureSession时参数不是有一个输出的Surface列表么,在列表中添加一个ImageReader的Surface用户获取图片数据(ImageReader是一个可以让我们对绘制到surface的图像进行直接操作的类),
即:
mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback()...)

------------在ImageReader中对图片获取就行监听:
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);

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

        @Override
        public void onImageAvailable(ImageReader reader)
            //当图片可得到的时候获取图片并保存,reader.acquireNextImage()我们就可以拿到当前的图片数据了
            mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile));
        reader.acquireNextImage().close();
       

    ;

注意:一定要调用reader.acquireLatestImage()和close()方法,否则画面就会卡住

 

5)拍完后我们需要解锁焦点让相机回到预览状态

/**
     * 解锁焦点
     */
    private void unlockFocus()
        try
            // 重置自动对焦
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                    CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
            setAutoFlash(mPreviewRequestBuilder);
            mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
                    mBackgroundHandler);
            // 将相机恢复正常的预览状态。
            mState = STATE_PREVIEW;
            // 打开连续取景模式
            mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback,
                    mBackgroundHandler);
        catch (CameraAccessException e)
            e.printStackTrace();
       
   

 

 

6、预览图片实时输出
mPreviewRequestBuilder.addTarget(mSurfaceHolder.getSurface());
mPreviewRequestBuilder.addTarget(mImageReader.getSurface());//添加mImageReader.getSurface(),这样会在mOnImageAvailableListener监听中会实时收到预览数据
mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback()...)

 

7、注意点
Camera2 API配合SurfaceView暂时没有找到设置输出分辨率的接口。导致mOnImageAvailableListener监听中获取的Image对象,data数据的分辨率和image的宽高(mImageReader对象创建时设置)不一样。
Camera2 API根据google官方API配合TextureView进行预览

 

 

 

 

四、其他
1、通过设置ANDROID_CONTROL_MODE控制3A。可以选择没有3A(ANDROID_CONTROL_MODE_OFF),自动模式(ANDROID_CONTROL_MODE_AUTO)和场景模式(ANDROID_CONTROL_USE_SCENE_MODE)。
·        在OFF模式下,自动聚焦(AF),自动曝光(AE)和自动白平衡(AEB)模式都被关闭。3A事例不会重置捕获控制中的任何设置。
·        在AUTO模式下,AF,AE和AWB模式运行各自的独立算法,它们有自己的模式,状态和触发器元数据实体,如下段描述。
·        在USE_SCENE_MODE模式下,ANDROID_CONTROL_SCENE_MODE的值决定3A事例的行为。在除了FACE_PRIORITY的SCENE_MODE中,HAL层必须将ANDROID_CONTROL_AE/AWB/AF_MODE的值重置为更适合被选择的SCENE_MODE的模式。例如,HAL层喜欢在SCENE_MODE_NIGHT场景中使用AF的CONTINUOUS_FOCUS模式。当这些场景模式被忽略时,将使用用户对AE/AWB/AF_MODE的选择。
·        对SCENE_MODE_FACE_PRIORITY的场景,AE/AWB/AFMODE工作在ANDROID_CONTROL_MODE_AUTO模式下。但是3A算法需要侧重对场景中检测出来的脸进行测光和聚焦。

2、自动曝光模式下:
requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
requestBuilder.set(CaptureRequest.CONTROL_MODE,
                CaptureRequest.CONTROL_MODE_AUTO);

mCaptureCallback中回调的的数据对象CaptureResult:
通过CaptureResult.get(CaptureResult.SENSOR_SENSITIVITY) 可以实时获取每一帧的实时感光度。

 

 

    

 

 

 

 

 

以上是关于Android Camera2 开发文档说明的主要内容,如果未能解决你的问题,请参考以下文章

Android多媒体功能开发(14)——Camera2框架

Android Camera2 教程 · 第一章 · 概览

Android Camera2 拍照入门学习

Android Camera2 开发详解

android.hardware.camera2.full 来自哪里?

如何实现Android平台GB28181设备对接Camera2数据