Camera2学习笔记

2024-01-07 16:59
文章标签 学习 笔记 camera2

本文主要是介绍Camera2学习笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Camera2学习笔记

Camera API2 使用流程图

Camera2主要API
CameraManager 相机的管理类通过Context.getSystemService获取
1. getCameraCharacteristrics  查询Camera有哪些功能,这个对象是不可改变的
2. getCameraExtensionCharacteristics  查询某个Camera的Extension能力
3. getConcurrentCameraIds  获取可以同时配置会话(Session)的Camera的ID
4. getCameraIdList  获取当前可用的Camera的ID
5. openCamera  打开指定ID的Camera
6. registerAvailabilityCallback  注册一个监听回调来监听Camera的状态,是否可用
7. unregisterAvailabilityCallback  注销监听回调
CameraCharacteristics 记录了相机的详细信息,比如支持的图片格式,支持聚焦功能等
1. get(Key<T> key) 根据Key获取Value值
2. getKeys() 获取所有CameraCharacteristics支持的Key集合
3. getAvailableCaptureRequestKes() 获取所有CaptureRequest支持的Key集合
4. getAvailableCaptureResultKeys() 获取所有CaptureResult支持的Key集合
5. getPhysicalCameraIds() 获取当前逻辑Camera对应的物理Camera集合(我们手机后面实实在在的摄像头就是物理Camera,我们把任意几个物理Camera组合叫做逻辑Camera)
CameraDevice 是已打开的Camera设备
1. close 以最快的速度关闭Camera
2. getId 获取当前Camera对应的ID
3. createCaptureRequest(int templateType) 根据templateType创建CaptureRequest.Builder
4. createCaptureSession(SessionConfiguration config) 根据config创建CameraCaptureSession
5. setCameraAudioRestriction(int mode) 设置音频限制模式,比如屏蔽notifications的震动和声音
6. getCameraAudioRestriction 获取当前使用的音频限制模式
CameraDevice.StateCallback 一个接收相机更新状态的回调
这些状态更新包括有关设备完成启动,设备断开、关闭、设备错误的通知
1. ERROR_CAMERA_DEVICE	表示摄像头设备遇到了致命错误
2. ERROR_CAMERA_DISABLED 表示由于设备策略而无法打开摄像头设备
3.	ERROR_CAMERA_IN_USE	表示摄像头设备已经在使用中
4. ERROR_CAMERA_SERVICE	表示摄像头服务遇到了致命错误
5. ERROR_MAX_CAMERAS_IN_USE 因为打开了太多摄像头设备导致无法打开该设备
CameraCaptureSession 这是一个相机会话,与Camera设备建立通道,后面对Camera设备的控制都是通过这个通道
通过 CameraDevice 类的 createCaptureSession() 方法创建,并在回调的 onConfigured(CameraCaptureSession session) 方法中获取实例1. capture 发送一个请求到Camera底层,参数Handler指定回调线程
2. captureSingleRequest 发送一个请求到C底层,参数Executor指定回调线程
3. captureBurst 发送一组请求到Camera底层,参数Handler指定回调线程
4. captureBurstRequests 发送一组请求到Camera底层,参数Executor指定回调线程
5. setRepeatingRequest 发送一组重复请求到Camera底层,参数Handler指定回调线程
6. setSingleRepeatingRequest 发送一组重复请求到Camera底层,参数Executor指定回调线程
7. setRepeatingBurst 发送一组重复请求到Camera底层,参数Handler指定回调线程
8. setRepeatingBurstRequest 发送一组重复请求到Camera底层,参数Executo指定回调线程
9. stopRepeating 通知相机服务不要向HAL发送请求了
10. abortCaptures 通知相机服务不要向HAL发送请求了,并且让HAL放弃还没有处理完的请
11. supportsOfflineProcessing(Surface surface) 判断surface是否支持离线处理模式
12. switchToOffline 将会话切换到离线处理模式
13. isReprocessable 判断是否支持重新处理
CameraCaptureSession.StateCallback 当相机捕获图像的状态发生变化时,会回调这个方法
其中只有 onConfigured 和 onConfigureFailed 两个方法是抽象的(必须重写),其余均有空实现1. onConfigureFailed(CameraCaptureSession session)	会话无法按照相应的配置发起请求时回调
2. onConfigured(CameraCaptureSession session)	相机设备完成配置并开始处理捕捉请求时回调
3. onActive(CameraCaptureSession session)	在 onConfigured 后执行,即开始真的处理捕捉请求了
4. onCaptureQueueEmpty(CameraCaptureSession session)	当相机设备的捕捉队列为空时回调
5. onClosed(CameraCaptureSession session)	当事务关闭时回调
6. onReady(CameraCaptureSession session)	每当没有捕捉请求处理时都会回调该方法,该方法会回调多次
CameraCaptureSession.CaptureCallback 当一个捕捉请求发送给相机设备时,这个方法会监听图像捕捉的进度
所有方法均有默认的空实现,根据需求重写相应的方法1. onCaptureStarted	当相机设备开始为请求捕捉并输出图像时回调
2. onCaptureProgressed	当图像捕获部分进行时就会回调该方法,此时一些(但不是全部)结果是可用的
3. onCaptureCompleted	当图像捕获完成时,并且结果可用时回调
4. onCaptureFailed	对应 onCaptureCompleted 方法,当相机设备产生 TotalCaptureResult 失败时就回调
5. onCaptureBufferLost	当捕获的数据没有被送到目标 surface 时被回调
CaptureRequest 这是一个图像请求
1. getKeys 获取当前CaptureRequest包含的Key集合
2. getTag 获取CaptureRequest的Tag,每个CaptureRequest都有一个Tag,代表一个标识
3. get 获取Key对应的值
4. isReprocess 判断是否是一个再处理的请求(CaptureRequest)
5. CaptureResult 这是一个请求最终生成的图象信息
6. getKeys 获取当前CaptureReult所包含的Key集合
7. get 获取当前Key对应的值
8. getRequest 获取当前CaptureReeult对应的CaptureRequest
9. getCameraId  获取Camera的ID
CaptureResult 这是捕获单个图像的结果集
捕获结果是由摄像头在对捕获请求进行处理完成后产生,CaptureResult 对象是不可变的,常使用的子类是 TotalCaptureResult
我们在 CameraCaptureSession.CaptureCallback 类的回调方法 onCaptureProgressed() 和 onCaptureCompleted() 中都可以拿到 CaptureResult 的对象1. get(Key key) 获取 CaptureResult 中指定 key 的值
2. getFrameNumber() 获取该结果申请的帧的数量。
3. getKeys() 返回所有 Key 的列表。
4. getRequest() 返回这个结果对应的 CaptureRequest 对象。
Camera2基本流程

  1. 创建TextureView布局
  2. 在activity中获取TextureView对象并设置监听
  3. 在onSurfaceTextureAvailable中开始进行摄像头操作
  4. 创建子线程,用于监听和处理数据
  5. 通过context.getSystemService(Context.CAMERA_SERVICE)获取CameraManager对象
  6. 通过manager.getCameraCharacteristics(“0”)获取后置摄像头配置信息类,一般前置为 “1” 后置为 “0”
  7. 调用CameraManager.openCamera()方法在回调中获取CameraDevice对象
  8. 通过CameraDevice.createCaptureSession()方法在回调中获取CameraCaptureSession对象
  9. 构建CaptureRequest请求, 其中参数有三种模式分别是
  • 预览 TEMPLATE_PREVIEW
  • 拍照 TEMPLATE_STILL_CAPTURE
  • 录像 TEMPLATE_RECORD
  1. 接着是通过CameraCaptureSession发送CaptureRequest, capture是只发一次请求, setRepeatingRequest则是不断发送请求.
  2. 摄像头捕获的数据即拍照数据可以在ImageReader.OnImageAvailableListener回调中获取,保存图片就是在这里开一个子线程进行保存, CaptureCallback中可以获取拍照实际的参数和当前Camera的状态.
