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

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件