Android CameraX 使用说明的翻译 1.0.0-beta03版本 安卓10.0真机可运行

学习Android CameraX 顺便翻译一下 官方给的说明

翻译来源CameraX 使用入门 该链接可能需要科学上网

额 前面那几章权限管理 就比较简单了 不翻了 放上布局文件吧 很简单 一个按钮一个 预览的View


<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=".MainActivity">

    <Button
        android:id="@+id/camera_capture_button"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:scaleType="fitCenter"
        android:elevation="2dp"
        android:text="Take Photo"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.camera.view.PreviewView
        android:id="@+id/viewFinder"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

androidx.constraintlayout.widget.ConstraintLayout>

当然AndroidManifest.xml需要注册权限

	<!-- 这个貌似是 app需要相机的类型 此处是任何相机 还可以有其他类型 比如需要聚焦 --!>
    <uses-feature android:name="android.hardware.Camera.any"/>
    <uses-permission android:name="android.permission.CAMERA"/>

以下是翻译正文

前三章 如何构建项目以及权限管理 略

4. 实现预览用例

在相机应用程序中,取景器用于让用户预览将要拍摄的照片。你可以使用Camerax Preview类来实现一个取景器。

要使用Preview,您首先需要定义一个配置,然后用它来创建用例的一个实例。你需要绑定到CameraX生命周期的结果实例。
1.复制代码到startCamera()函数

下面的分析您刚才复制的代码。

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 = Preview.Builder().build()

            //select back camera
            val cameraSelector =
                CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK)

                    .build()

            try {
                //unbide use cases before rebinding
                cameraProvider.unbindAll()

                //bind use cases to camera
                camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)
                preview?.setSurfaceProvider(viewFinder.createSurfaceProvider(camera?.cameraInfo))

            } catch (exc: Exception) {
                Log.e(TAG, "Use case binding failed", exc)
            }

        },ContextCompat.getMainExecutor(this))
    }
  • 创建一个ProcessCameraProvider。这用于将相机的生命周期绑定到生命周期所有者。这允许你不用担心打开和关闭相机,因为CameraX是生命周期感知的
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
  • 添加一个侦听器到cameraProviderFuture。添加一个Runnable作为一个参数,我们稍后将填充它。添加ContextCompat.getMainExecutor() 作为第二个参数,它返回在主线程上运行的执行程序。
cameraProviderFuture.addListener(Runnable {},ContextCompat.getMainExecutor(this))
  • 在这个Runalbe 添加一个ProcessCameraProvider,它用于将相机的生命周期绑定到应用程序进程中的LifecycleOwner。
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
  • 初始化你的Preview
preview = Preview.Builder().build()
  • 创建一个CameraSelector对象和使用CameraSelector.Builder.requireLensFacing()函数来打开你想要的镜头
CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK)
  • 创建一个try块。在那个块中,确保没有东西绑定到你的cameraProvider,然后绑定你的cameraSelector和preview对象到cameraProvider。将取景器的surface提供程序附加到preview。
            try {
               //unbide use cases before rebinding
               cameraProvider.unbindAll()
               //bind use cases to camera
               camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)
               preview?.setSurfaceProvider(viewFinder.createSurfaceProvider(camera?.cameraInfo))
           }
  • 这段代码有几种可能失败的情况,比如应用程序不再是焦点。如果出现故障,将此代码包装在catch块中以进行日志记录。
			catch (exc: Exception) {
               Log.e(TAG, "Use case binding failed", exc)
           }

2.运行APP,然后你就能看到相机的预览画面

5. 实现ImageCapture用例

与预览相比,其他用例的工作方式非常类似。首先,您必须定义一个用于实例化实际用例对象的配置对象。要捕获照片,您需要实现takePhoto()方法,该方法在按下捕获按钮时调用

将此代码复制到takePhoto()方法中。

下面的要点将分解您刚才复制的代码。