Camera2实践
CameraDemo的总流程图

拍照流程

摄像头切换流程

实现思路:这里只是简单的前后摄像头的切换,首先通过 cameraManager.getCameraIdList() 获取当前设备摄像头ID列表,然后遍历该列表,通过当前Camera的ID来判断当前摄像头是前置还是后置,如果是前置则查询到后置摄像头ID并将其赋值为当前Camera的ID,反之亦然,拿到要切换摄像头ID后就是去调用 manager.openCamera 方法打开指定ID的摄像头,再调用此方法之前要把当前摄像头关闭即调用closeCamera,再调用 reopenCamera,主要实现代码如下

//切换摄像头
public void switchCamera() {//获取摄像头的管理者CameraManager cameraManager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);try {for (String id : cameraManager.getCameraIdList()) {CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id);if (mCameraId.equals(String.valueOf(CameraCharacteristics.LENS_FACING_BACK)) && characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) {mCameraId = String.valueOf(CameraCharacteristics.LENS_FACING_FRONT);closeCamera();reopenCamera();break;} else if (mCameraId.equals(String.valueOf(CameraCharacteristics.LENS_FACING_FRONT)) && characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK) {mCameraId = String.valueOf(CameraCharacteristics.LENS_FACING_BACK);closeCamera();reopenCamera();break;}}} catch (Exception e) {e.printStackTrace();}
}//关闭摄像头,释放相关资源,这里采用了锁防止出现线程并发的问题
private void closeCamera() {try {mCameraOpenCloseLock.acquire();synchronized (mCameraStateLock) {mPendingUserCaptures = 0;mState = STATE_CLOSED;if (null != mCaptureSession) {mCaptureSession.close();mCaptureSession = null;}if (null != mCameraDevice) {mCameraDevice.close();mCameraDevice = null;}if (null != mJpegImageReader) {mJpegImageReader.close();mJpegImageReader = null;}}} catch (InterruptedException e) {e.printStackTrace();} finally {mCameraOpenCloseLock.release();}
}//重新打开摄像头
public void reopenCamera() {if (mTextureView.isAvailable()) {openCamera();} else {mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);}
}
拍照比例流程

