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
版权声明: 本文为 InfoQ 作者【Changing Lin】的原创文章。
原文链接:【http://xie.infoq.cn/article/f639b10f6502d75fe59b5e14e】。文章转载请联系作者。
Changing Lin
关注
获得机遇的手段远超于固有常规之上~ 2020.04.29 加入
我能做的,就是调整好自己的精神状态,以最佳的面貌去面对那些未曾经历过得事情,对生活充满热情和希望。











    
评论