内容简介:近期,Google 的 Jetpack 组件又出了新的库:CameraX 。顾名思义:CameraX 就是用来进行 Camera 开发的官方库了,而且后续会有 Google 进行维护和升级。这对于广大 Camera 开发工程师和即将成为 Camera 的程序员来说,真是个好消息~~~官方有给出一个示例的工程,我 fork 了之后,加入使用 OpenGL 黑白滤镜渲染的操作,具体地址如下:
近期,Google 的 Jetpack 组件又出了新的库:CameraX 。
顾名思义:CameraX 就是用来进行 Camera 开发的官方库了,而且后续会有 Google 进行维护和升级。这对于广大 Camera 开发工程师和即将成为 Camera 的 程序员 来说,真是个好消息~~~
CameraX 介绍
官方有给出一个示例的工程,我 fork 了之后,加入使用 OpenGL 黑白滤镜渲染的操作,具体地址如下:
https://github.com/glumes/camera
官方并没有提到 CameraX 库具体如何进行 OpenGL 线程渲染的, 继续往下看,你会找到答案的~~~
关于 CameraX 更多的介绍,建议看看 Google I/O 大会上的视频记录,比看文档能了解更多内容~~~
https://www.youtube.com/watch?v=kuv8uK-5CLY
在视频中提到,目前有很多应用都开始接入了 CameraX,比如 Camera360、Tik Tok 等。
简述 Camera 开发
关于 Camera 的开发,之前也有写过相关的文章
Android 相机开发中的尺寸和方向问题 https://glumes.com/post/android/android-camera-aspect-ratio-and-orientation/ Android Camera 模型及 API 接口演变 https://glumes.com/post/android/android-camrea-api-evolution/
对于一个简单能用的 Camera 应用(Demo 级别)来说,关注两个方面就好了:预览和拍摄。
而预览和拍摄的图像都受到分辨率、方向的影响。Camera 最必备的功能就是能针对预览和拍摄提供两套分辨率,因此就得区分场景去设置。
对于拍摄还好说一点,要获得最好的图像质量,就选择同比例中分辨率最大的吧。
而预览的图像最终要呈现到 Android 的 Surface 上,因此选择分辨率的时候要考虑 Surface 的宽高比例,不要出现比例不匹配导致图像拉伸的现象。
另外,如果要做美颜、滤镜类的应用,就要把 Camera 预览的图像放到 OpenGL 渲染的线程上去,然后由 OpenGL 去做图像相关的操作,也就没 Camera 什么事了。等到拍摄图片时,可以由 OpenGL 去获取图像内容,也可以由 Camera 获得图像内容,然后经过 OpenGL 做离屏处理~~~
至于 Camera 开发的其他功能,比如对焦、曝光、白平衡、HDR 等操作,不一定所有的 Camera 都能够支持,而且也可以在上面的基础上当做 Camera 的一个 feature 去拓展开发,并不算难事,这也是一个 Camera 开发工程师进阶所要掌握的内容~~
CameraX 开发实践
CameraX 目前的版本是 1.0.0-alpha01
,在使用时要添加如下的依赖:
1 // CameraX 2 def camerax_version = "1.0.0-alpha01" 3 implementation "androidx.camera:camera-core:${camerax_version}" 4 implementation "androidx.camera:camera-camera2:${camerax_version}"
CameraX 向后兼容到 Android 5.0(API Level 21),并且它是基于 Camera 2.0 的 API 进行封装的,解决了市面上绝大部分手机的兼容性问题~~~
相比 Camera 2.0 复杂的调用流程,CameraX 就简化很多,只关心我们需要的内容就好了,不像前者得自己维护 CameraSession 会话等状态,并且 CameraX 和 Jetpack 主打的 Lifecycle 绑定在一起了,什么时候该打开相机,什么时候该释放相机,都交给 Lifecycle 生命周期去管理吧
上手 CameraX 主要关注三个方面:
-
图像预览(Image Preview)
-
图像分析(Image analysis)
-
图像拍摄(Image capture)
预览
不管是 预览 还是 图像分析、图像拍摄,CameraX 都是通过一个建造者模式来构建参数 Config 类,再由 Config 类创建预览、分析器、拍摄的类,并在绑定生命周期时将它们传过去。
1// // Apply declared configs to CameraX using the same lifecycle owner 2CameraX.bindToLifecycle( 3 lifecycleOwner: this, preview, imageCapture, imageAnalyzer)
既可以绑定 Activity 的 Lifecycle,也可以绑定 Fragment 的。
当需要解除绑定时:
1// Unbinds all use cases from the lifecycle and removes them from CameraX. 2 CameraX.unbindAll()
关于预览的参数配置,如果你有看过之前的文章:Android 相机开发中的尺寸和方向问题 想必就会很了解了。
提供我们的目标参数,由 CameraX 去判断当前 Camera 是否支持,并选择最符合的。
1fun buildPreviewUseCase(): Preview { 2 val previewConfig = PreviewConfig.Builder() 3 // 宽高比 4 .setTargetAspectRatio(aspectRatio) 5 // 旋转 6 .setTargetRotation(rotation) 7 // 分辨率 8 .setTargetResolution(resolution) 9 // 前后摄像头 10 .setLensFacing(lensFacing) 11 .build() 12 13 // 创建 Preview 对象 14 val preview = Preview(previewConfig) 15 // 设置监听 16 preview.setOnPreviewOutputUpdateListener { previewOutput -> 17 // PreviewOutput 会返回一个 SurfaceTexture 18 cameraTextureView.surfaceTexture = previewOutput.surfaceTexture 19 } 20 21 return preview 22}
通过建造者模式创建 Preview
对象,并且一定要给 Preview 对象设置 OnPreviewOutputUpdateListener
接口回调。
相机预览的图像流是通过 SurfaceTexture 来返回的,而在项目例子中,是通过把 TextureView 的 SurfaceTexture 替换成 CameraX 返回的 SurfaceTexture,这样实现了 TextureView 控件显示 Camera 预览内容。
另外,还需要考虑到设备的选择方向,当设备横屏变为竖屏了,TextureView 也要相应的做旋转。
1preview.setOnPreviewOutputUpdateListener { previewOutput -> 2 cameraTextureView.surfaceTexture = previewOutput.surfaceTexture 3 4 // Compute the center of preview (TextureView) 5 val centerX = cameraTextureView.width.toFloat() / 2 6 val centerY = cameraTextureView.height.toFloat() / 2 7 8 // Correct preview output to account for display rotation 9 val rotationDegrees = when (cameraTextureView.display.rotation) { 10 Surface.ROTATION_0 -> 0 11 Surface.ROTATION_90 -> 90 12 Surface.ROTATION_180 -> 180 13 Surface.ROTATION_270 -> 270 14 else -> return@setOnPreviewOutputUpdateListener 15 } 16 17 val matrix = Matrix() 18 matrix.postRotate(-rotationDegrees.toFloat(), centerX, centerY) 19 20 // Finally, apply transformations to TextureView 21 cameraTextureView.setTransform(matrix) 22}
TextureView 旋转的设置同样在 OnPreviewOutputUpdateListener
接口中去完成。
图像分析
在 bindToLifecycle
方法中, imageAnalyzer
参数并不是必需的。
ImageAnalysis
可以帮助我们做一些图像质量的分析,需要我们去实现 ImageAnalysis.Analyzer
接口的 analyze
方法。
1fun buildImageAnalysisUseCase(): ImageAnalysis { 2 // 分析器配置 Config 的建造者 3 val analysisConfig = ImageAnalysisConfig.Builder() 4 // 宽高比例 5 .setTargetAspectRatio(aspectRatio) 6 // 旋转 7 .setTargetRotation(rotation) 8 // 分辨率 9 .setTargetResolution(resolution) 10 // 图像渲染模式 11 .setImageReaderMode(readerMode) 12 // 图像队列深度 13 .setImageQueueDepth(queueDepth) 14 // 设置回调的线程 15 .setCallbackHandler(handler) 16 .build() 17 18 // 创建分析器 ImageAnalysis 对象 19 val analysis = ImageAnalysis(analysisConfig) 20 21 // setAnalyzer 传入实现了 analyze 接口的类 22 analysis.setAnalyzer { image, rotationDegrees -> 23 // 可以得到的一些图像信息,参见 ImageProxy 类相关方法 24 val rect = image.cropRect 25 val format = image.format 26 val width = image.width 27 val height = image.height 28 val planes = image.planes 29 } 30 31 return analysis 32}
在图像分析器的相关配置中,有个 ImageReaderMode
和 ImageQueueDepth
的设置。
ImageQueueDepth 会指定相机管线中图像的个数,提高 ImageQueueDepth 的数量会对相机的性能和内存的使用造成影响
其中,ImageReaderMode 有两种模式:
-
ACQUIRE_LATEST_IMAGE
-
该模式下,获得图像队列中最新的图片,并且会清空队列已有的旧的图像。
-
ACQUIRE_NEXT_IMAGE
-
该模式下,获得下一张图像。
在图像分析的 analyze
方法中,能通过 ImageProxy 类拿到一些图像信息,并基于这些信息做分析。
拍摄
拍摄同样有一个 Config 参数构建者类,而且设定的参数和预览相差不大,也是图像宽高比例、旋转方向、分辨率,除此之外还有闪光灯等配置项。
1fun buildImageCaptureUseCase(): ImageCapture { 2 val captureConfig = ImageCaptureConfig.Builder() 3 .setTargetAspectRatio(aspectRatio) 4 .setTargetRotation(rotation) 5 .setTargetResolution(resolution) 6 .setFlashMode(flashMode) 7 // 拍摄模式 8 .setCaptureMode(captureMode) 9 .build() 10 11 // 创建 ImageCapture 对象 12 val capture = ImageCapture(captureConfig) 13 cameraCaptureImageButton.setOnClickListener { 14 // Create temporary file 15 val fileName = System.currentTimeMillis().toString() 16 val fileFormat = ".jpg" 17 val imageFile = createTempFile(fileName, fileFormat) 18 19 // Store captured image in the temporary file 20 capture.takePicture(imageFile, object : ImageCapture.OnImageSavedListener { 21 override fun onImageSaved(file: File) { 22 // You may display the image for example using its path file.absolutePath 23 } 24 25 override fun onError(useCaseError: ImageCapture.UseCaseError, message: String, cause: Throwable?) { 26 // Display error message 27 } 28 }) 29 } 30 31 return capture 32}
在图像拍摄的相关配置中,也有个 CaptureMode
的设置。
它有两种选项:
-
MIN_LATENCY
-
该模式下,拍摄速度会相对快一点,但图像质量会打折扣
-
MAX_QUALITY
-
该模式下,拍摄速度会慢一点,但图像质量好
OpenGL 渲染
以上是关于 CameraX 的简单应用方面的内容,更关心的是如何用 CameraX 去做 OpenGL 渲染实现美颜。滤镜等效果。
还记得在图像预览 Preview 的 setOnPreviewOutputUpdateListener 方法中,会返回一个 SurfaceTexture
,相机的图像流就是通过它返回的。
那么要实现 OpenGL 线程的渲染,首先就要基于 EGL 去创建 OpenGL 绘制环境,然后利用 SurfaceTexture 的 attachToGLContext
方法,将 SurfaceTexture 添加到 OpenGL 线程去。
attachToGLContext 的参数是一个纹理 ID ,这个纹理就必须是 OES 类型的纹理。
然后再把这纹理 ID 绘制到 OpenGL 对应的 Surface 上,这可以看成是两个不同的线程在允许,一个 Camera 预览线程,一个 OpenGL 绘制线程。
如果你不是很理解的话,建议还是看看上面提供的代码地址:
https://github.com/glumes/camera
也可以关注我的微信公众号 【纸上浅谈】,里面有一些关于 OpenGL 学习和实践的文章~~~
CameraX 的拓展
如果你看了 Google I/O 大会的视频,那肯定了解 CameraX 的拓展属性。
在视频中提到 Google 也正在和华为、三星、LG、摩托摩拉等厂商进行合作,为了获得厂商系统相机的一些能力,比如 HDR 等。
不过考虑到目前的形势,可能和华为的合作难以继续下去了吧…
但还是期待 CameraX 能给带来更多的新特性吧~~~
参考
-
https://www.youtube.com/watch?v=kuv8uK-5CLY
-
https://proandroiddev.com/android-camerax-preview-analyze-capture-1b3f403a9395
文章推荐
觉得文章不错,欢迎转发和关注微信公众号【纸上浅谈】
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
游戏数据分析的艺术
于洋、余敏雄、吴娜、师胜柱 / 机械工业出版社 / 2015-7 / 79.00
《游戏数据分析的艺术》是中国游戏产业的开创性著作,具有里程碑意义,它首次系统讲解了如何对游戏行业的数据进行分析,在行业里竖起了一根标杆。作者是来自TalkingData等国内顶尖的数据分析机构和西山居这样的知名游戏公司的资深数据分析专家, 对游戏数据从不同的业务角度进行了诠释。本书详细剖析了游戏数据分析相关的指标、方法论、内容挖掘、数据挖掘、软件使用、游戏设计、运营策划、渠道推广、收入解读、用户分......一起来看看 《游戏数据分析的艺术》 这本书的介绍吧!