实现思路:我的目标是实现 4:3、16:9、全屏这三种拍照比例,所以我通过 Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)) 来获取当前摄像头支持的Size列表,当然这只是一种方式,我是拍照所以选择JPEG格式,然后遍历该列表找出最接近4:3、16:9、全屏这三种拍照比例的Size并把它们存到sizeList中,其中全屏我是获取手机屏幕宽高比来进行相应的匹配的,最后就是当我按切换拍照比例的按钮时进行循环切换拍照比例,我其中configureTransform是当我们选择相应的拍照比例时,TextureView宽高比进行自适应,当然这样只是预览比例变化了,拍出来的图片还是之前的比例,所以我们还需要进行 closeCamera 和 reopenCamera,主要实现代码如下

//初始化拍照比例
private void initPictureSize() {CameraManager manager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);try {CameraCharacteristics characteristics= manager.getCameraCharacteristics(mCameraId);StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);List<Size> outputSize = Arrays.asList(map.getOutputSizes(ImageFormat.JPEG));//寻找一个最合适的尺寸与TextrueView适配//定义List用来存全屏、16:9、4:3的尺寸sizeList = new ArrayList<>();//全屏int mWidth = outputSize.get(0).getWidth();int mHeight = outputSize.get(0).getHeight();//16:9int mWidtha = outputSize.get(0).getWidth();int mHeighta = outputSize.get(0).getHeight();//4:3int mWidthb = outputSize.get(0).getWidth();int mHeightb = outputSize.get(0).getHeight();//TODO 如何计算各个拍照比例,需要把怎么确定index 0是4:3比例,2是16:9, 3是全屏的计算过程代码加入进来//获取手机屏幕宽高//方法三:应用程序显示区域指定可能包含应用程序窗口的显示部分,不包括系统装饰DisplayMetrics displayMetrics = getResources().getDisplayMetrics();int screenHeight = displayMetrics.widthPixels;int screenWidth = displayMetrics.heightPixels;Log.i(TAG, "updateCameraPreview: ---" + screenWidth);Log.i(TAG, "updateCameraPreview: ---" + screenHeight);//寻找与手机屏幕宽高最接近的预览尺寸double screenRatioa = (double) screenWidth / (double) screenHeight;//寻找最接近手机屏幕尺寸的预览尺寸double screenRatiob = Math.abs(screenRatioa - (double) outputSize.get(0).getWidth() / (double) outputSize.get(0).getHeight());//寻找最接近16:9的预览尺寸double ratioa = Math.abs(16.0 / 9.0 - (double) outputSize.get(0).getWidth() / (double) outputSize.get(0).getHeight());//寻找最接近4:3的预览尺寸double ratiob = Math.abs(4.0 / 3.0 - (double) outputSize.get(0).getWidth() / (double) outputSize.get(0).getHeight());//遍历尺寸for (int i = 1; i < outputSize.size() - 1; i++) {int w = outputSize.get(i).getWidth();int h = outputSize.get(i).getHeight();Log.i(TAG, "updateCameraPreview: w:  " + w + "   h:" + h);//全屏double ratios = Math.abs(screenRatioa - (double) w / (double) h);if (ratios < screenRatiob) {screenRatiob = ratios;mWidth = w;mHeight = h;}//16:9double ratio = Math.abs(16.0 / 9.0 - (double) w / (double) h);if (ratio < ratioa) {ratioa = ratio;mWidtha = w;mHeighta = h;}//4:3double ratiox = Math.abs(4.0 / 3.0 - (double) w / (double) h);if (ratiox < ratiob) {ratiob = ratiox;mWidthb = w;mHeightb = h;}}Log.i(TAG, "updateCameraPreview: 4:3  " + mWidthb + "  " + mHeightb);Log.i(TAG, "updateCameraPreview: 16:9  " + mWidtha + "  " + mHeighta);Log.i(TAG, "updateCameraPreview: 全屏  " + mWidth + "  " + mHeight);sizeList.add(new Size(mWidthb, mHeightb));//4:3sizeList.add(new Size(mWidtha, mHeighta));//16:9sizeList.add(new Size(mWidth, mHeight));//全屏mCurrentPictureSize.setText("4:3"); //默认拍照比例largestJpeg = sizeList.get(0);} catch (CameraAccessException e) {e.printStackTrace();}
}//切换拍照比例
private void switchPictureSize() {isSwitchPictureSize = false;if (sizeIndex < sizeList.size() - 1) {sizeIndex++;} else {sizeIndex = 0;}switch (sizeIndex) {case 0:largestJpeg = sizeList.get(0);mCurrentPictureSize.setText("4:3");break;case 1:largestJpeg = sizeList.get(1);mCurrentPictureSize.setText("16:9");break;case 2:largestJpeg = sizeList.get(2);mCurrentPictureSize.setText("全屏");break;}configureTransform(mTextureView.getWidth(), mTextureView.getHeight());closeCamera();reopenCamera();
}
闪光模式切换流程

