原文地址:magdamiu.com/2020/09/16/…
作者:MAGDA MIU
图片拍摄时的图像分析和应用扩展是用户经常访问的用例。通过使用CameraX API,我们能够以非常简单的方式实现它们。如果您想了解更多,请查看当前文章中的代码示例和这些特性背后的理论。
在我之前关于CameraX的文章中,我涵盖了以下主题:
-
我们使用最古老的相机API所面临的挑战
-
使用这个新API的优点是什么
-
实现预览用例的步骤
-
实现捕获用例的步骤
如果你想查看以前的帖子,这里有详细信息:
分析用例
用于图像处理的CPU可访问图像,计算机视觉,ML
对于每个用例,首先我们将检查实现步骤,然后检查每个步骤的分配代码。
步骤1:创建Image Analysis引用
val imageAnalysis = ImageAnalysis.Builder().build()
// the executor receives sequentially the frames
val blocking = ImageAnalysis.STRATEGY_BLOCK_PRODUCER
// the executor receives last available frame (default)
val nonBlocking = ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
val imageAnalysis = ImageAnalysis.Builder()
.setBackpressureStrategy(nonBlocking)
.build()
-
Image Analysis是UseCase抽象类提供的实现之一
-
这是创建Image Analysis类实例的最简单方法
-
Image Analysis通过Image Reader从相机获取图像
-
这些图像可以用两种不同的方式处理:
- 按顺序地
-
或者只使用最后可用的帧
-
所以实际上我们有一个反压力策略是通过使用set Backpress Policy方法添加的
-
如果没有设置,默认策略是当执行程序接收到最后一个可用帧时
步骤2和3:定义一个自定义分析器并实现Analysis()方法
class PurpleColorAnalyser : ImageAnalysis.Analyzer {
private var lastAnalyzedTimestamp = 0L
private fun ByteBuffer.toByteArray(): ByteArray {
rewind()
val data = ByteArray(remaining())
get(data)
return data
}
override fun analyze(image: ImageProxy) {
val currentTimestamp = System.currentTimeMillis()
val oneSecond = TimeUnit.SECONDS.toMillis(1)
if (currentTimestamp – lastAnalyzedTimestamp >= oneSecond) {
val buffer = image.planes[0].buffer
val data = buffer.toByteArray()
val pixels = data.map { it.toInt() and 0x9370DB }
val averagePurplePixels = pixels.average()
Log.e("PURPLE", "Average purple pixels: $averagePurplePixels")
lastAnalyzedTimestamp = currentTimestamp
}
image.close()
}
}
-
在从分析()返回之前,通过调用image.close()关闭图像引用,以避免阻塞进一步图像的生成(导致预览停滞),并避免可能丢弃图像。
-
分析图像以产生结果。
-
该方法对来自摄像机的每个图像调用一次,并以摄像机的帧速率调用。每个分析调用被顺序执行。
步骤4:设置自定义分析器
imageAnalysis.setAnalyzer(executor, PurpleColorAnalyser())
步骤5:更新调用以绑定生命周期
// bind the image analysis use case to camera
camera = cameraProvider.bindToLifecycle(
this as LifecycleOwner,
cameraSelector,
imageAnalysis,
preview
)
图像格式
我们都知道RGB色系。它从红色、绿色和蓝色的组合中构建所有的颜色。
CameraX产生YUV_420_888格式的图像。YUV颜色编码为像素分配2个维度:
-
**亮度=**指像素的亮度=Y(灰度图像)
-
色度=指颜色=UV
**YUV=>Y=亮度U=**蓝色的色度V=红色的色度。YUV=YCbCr
该格式是一种通用的YCbCr格式,能够描述任何4:2:0色度子采样平面或半平面缓冲区(但不是完全交错),每个颜色样本有8位。
如果我们使用Camera 2 API,在Image Format中捕获图像。YUV_420_888格式。如果我们使用旧的相机API,在Image Format中捕获图像。NV21格式。
YUV是彩色图像管道中主要使用的编码系统之一,这意味着它用于图像源(摄像机)和图像渲染器(设备的显示器)之间。这是图像处理应用程序中涉及显示器的最有效的方法之一。与RGB方案相比,传输误差减小。给定颜色的光度被分离,色调(颜色)被确定。因此,通过使用YUV,我们能够在不影响质量的情况下应用图像处理算法。
研究表明,人眼能够识别颜色的亮度,但不太擅长看到同一颜色的不同细微差别,所以这就是色度子采样的出现方式。色度子采样实际上是一种用于图像和视频的压缩技术。因此,关于图像亮度的信息比来自图像的颜色更重要。
色度子采样有3种常见方案:
-
4:4:4
-
4:2:2
-
4:2:0
其主要思想是降低颜色分量的分辨率,因此它在不影响人眼感知的情况下减小了图像的大小。
现在要解决的正常问题是我们保存了多少数据?!YUV_420_888我们节省50%。所以我认为这是一笔好交易。
相机控制
Camera Control提供各种异步操作,如缩放、对焦和测光,这些操作会影响当前绑定到该相机的所有用例的输出。
此外,相机信息在这个等式中起着重要作用。Camera Info是一个用于检索相机信息的接口。
val cameraControl = camera.cameraControl
val cameraInfo = camera.cameraInfo
cameraInfo.torchState.observe(this, Observer { state ->
if (state == TorchState.ON) {
// state on
} else {
// state off
}
})
扩展用例
专用API的可选效果,如HDR,肖像,夜间模式。
扩展与CameraX的Camera2核心分开。在下一个图表中,红色箭头表示当用户触发基于扩展的功能时的主要数据流,例如HDR图像捕获。
Image Capture Exender是一个抽象类,现在在api中它有5种不同的实现:
-
加载自动效果类型的OEM(原始设备制造商)扩展实现。
-
为美容效果类型加载OEM扩展实现。
-
加载用于bokeh效果类型的OEM扩展实现。
-
加载HDR效果类型的OEM扩展实现。
-
加载夜间效果类型的OEM扩展实现。
博克效应
对于每个用例,首先我们将检查实现步骤,然后检查每个步骤的分配代码。
对于扩展来说,需要提到的一件重要事情是,它们不是在所有设备上都可用:
-
华为(HDR,肖像): Mate 20系列,P30系列,荣誉魔法2,荣誉视图20。
-
三星(HDR,夜间,美容,汽车):银河笔记10系列。
基于文档:“对于支持供应商扩展的设备,以下所有内容都必须为真:
-
该效果具有来自设备OEM的库支持。
-
OEM库安装在当前设备上。
-
OEM库将设备报告为支持扩展。
-
该设备具有库所需的操作系统版本。
您可以优先启用扩展:如果扩展既受设备支持,又在设备上物理支持,那么它将被启用;否则,它会优雅地降级。"
步骤1:创建一个扩展器对象
val cameraControl = camera.cameraControl
val cameraInfo = camera.cameraInfo
cameraInfo.torchState.observe(this, Observer { state ->
if (state == TorchState.ON) {
// state on
} else {
// state off
}
})
步骤2:启用扩展
if (beautyExtender.isExtensionAvailable(cameraSelector)) {
beautyExtender.enableExtension(cameraSelector)
}
这就是所有人!简短回顾一下CameraX的主要优势:
如果你想了解更多关于CameraX的信息,你可以查看下一个资源:
-
正式文件
-
媒体文章@android Developers
-
播客
-
查看有关CameraX的最新更新
-
实验室测试设备
如果有什么不清楚或有问题,请随时留下评论。如果你喜欢它,请分享!
感谢您的阅读!