写点什么

CameraX 入门笔记

作者:Changing Lin
  • 2021 年 12 月 20 日
  • 本文字数:4044 字

    阅读完需:约 13 分钟

1.知识点

  • 学习使用 CameraX 预览摄像头的内容

  • 学习使用 CameraX 拍一张照片

  • 学习使用 CameraX 进行分析一个图片流的亮度

2.原理

  • LuminosityAnalyzer:亮度分析类,实现了 ImageAnalysis.Analyzer 接口,其中有一个 analyze 方法,该方法有一个 ImageProxy 参数

  • ImageProxy:一个与 Image 相似的代理类,简单来说,描述了一张图片的信息

  • getPlanes 接口:Get the array of pixel planes for this Image. The number of planes is determined by the format of the Image. 获取图片的水平像素分量数组

3.源码

  • activity_camera_xactivity

<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context=".CameraXActivity">
<Button android:id="@+id/camera_capture_button" android:layout_width="100dp" android:layout_height="100dp" android:layout_marginBottom="50dp" android:scaleType="fitCenter" android:text="Take Photo" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintBottom_toBottomOf="parent" android:elevation="2dp" />
<androidx.camera.view.PreviewView android:id="@+id/viewFinder" android:layout_width="match_parent" android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
复制代码
  • CameraXActivity

package com.besmart.hello
import android.Manifestimport android.content.pm.PackageManagerimport android.net.Uriimport android.os.Bundleimport android.util.Logimport android.widget.Toastimport androidx.appcompat.app.AppCompatActivityimport androidx.camera.core.*import androidx.camera.lifecycle.ProcessCameraProviderimport androidx.core.app.ActivityCompatimport androidx.core.content.ContextCompatimport kotlinx.android.synthetic.main.activity_camera_xactivity.*import java.io.Fileimport java.nio.ByteBufferimport java.text.SimpleDateFormatimport java.util.*import java.util.concurrent.ExecutorServiceimport java.util.concurrent.Executors
typealias LumaListener = (luma: Double) -> Unit
class CameraXActivity : AppCompatActivity() { private var imageCapture: ImageCapture? = null
private lateinit var outputDirectory: File private lateinit var cameraExecutor: ExecutorService
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera_xactivity)
// Request camera permissions if (allPermissionsGranted()) { startCamera() } else { ActivityCompat.requestPermissions( this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS) }
// Set up the listener for take photo button camera_capture_button.setOnClickListener { takePhoto() }
outputDirectory = getOutputDirectory()
cameraExecutor = Executors.newSingleThreadExecutor() }
private fun takePhoto() { // Get a stable reference of the modifiable image capture use case val imageCapture = imageCapture ?: return
// Create time-stamped output file to hold the image val photoFile = File( outputDirectory, SimpleDateFormat(FILENAME_FORMAT, Locale.US ).format(System.currentTimeMillis()) + ".jpg")
// Create output options object which contains file + metadata val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
// Set up image capture listener, which is triggered after photo has // been taken imageCapture.takePicture( outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback { override fun onError(exc: ImageCaptureException) { Log.e(TAG, "Photo capture failed: ${exc.message}", exc) }
override fun onImageSaved(output: ImageCapture.OutputFileResults) { val savedUri = Uri.fromFile(photoFile) val msg = "Photo capture succeeded: $savedUri" Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() Log.d(TAG, msg) } }) }
private fun startCamera() { val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener(Runnable { // Used to bind the lifecycle of cameras to the lifecycle owner val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Preview val preview = Preview.Builder() .build() .also { it.setSurfaceProvider(viewFinder.surfaceProvider) }
imageCapture = ImageCapture.Builder() .build() val imageAnalyzer = ImageAnalysis.Builder() .build() .also { it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma -> Log.d(TAG, "Average luminosity: $luma") }) }
// Select back camera as a default val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try { // Unbind use cases before rebinding cameraProvider.unbindAll()
// Bind use cases to camera cameraProvider.bindToLifecycle( this, cameraSelector, preview, imageCapture, imageAnalyzer)
} catch(exc: Exception) { Log.e(TAG, "Use case binding failed", exc) }
}, ContextCompat.getMainExecutor(this)) }
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all { ContextCompat.checkSelfPermission( baseContext, it) == PackageManager.PERMISSION_GRANTED }
private fun getOutputDirectory(): File { val mediaDir = externalMediaDirs.firstOrNull()?.let { File(it, resources.getString(R.string.app_name)).apply { mkdirs() } } return if (mediaDir != null && mediaDir.exists()) mediaDir else filesDir }
override fun onDestroy() { super.onDestroy() cameraExecutor.shutdown() } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == REQUEST_CODE_PERMISSIONS) { if (allPermissionsGranted()) { startCamera() } else { Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show() finish() } } } companion object { private const val TAG = "CameraXBasic" private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS" private const val REQUEST_CODE_PERMISSIONS = 10 private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA) }
private class LuminosityAnalyzer(private val listener: LumaListener) : ImageAnalysis.Analyzer {
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 }
override fun analyze(image: ImageProxy) { Log.e(TAG, "analyze: ${image.height} ${image.width}") val buffer = image.planes[0].buffer val data = buffer.toByteArray() val pixels = data.map { it.toInt() and 0xFF } val luma = pixels.average()
listener(luma)
image.close() } }}
复制代码


发布于: 1 小时前阅读数: 3
用户头像

Changing Lin

关注

获得机遇的手段远超于固有常规之上~ 2020.04.29 加入

我能做的,就是调整好自己的精神状态,以最佳的面貌去面对那些未曾经历过得事情,对生活充满热情和希望。

评论

发布
暂无评论
CameraX入门笔记