实现思路:

闪光模式一般有四个状态分别是:关闭、拍照打闪、长亮、自动,这里我只实现了关闭、拍照打闪、自动这三种状态,实现如下

关闭:

FLASH_MODE:FLASH_MODE_OFF
CONTROL_AE_MODE:CONTROL_AE_MODE_ON/CONTROL_AE_MODE_OFF

拍照打闪:

FLASH_MODE:FLASH_MODE_SINGLE (不管AE的结果,拍照时Flash都会打闪,这种模式下拍照前一定要通过 android.control.aePrecaptureTrigger进行预测光,不然拍下来的图可能会过曝或者全黑)
CONTROL_AE_MODE:CONTROL_AE_MODE_ON/CONTROL_AE_MODE_OFF

或者

FLASH_MODE:任意
CONTROL_AE_MODE:CONTROL_AE_MODE_ON_ALWAYS_FLASH

长亮:

FLASH_MODE:FLASH_MODE_TORCH
CONTROL_AE_MODE:CONTROL_AE_MODE_ON/CONTROL_AE_MODE_OFF

自动:

FLASH_MODE:任意
CONTROL_AE_MODE:CONTROL_AE_MODE_ON_AUTO_FLASH/CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE

通过我的CameraDemo流程图可以看出,闪光模式切换流程是
switchFlashMode -> CameraCaptureSession.CaptureCallback(mPreCatureCallback) -> process(打闪处理) -> captureStillPictureLcked(对图像再次处理) -> setUp3AControlsLocked -> CameraCaptureSession.CaptureCallback(mCatureCallback) ->handleCompletionLocked(开启异步线程池进行保存图像)

拍照之前要调用 setFlashMode,其中setUp3AControlsLocked方法是进行当前闪光模式判断然后进行相应调整

//切换闪光模式
private void switchFlashMode() {switch (mFlashMode) {case 0:mFlashMode = 1;mFlashBtn.setImageResource(R.drawable.ic_flash_auto);mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);try {mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),mPreCaptureCallback, mBackgroundHandler);} catch (CameraAccessException e) {e.printStackTrace();return;}break;case 1:mFlashMode = 2;mFlashBtn.setImageResource(R.drawable.ic_flash_open);mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);try {mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),mPreCaptureCallback, mBackgroundHandler);} catch (CameraAccessException e) {e.printStackTrace();return;}break;case 2:mFlashMode = 0;mFlashBtn.setImageResource(R.drawable.ic_flash_close);mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);try {mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),mPreCaptureCallback, mBackgroundHandler);} catch (CameraAccessException e) {e.printStackTrace();return;}break;}
}//设置闪光模式参数
private void setFlashMode() {switch (mFlashMode) {case 0:mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);break;case 1:mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);break;case 2:mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);break;}
}