private fun takePhoto() {
        //Get a stable reference of the modifiable image capture use case
        //获取可修改图像捕获用例的稳定引用
        val imageCapture = imageCapture ?: return

        //Create timestamped output file to hold the image
        val photoFile = File(
            outputDirectory,
            SimpleDateFormat(FILENAME_FORMAT, Locale.CHINA)
                .format(System.currentTimeMillis()) + ".JPG"
        )

        //Create output pptions object which constins file + metadata
        val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()

        //Setup image capture listener which is triggered after photo has been taken
        //设置图像捕获监听器,在拍照后触发
        imageCapture.takePicture(
            outputOptions,
            ContextCompat.getMainExecutor(this),
            object  : ImageCapture.OnImageSavedCallback{
                override fun onError(exception: ImageCaptureException) {
                    Log.e(TAG, "Photo capture failed: ${exception.message}", exception)
                }

                override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                    val savedUri = Uri.fromFile(photoFile)
                    val msg = "Photo caputre succeeded: $savedUri"
                    Toast.makeText(baseContext,msg,Toast.LENGTH_SHORT).show()
                    Log.d(TAG,msg)
                }
            }
        )
    }
  • 首先,获取对ImageCapture用例的引用。如果用例为空,则从函数中返回。如果在设置图像捕获之前点击photo按钮,则该值为null。如果没有return语句,应用程序如果为null就会崩溃。(为啥是崩溃? 就是拍照没反应吧。。。)
val imageCapture = imageCapture ?: return
  • 接下来创建一个文件来保存图像。添加一个时间戳,这样文件名将是唯一的。
	//Create timestamped output file to hold the image
       val photoFile = File(
           outputDirectory,
           SimpleDateFormat(FILENAME_FORMAT, Locale.CHINA)
               .format(System.currentTimeMillis()) + ".JPG"
       )
  • 创建一个outputFileoptions对象。在这个对象中,您可以指定有关您希望输出如何的内容。您希望将输出保存到我们刚刚创建的文件中,因此添加您的photoFile。
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
  • 在imageCapture对象上调用takePicture()。传入输出、执行程序和映像保存时的回调。接下来将填写回调。
imageCapture.takePicture(
            outputOptions,
            ContextCompat.getMainExecutor(this),
            object  : ImageCapture.OnImageSavedCallback{}
            )
  • 在图像捕获失败或保存图像捕获失败的情况下,添加一个错误情况以记录它失败。
                override fun onError(exception: ImageCaptureException) {
                    Log.e(TAG, "Photo capture failed: ${exception.message}", exception)
                }

  • 如果捕捉没有失败,照片拍摄成功!将照片保存到前面创建的文件中,向用户表示祝贺,并打印一条日志语句。
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                    val savedUri = Uri.fromFile(photoFile)
                    val msg = "Photo caputre succeeded: $savedUri"
                    Toast.makeText(baseContext,msg,Toast.LENGTH_SHORT).show()
                    Log.d(TAG,msg)
                }
  1. 转到startCamera()方法,并在“预览代码”下复制这段代码
imageCapture=ImageCapture.Builder().build()

这里显示粘贴代码的方法:

private fun startCamera() {
	...
	preview = Preview.Builder().build()
	//Paste image capture code here!
	val cameraSelector = CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
	...
  1. 最后,在try块中更新对bindToLifecycle()的调用,包括新的用例:
camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
  1. 重新运行应用程序并按下拍照。您将在屏幕上看到一个Toast,并在日志中看到一条消息。

6. 实现ImageAnalysis用例

如果运行Q或更低,那么同时实现预览、图像捕获和图像分析将不适用于Android Studio的设备模拟器。我们建议使用真实的设备来测试这部分代码。

让你的相机应用程序更有趣的一个好方法是使用ImageAnalysis功能。它允许您定义一个实现ImageAnalysis的自定义类。分析器接口,它将被调用与传入的摄像机帧。您将不必担心管理相机会话状态或甚至处理图像;绑定到我们想要的生命周期就足够了,就像其他生命周期感知组件一样。

  1. 将这个分析器作为main .kt中的一个内部类添加进来。
    这个分析器记录图像的平均亮度。要创建分析程序,您需要在实现ImageAnalysis的类中重写analyze函数。分析仪的接口。
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) {

       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()
   }
}

用我们的类实现ImageAnalysis。Analyzer接口,所有我们需要做的是在ImageAnalysis中实例化一个LuminosityAnalyzer的实例,就像所有其他的用例一样,在调用CameraX.bindToLifecycle()之前,再次更新’ startCamera() '函数:

  1. 在startCamera()方法中,在imageCapture()代码下添加此代码。
imageAnalyzer = ImageAnalysis.Builder()
  .build()
  .also {
      it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
          Log.d(TAG, "Average luminosity: $luma")
      })
  }

这显示了粘贴代码的方法:

