简介
CameraX 应用可以通过 CameraX Extensions API 使用扩展。CameraX Extensions API 可用于管理可用扩展的查询、配置扩展相机会话以及与相机扩展 OEM 库的通信。这样,您的应用就可以使用夜间、HDR、自动、焦外成像或脸部照片修复等功能。
相机任务
CameraX 中默认有以下四个 UseCase,您可以将其用于各种相机任务:Preview、ImageCapture、VideoCapture 和 ImageAnalysis
Preview
在向应用添加预览时,请使用 PreviewView,这是一种可以剪裁、缩放和旋转以确保正确显示的 View。
当相机处于活动状态时,图片预览会流式传输到 PreviewView 中的 Surface。
1、实现预览
previewView = findViewById(R.id.pv_camera_x);
cameraProviderFuture = ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(new Runnable() {
@Override
public void run() {
try {
//将相机的生命周期和activity的生命周期绑定,camerax 会自己释放
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
Preview preview = new Preview.Builder().build();
mImageCapture = new ImageCapture.Builder()
.setFlashMode(ImageCapture.FLASH_MODE_OFF)
.build();
//选择后置摄像头
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build();
camera = cameraProvider.bindToLifecycle(
((LifecycleOwner) CameraXActivity.this),
cameraSelector,
preview,
mImageCapture);
preview.setSurfaceProvider(
previewView.getSurfaceProvider());
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}
},ContextCompat.getMainExecutor(this));
2、用于渲染预览流的实现模式。
PERFORMANCE 是默认模式。PreviewView 会使用 SurfaceView 显示视频串流,但在某些情况下会回退为使用 TextureView。SurfaceView 具有专用的绘图界面,该对象更有可能通过内部硬件合成器实现硬件叠加层,尤其是当预览视频上面没有其他界面元素(如按钮)时。通过使用硬件叠加层进行渲染,视频帧会避开 GPU 路径,从而能降低平台功耗并缩短延迟时间。
COMPATIBLE 模式。在此模式下,PreviewView 会使用 TextureView;不同于 SurfaceView,该对象没有专用的绘图表面。因此,视频要通过混合渲染,才能显示。在这个额外的步骤中,应用可以执行额外的处理工作,例如不受限制地缩放和旋转视频。
3、缩放类型
当预览视频分辨率与目标 PreviewView 的尺寸不同时,视频内容需要通过剪裁操作或添加遮幅式黑边来适应视图(保持原始宽高比)。为此,PreviewView 提供了以下 ScaleTypes:
- FIT_CENTER、FIT_START 和 FIT_END,用于添加遮幅式黑边。整个视频内容会调整(放大或缩小)为可在目标 PreviewView 中显示的最大尺寸。不过,虽然整个视频帧会完整显示,但屏幕画面中可能会出现空白部分。视频帧会与目标视图的中心、起始或结束位置对齐,具体取决于您在上述三种缩放类型中选择了哪一种。
- FILL_CENTER、FILL_START 和 FILL_END,用于进行剪裁。如果视频的宽高比与 PreviewView 不匹配,画面中只会显示部分内容,但视频仍会填满整个 PreviewView。
CameraX 使用的默认缩放类型是 FILL_CENTER。您可以使用 PreviewView.setScaleType() 设置最适合具体应用的缩放类型。下面的代码示例设置了 FIT_CENTER 缩放类型:
//用于设置缩放类型
previewView.setScaleType(PreviewView.ScaleType.FILL_CENTER);
ImageCapture
运行 ImageCapture 的可自定义执行程序有两种类型:回调执行程序和 IO 执行程序。
- 回调执行程序是 takePicture 方法的参数。它用于执行用户提供的 OnImageCapturedCallback()。
- 如果调用方选择将图片保存到文件位置,您可以指定执行程序以执行 IO。如需设置 IO 执行程序,请调用 ImageCapture.Builder.setIoExecutor(Executor)。如果执行程序不存在,则默认 CameraX 为任务的内部 IO 执行程序。
设置闪光灯模式
默认闪光灯模式为 FLASH_MODE_OFF。
如需设置闪光灯模式,请使用 ImageCapture.Builder.setFlashMode()
- FLASH_MODE_OFF :没有闪光灯。拍照时永远不会使用闪光灯。
- FLASH_MODE_ON :闪光灯始终处于开启状态。
- FLASH_MODE_AUTO :在弱光环境下拍摄时,自动开启闪光灯。
//设置闪光灯模式
mImageCapture.setFlashMode(ImageCapture.FLASH_MODE_AUTO);
设置拍摄模式
ImageCapture.Builder.setCaptureMode() 可用于配置拍摄照片时所采用的拍摄模式:
拍摄模式默认为 CAPTURE_MODE_MINIMIZE_LATENCY
- CAPTURE_MODE_MINIMIZE_LATENCY :缩短图片拍摄的延迟时间。
- CAPTURE_MODE_MAXIMIZE_QUALITY :提高图片拍摄的图片质量。
- CAPTURE_MODE_ZERO_SHOT_LAG : 与CAPTURE_MODE_MINIMIZE_LATENCY延迟时间会更加明显缩短,零快门延迟。
mImageCapture = new ImageCapture.Builder()
//设置闪光灯模式
.setFlashMode(ImageCapture.FLASH_MODE_OFF)
//设置拍摄模式
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build();
拍照
private void takePic(){
mImageCapture.takePicture(ContextCompat.getMainExecutor(this), new ImageCapture.OnImageCapturedCallback() {
@Override
public void onCaptureSuccess(@NonNull ImageProxy image) {
super.onCaptureSuccess(image);
Log.w(TAG , "onCaptureSuccess");
//转bitmap
Bitmap bitmap = image.toBitmap();
//使用完image关闭
image.close();
}
@Override
public void onError(@NonNull ImageCaptureException exception) {
super.onError(exception);
}
});
}
ImageAnalysis
图像分析用例为您的应用提供可供 CPU 访问的图像,您可以对这些图像执行图像处理、计算机视觉或机器学习推断。应用会实现对每个帧运行的 analyze() 方法。
操作模式
当应用的分析流水线无法满足 CameraX 的帧速率要求时,您可以将 CameraX 配置为通过以下其中一种方式丢帧:
- 非阻塞(默认):在该模式下,执行器始终会将最新的图像缓存到图像缓冲区(与深度为 1 的队列相似),与此同时,应用会分析上一个图像。如果 CameraX 在应用完成处理之前收到新图像,则新图像会保存到同一缓冲区,并覆盖上一个图像。 请注意,在这种情况下,ImageAnalysis.Builder.setImageQueueDepth() 不起任何作用,缓冲区内容始终会被覆盖。您可以通过使用 STRATEGY_KEEP_ONLY_LATEST 调用 setBackpressureStrategy() 来启用该非阻塞模式。如需详细了解执行器的相关影响,请参阅 STRATEGY_KEEP_ONLY_LATEST 的参考文档。
- 阻塞:在该模式下,内部执行器可以向内部图像队列添加多个图像,并仅在队列已满时才开始丢帧。系统会在整个相机设备上进行屏蔽:如果相机设备具有多个绑定用例,那么在 CameraX 处理这些图像时,系统会屏蔽所有这些用例。例如,如果预览和图像分析都已绑定到某个相机设备,那么在 CameraX 处理图像时,系统也会屏蔽相应预览。您可以通过将 STRATEGY_BLOCK_PRODUCER 传递到 setBackpressureStrategy() 来启用阻塞模式。此外,您还可以通过使用 ImageAnalysis.Builder.setImageQueueDepth() 来配置图像队列深度。
如果分析器延迟低且性能高,在这种情况下用于分析图像的总时间低于 CameraX 帧的时长(例如,60fps 用时 16 毫秒),那么上述两种操作模式均可提供顺畅的总体体验。在某些情况下,阻塞模式仍非常有用,例如在处理非常短暂的系统抖动时。
如果分析器延迟高且性能高,则需要结合使用阻塞模式和较长的队列来抵补延迟。但请注意,在这种情况下,应用仍可以处理所有帧。
如果分析器延迟高且耗时长(分析器无法处理所有帧),非阻塞模式可能更为适用,因为在这种情况下,系统必须针对分析路径进行丢帧,但要让其他同时绑定的用例仍能看到所有帧。
图像输出参数
- 格式 : CameraX 可通过 setOutputImageFormat(int) 支持 YUV_420_888 和 RGBA_8888。默认格式为 YUV_420_888。
- Resolution 和 AspectRatio :您可以设置其中一个参数,但请注意,您不能同时设置这两个值。
- SetTargetRotation : 设置选择
- setTargetName : 设置正在配置的目标对象的名称,仅用于调试日志记录。
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
//设置图像输出格式
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888)
//设置此配置中图像的预期目标的纵横比。
.setTargetAspectRatio(AspectRatio.RATIO_4_3)
//设置此配置中预期目标的分辨率。请注意Resolution和Ratio只能设置一个
// .setTargetResolution(new Size(1200,900))
//设置正在配置的目标对象的名称,仅用于调试日志记录。
.setTargetName("LiangXy")
//设置将用于后台任务的默认执行器。
// .setBackgroundExecutor()
//设置应用于图像生成器的背压策略,以处理图像生成速度可能快于分析速度的情况。
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build();
VideoCapture
CameraX 视频捕获架构
捕获系统通常会录制视频流和音频流,对其进行压缩,对这两个流进行多路复用,然后将生成的流写入磁盘。
在 CameraX 中,用于视频捕获的解决方案是 VideoCapture 用例:
使用 VideoCapture API
如需将 CameraX VideoCapture 用例集成到您的应用中,请执行以下操作:
- 绑定 VideoCapture。
- 准备和配置录制。
- 开始和控制运行时录制
private void initVideoCapture(){
try {
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
List<CameraInfo> cameraInfo = cameraProvider.getAvailableCameraInfos();
List<Quality> supportedQualities = QualitySelector.getSupportedQualities(cameraInfo.get(0));
QualitySelector qualitySelector = QualitySelector.from(supportedQualities.get(0));
Recorder recorder = new Recorder.Builder()
.setQualitySelector(qualitySelector)
.setExecutor(ContextCompat.getMainExecutor(this))
.build();
VideoCapture videoCapture = VideoCapture.withOutput(recorder);
cameraProvider.bindToLifecycle((LifecycleOwner) this, CameraSelector.DEFAULT_BACK_CAMERA, videoCapture);
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}