这篇关于Camera2学习笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/580627

相关文章

51单片机学习记录———定时器

文章目录 前言一、定时器介绍二、STC89C52定时器资源三、定时器框图四、定时器模式五、定时器相关寄存器六、定时器练习 前言 一个学习嵌入式的小白~ 有问题评论区或私信指出~ 提示:以下是本篇文章正文内容,下面案例可供参考 一、定时器介绍 定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。 定时器作用: 1.用于计数系统,可

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

[word] word设置上标快捷键 #学习方法#其他#媒体

word设置上标快捷键 办公中,少不了使用word,这个是大家必备的软件,今天给大家分享word设置上标快捷键,希望在办公中能帮到您! 1、添加上标 在录入一些公式,或者是化学产品时,需要添加上标内容,按下快捷键Ctrl+shift++就能将需要的内容设置为上标符号。 word设置上标快捷键的方法就是以上内容了,需要的小伙伴都可以试一试呢!

Tolua使用笔记(上)

目录   1.准备工作 2.运行例子 01.HelloWorld:在C#中,创建和销毁Lua虚拟机 和 简单调用。 02.ScriptsFromFile:在C#中,对一个lua文件的执行调用 03.CallLuaFunction:在C#中,对lua函数的操作 04.AccessingLuaVariables:在C#中,对lua变量的操作 05.LuaCoroutine:在Lua中,

AssetBundle学习笔记

AssetBundle是unity自定义的资源格式,通过调用引擎的资源打包接口对资源进行打包成.assetbundle格式的资源包。本文介绍了AssetBundle的生成,使用,加载,卸载以及Unity资源更新的一个基本步骤。 目录 1.定义: 2.AssetBundle的生成: 1)设置AssetBundle包的属性——通过编辑器界面 补充:分组策略 2)调用引擎接口API

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J

大学湖北中医药大学法医学试题及答案,分享几个实用搜题和学习工具 #微信#学习方法#职场发展

今天分享拥有拍照搜题、文字搜题、语音搜题、多重搜题等搜题模式,可以快速查找问题解析,加深对题目答案的理解。 1.快练题 这是一个网站 找题的网站海量题库,在线搜题,快速刷题~为您提供百万优质题库,直接搜索题库名称,支持多种刷题模式:顺序练习、语音听题、本地搜题、顺序阅读、模拟考试、组卷考试、赶快下载吧! 2.彩虹搜题 这是个老公众号了 支持手写输入,截图搜题,详细步骤,解题必备

《offer来了》第二章学习笔记

1.集合 Java四种集合:List、Queue、Set和Map 1.1.List:可重复 有序的Collection ArrayList: 基于数组实现,增删慢,查询快,线程不安全 Vector: 基于数组实现,增删慢,查询快,线程安全 LinkedList: 基于双向链实现,增删快,查询慢,线程不安全 1.2.Queue:队列 ArrayBlockingQueue:

硬件基础知识——自学习梳理

计算机存储分为闪存和永久性存储。 硬盘(永久存储)主要分为机械磁盘和固态硬盘。 机械磁盘主要靠磁颗粒的正负极方向来存储0或1,且机械磁盘没有使用寿命。 固态硬盘就有使用寿命了,大概支持30w次的读写操作。 闪存使用的是电容进行存储,断电数据就没了。 器件之间传输bit数据在总线上是一个一个传输的,因为通过电压传输(电流不稳定),但是电压属于电势能,所以可以叠加互相干扰,这也就是硬盘,U盘

人工智能机器学习算法总结神经网络算法(前向及反向传播)

1.定义,意义和优缺点 定义: 神经网络算法是一种模仿人类大脑神经元之间连接方式的机器学习算法。通过多层神经元的组合和激活函数的非线性转换,神经网络能够学习数据的特征和模式,实现对复杂数据的建模和预测。(我们可以借助人类的神经元模型来更好的帮助我们理解该算法的本质,不过这里需要说明的是,虽然名字是神经网络,并且结构等等也是借鉴了神经网络,但其原型以及算法本质上还和生物层面的神经网络运行原理存在