CameraX(主要关注三块内容前三个内容)
// CameraX core library using the camera2 implementation
def camerax_version = "1.0.0-beta01"
implementation "androidx.camera:camera-camera2:${camerax_version}"
// If you want to use the CameraX View class
implementation "androidx.camera:camera-view:1.0.0-alpha08"
// If you want to use the CameraX Extensions library
implementation "androidx.camera:camera-extensions:1.0.0-alpha08"
// If you want to use the CameraX Lifecycle library
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
注:这里是有坑的,1.0.0-beta01是第一个稳定版本也是当前最新的版本,以前的alpha01到alpha10不堪回首,每一版API变动都很大,所以尽量使用稳定版…
相机权限:
注:动态权限申请activity、fragment和fragment的子fragment的申请方式不一样!!!,参考我的另一篇文章:https://blog.csdn.net/ydduong
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".vision.CameraXFragment">
<androidx.camera.view.PreviewView
android:id="@+id/textureView"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toTopOf="@+id/textView2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:text="TextView"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="@+id/textView3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
注:用于显示相机的组件,有TextureView,PreviewView等,alpha版使用TextureView,beta版使用的是PreviewView (我看官方代码是这么写的,不知道为啥)
package com.example.image3.vision
import android.Manifest
......
import kotlin.collections.ArrayList
typealias LumaListener = (luma: Double) -> Unit // 图像分析监听的放回类型,(typealias是取个别名)
private const val REQUEST_CODE_PERMISSIONS = 10 // 权限标识符
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA) // 相机权限
// 继承CameraXConfig.Provider,重载getCameraXConfig方法
class CameraXFragment : Fragment(), CameraXConfig.Provider {
override fun getCameraXConfig(): CameraXConfig {
return Camera2Config.defaultConfig()
}
private lateinit var viewModel: CameraXViewModel // viewModel只有一个变量:平均亮度
private lateinit var cameraProviderFuture : ListenableFuture<ProcessCameraProvider> // 相机的控制者
private lateinit var imagePreview: Preview // 图像预览
private lateinit var imageAnalysis: ImageAnalysis // 图像分析
private val executor = Executors.newSingleThreadExecutor() // 后台线程
private lateinit var previewView: PreviewView // xml里显示相机的组件
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.camera_x_fragment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
viewModel = ViewModelProviders.of(this).get(CameraXViewModel::class.java)
previewView = textureView
// 显示数据
viewModel.imageLight.observe(this, androidx.lifecycle.Observer {
textView2.text = it.toString()
})
// 检查相机权限
if (allPermissionsGranted()) {
Log.v(CameraXFragment::class.java.simpleName, "已有权限,执行")
previewView.post {
startCamera() }
} else {
Log.v(CameraXFragment::class.java.simpleName, "没有权限,请求权限")
requestPermissions(REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
}
}
// 相机操作
private fun startCamera() {
// 预览
imagePreview = Preview.Builder().apply {
setTargetAspectRatio(AspectRatio.RATIO_16_9)
setTargetRotation(previewView.display.rotation)
}.build()
imagePreview.setSurfaceProvider(previewView.previewSurfaceProvider)
// 分析:LuminosityAnalyzer 就是分析器,回调值就是it; MainScope().launch就是主线程
imageAnalysis = ImageAnalysis.Builder().apply {
setImageQueueDepth(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
setTargetResolution(Size(224, 224))
}.build()
imageAnalysis.setAnalyzer(executor, LuminosityAnalyzer {
MainScope().launch {
viewModel.imageLight.value = it
}
Log.v(CameraXFragment::class.java.simpleName, it.toString())
})
// 绑定
val cameraSelector = CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
cameraProviderFuture.addListener(Runnable {
val cameraProvider = cameraProviderFuture.get()
cameraProvider.bindToLifecycle(this, cameraSelector, imagePreview, imageAnalysis)
}, ContextCompat.getMainExecutor(requireContext()))
}
// 权限请求结果回调
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults: IntArray
) {
Log.v(CameraXFragment::class.java.simpleName, "执行回调结果")
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
textureView.post {
startCamera() }
} else {
Toast.makeText(
requireContext(),
"Permissions not granted by the user.",
Toast.LENGTH_SHORT
).show()
// 这里应该是返回上一层,或者跳转到该页面的时候,就先判断有没有权限
activity?.finish()
}
}
}
// 检查权限
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(
requireContext(), it
) == PackageManager.PERMISSION_GRANTED
}
// 平均亮度
private class LuminosityAnalyzer(listener: LumaListener? = null) : ImageAnalysis.Analyzer {
private val frameRateWindow = 8
private val frameTimestamps = ArrayDeque<Long>(5)
private val listeners = ArrayList<LumaListener>().apply {
listener?.let {
add(it) } }
private var lastAnalyzedTimestamp = 0L
var framesPerSecond: Double = -1.0
private set
/**
* Used to add listeners that will be called with each luma computed
*/
fun onFrameAnalyzed(listener: LumaListener) = listeners.add(listener)
/**
* Helper extension function used to extract a byte array from an image plane buffer
*/
private fun ByteBuffer.toByteArray(): ByteArray {
rewind() // Rewind the buffer to zero
val data = ByteArray(remaining())
get(data) // Copy the buffer into a byte array
return data // Return the byte array
}
/**
* Analyzes an image to produce a result.
*
* The caller is responsible for ensuring this analysis method can be executed quickly
* enough to prevent stalls in the image acquisition pipeline. Otherwise, newly available
* images will not be acquired and analyzed.
*
*
The image passed to this method becomes invalid after this method returns. The caller
* should not store external references to this image, as these references will become
* invalid.
*
* @param image image being analyzed VERY IMPORTANT: Analyzer method implementation must
* call image.close() on received images when finished using them. Otherwise, new images
* may not be received or the camera may stall, depending on back pressure setting.
*
*/
override fun analyze(image: ImageProxy) {
// If there are no listeners attached, we don't need to perform analysis
if (listeners.isEmpty()) {
image.close()
return
}
// Keep track of frames analyzed
val currentTime = System.currentTimeMillis()
frameTimestamps.push(currentTime)
// Compute the FPS using a moving average
while (frameTimestamps.size >= frameRateWindow) frameTimestamps.removeLast()
val timestampFirst = frameTimestamps.peekFirst() ?: currentTime
val timestampLast = frameTimestamps.peekLast() ?: currentTime
framesPerSecond = 1.0 / ((timestampFirst - timestampLast) /
frameTimestamps.size.coerceAtLeast(1).toDouble()) * 1000.0
// Analysis could take an arbitrarily long amount of time
// Since we are running in a different thread, it won't stall other use cases
lastAnalyzedTimestamp = frameTimestamps.first
// Since format in ImageAnalysis is YUV, image.planes[0] contains the luminance plane
val buffer = image.planes[0].buffer
// Extract image data from callback object
val data = buffer.toByteArray()
// Convert the data into an array of pixel values ranging 0-255
val pixels = data.map {
it.toInt() and 0xFF }
// Compute average luminance for the image
val luma = pixels.average()
// Call all listeners with new value
listeners.forEach {
it(luma) }
image.close()
}
}
}
注:如果你要传参到分析器内部,那么你的回调类型,一定要放在最后一个参数的位置像这样定义:private class LuminosityAnalyzer(st: String, listener: LumaListener? = null) : ImageAnalysis.Analyzer { }
- CameraX介绍参考链接:https://glumes.com/post/android/google-jetpack-camerax
- 官方代码参考:https://github.com/android/camera-samples