private fun startCamera() {
       ... 

       imageCapture = ImageCapture.Builder()
   .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
   .build()

       // Paste image analyzer code here!


       val cameraSelector = CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()

       ... 
}
  1. 更新bindToLifecycle()调用cameraProvider以包含imageAnalyzer。
camera = cameraProvider.bindToLifecycle(
   this, cameraSelector, preview, imageCapture, imageAnalyzer)
  1. 现在运行应用程序!它大约每秒在logcat中生成一个类似的消息。
D/CameraXApp: Average luminosity: ...

翻译结束

最后把整个MainActivity.kt放上来

package city.carcate.u.myapplication

import android.Manifest
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.provider.ContactsContract
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.common.util.concurrent.ListenableFuture
import kotlinx.android.synthetic.main.activity_main.*
import java.io.File
import java.lang.Exception
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.Executor
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

/**
 *
 */
class MainActivity : AppCompatActivity() {
    private var preview: Preview? = null
    private var imageCapture: ImageCapture? = null
    private var camera: Camera? = null

    private lateinit var outputDirectory: File
    private lateinit var cameraExecutor: ExecutorService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if (allPermissionsGranted()) {
            startCamera()
        } else {
            ActivityCompat.requestPermissions(
                this, REQUIRED_PERMISSIONS, REQUEST_CODE_PREMISSIONS
            )
        }
        //setup the listener for take photo button
        camera_capture_button.setOnClickListener { takePhoto() }
        outputDirectory = getOutputDirectory()
        cameraExecutor = Executors.newSingleThreadExecutor()
    }

    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 = Preview.Builder().build()
            imageCapture = ImageCapture.Builder().build()

            //select back camera  选择后置摄像头
            val cameraSelector =
                CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()

            try {
                //unbide use cases before rebinding 先解绑一下
                cameraProvider.unbindAll()
                //bind use cases to camera
                camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
                preview?.setSurfaceProvider(viewFinder.createSurfaceProvider(camera?.cameraInfo))
            } catch (exc: Exception) {
                Log.e(TAG, "Use case binding failed", exc)
            }

        }, ContextCompat.getMainExecutor(this))

    }

    private fun takePhoto() {
        //Get a stable reference of the modifiable image capture use case
        //获取可修改图像捕获用例的稳定引用
        val imageCapture = imageCapture ?: return

        //Create timestamped output file to hold the image
        val photoFile = File(
            outputDirectory,
            SimpleDateFormat(FILENAME_FORMAT, Locale.CHINA)
                .format(System.currentTimeMillis()) + ".JPG"
        )

        //Create output pptions object which constins file + metadata
        val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()

        //Setup image capture listener which is triggered after photo has been taken
        //设置图像捕获监听器,在拍照后触发
        imageCapture.takePicture(
            outputOptions,
            ContextCompat.getMainExecutor(this),
            object : ImageCapture.OnImageSavedCallback {
                override fun onError(exception: ImageCaptureException) {
                    Log.e(TAG, "Photo capture failed: ${exception.message}", exception)
                }

                override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                    val savedUri = Uri.fromFile(photoFile)
                    val msg = "Photo caputre succeeded: $savedUri"
                    Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
                    Log.d(TAG, msg)
                }
            }
        )
    }

    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 onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        if (requestCode == REQUEST_CODE_PREMISSIONS) {
            if (allPermissionsGranted()) {
                startCamera()
            } else {
                Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_LONG)
                    .show()
                finish()
            }
        }
    }

    companion object {
        private const val TAG = "CameraXBasic"
        private const val FILENAME_FORMAT = "yyyy-MM-mm-ss-SSS"	//这个时间怪怪的  正常不是这个吗 yyyy-MM-dd HH:mm:ss
        private const val REQUEST_CODE_PREMISSIONS = 10
        private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
    }
}

还有app配置文件下引入的库

// CameraX core library
    def camerax_version = '1.0.0-beta03'
    implementation "androidx.camera:camera-core:$camerax_version"

    // CameraX Camera2 extensions
    implementation "androidx.camera:camera-camera2:$camerax_version"

    // CameraX Lifecycle library
    implementation "androidx.camera:camera-lifecycle:$camerax_version"

    // CameraX View class
    implementation 'androidx.camera:camera-view:1.0.0-alpha10'

    // CameraX Extensions library
     implementation "androidx.camera:camera-extensions:1.0.0-alpha10"
最后运行OK 基本实现了拍照的 功能

你可能感兴趣的:(笔记)