微笑,是相机![预览和捕获]

372 阅读5分钟

原文地址:magdamiu.com/2020/08/10/…

作者:MAGDA MIU

在我之前关于CameraX的文章中,我介绍了使用最古老的Camera API所面临的挑战,以及使用这个新API的优势。

用例驱动的方法是使用CameraX最重要的优势之一。UseCase是一个抽象类,目前它支持3个实现,每个用例是完全独立的。我们可以只使用其中一个,也可以将它们结合起来。

预览用例

使用表面显示摄像机的实时馈送。

对于每个用例,首先我们将检查实现步骤,然后检查每个步骤的分配代码。

步骤1:添加gradle依赖项

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}


def camerax = "1.0.0-beta07"
implementation "androidx.camera:camera-camera2:${camerax}"
implementation "androidx.camera:camera-lifecycle:${camerax}"
implementation 'androidx.camera:camera-view:1.0.0-alpha11'
implementation 'androidx.camera:camera-extensions:1.0.0-alpha11'

步骤2:权限处理

// manifest
<uses–feature android:name="android.hardware.camera.any" />
<uses–permission android:name="android.permission.CAMERA" />


// fragment or activity
if (areAllPermissionsGranted()) {
    startCamera()
} else {
    ActivityCompat.requestPermissions(
        this, PERMISSIONS, CAMERA_REQUEST_PERMISSION_CODE)
}
  • 添加android.hardware.camera.any确保设备有摄像头。指定. any意味着它可以是前置摄像头或后置摄像头。

  • 如果我们使用没有**. anyandroid.hardware.camera**,如果你有一个没有后置摄像头的设备,比如大多数Chromebook,它就无法工作。第二行添加了访问该摄像机的权限。

步骤3:在布局中添加Preview View

<FrameLayout
     android:id="@+id/container"
     android:layout_width="match_parent"
     android:layout_height="match_parent">

     <androidx.camera.view.PreviewView
         android:id="@+id/preview"
         android:layout_width="match_parent"
         android:layout_height="match_parent" >
     </androidx.camera.view.PreviewView>
 </FrameLayout>
  • 自测试版发布以来,Preview View实现相机预览的推荐方式。

  • 如果你想使用你自己的曲面(例如,纹理视图),那么你必须实现你自己的曲面提供程序,以及确保你处理适当的大小和方向,这可能很棘手。

幕后发生了什么?

  • Preview View是一个自定义视图,用于显示CameraX预览用例相机馈送。

  • Preview View还扩展了Frame Layout,我们都知道它是一个View Group

  • 默认实现Surface View。

  • 但是如果相机设备运行在向后兼容模式下,在代码中这意味着设备具有支持的相机硬件级别相机特性#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,实际实现模式将是纹理视图

val previewView = findViewById(R.id.preview)

// initialize the Preview object (current use case)
val preview = Preview.Builder().build()

为了在初始化CameraX时进行精细控制,我们可以实现CameraX Config。应用程序类中的提供程序接口。

步骤4:创建Process Camera Provider的实例

val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

// used to bind the lifecycle of camera to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

// add a listener to the cameraProviderFuture
val cameraExecutor = ContextCompat.getMainExecutor(this)
cameraProviderFuture.addListener(Runnable {}, cameraExecutor)
  • Process Camera Provider是一个单例,可用于将相机的生命周期绑定到应用程序流程中任何Life cle Owner。

  • 进程中只能存在单个进程摄像机提供程序,并且可以使用*get Instance(*Context)检索它。

  • 它帮助我们不用担心打开和关闭相机,因为CameraX是生命周期感知的。

  • 是一个运行在主线程上的执行器。

  • 将监听器添加到包含2个参数: Runna ble和执行程序Cameron Provider Future。

步骤5:选择Camera并将其绑定到生命周期

val backCamera = CameraSelector.LENS_FACING_BACK;
val frontCamera = CameraSelector.LENS_FACING_FRONT;
val cameraSelector = CameraSelector.Builder().requireLensFacing(backCamera).build()

cameraProviderFuture.addListener(Runnable {
    cameraProvider.unbindAll();

    camera = cameraProvider.bindToLifecycle(
        this as LifecycleOwner,
        cameraSelector,
        preview
    )

    preview?.setSurfaceProvider(previewView.createSurfaceProvider(camera?.cameraInfo))
}, cameraExecutor)
  • 此代码将被添加到Runna ble

  • 还建议创建一个try块,并在其中添加此代码。此代码可能会失败,例如,如果应用不再处于焦点中。

  • 在重新绑定之前取消绑定用例。

  • 建议我们在一次调用中通过CameraX将用例绑定到生命周期,并使用相同的生命周期所有者。

  • 通过一次提供所有用例,我们给CameraX一个找到折衷方案的机会,这样所有用例都可以运行。

捕获用例

它用于保存高质量的图像。

步骤1:创建Image Capture引用

val imageCapture = ImageCapture.Builder().build()

// SETUP CAPTURE MODE
// to optimize photo capture for quality
val captureMode = ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY

// to optimize photo capture for latency (default)
val captureMode = ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY

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

// SETUP FLASH MODE
// flash will always be used when taking a picture
val flashMode = ImageCapture.FLASH_MODE_ON

// flash will never be used when taking a picture (default)
val flashMode = ImageCapture.FLASH_MODE_OFF

// flash will be used according to the camera system's determination
val flashMode = ImageCapture.FLASH_MODE_AUTO

imageCapture = ImageCapture.Builder()
    .setFlashMode(flashMode)
    .build()

// SETUP ASPECT RATIO
// 16:9 standard aspect ratio
val aspectRatio = AspectRatio.RATIO_16_9

// 4:3 standard aspect ratio (default)
val aspectRatio = AspectRatio.RATIO_4_3

imageCapture = ImageCapture.Builder()
    .setTargetAspectRatio(aspectRatio)
    .build()

// SETUP TARGET RESOLUTION
val metrics = DisplayMetrics().also { previewView.display.getRealMetrics(it) }
val screenSize = Size(metrics.widthPixels, metrics.heightPixels)

imageCapture = ImageCapture.Builder()
    .setTargetResolution(screenSize)
    .setTargetName("CameraConference")
    .build()

  • 通过一次提供所有用例,我们给CameraX一个找到折衷方案的机会,这样所有用例都可以运行。

  • 如果我们想在捕获图像时拥有更多的控制权,我们可以向构建器添加不同的功能,如照片捕获优化、闪光模式、纵横比或目标分辨率。

  • 捕获模式:如果未设置,默认值为Image Capture。CAPTURE_MODE_MINIMIZE_LATENCY

  • 闪存模式:如果不设置默认值是Image Capture。FLASH_MODE_OFF

  • 宽高比:如果没有设置,宽高比为4:3的分辨率将被优先考虑。

  • set Target分辨率:如果未设置,将选择要使用的最大可用分辨率。

  • set Target Name(调试目的):如果不设置,目标名称将默认为自动生成的唯一名称,带有类规范名称和随机UUID

步骤2:添加定向事件侦听器

val orientationEventListener = object : OrientationEventListener(this as Context) {
    override fun onOrientationChanged(orientation: Int) {
        val rotation: Int = when (orientation) {
            in 45..134 -> Surface.ROTATION_270
            in 135..224 -> Surface.ROTATION_180
            in 225..314 -> Surface.ROTATION_90
            else -> Surface.ROTATION_0
        }

        // default => Display.getRotation()
        imageCapture.targetRotation = rotation
    }
}
orientationEventListener.enable()

步骤3:图像文件管理

val file = File(
    externalMediaDirs.first(),
    "${System.currentTimeMillis()}.jpg"
)
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(file).build()
  • *Image Capture.*Output File Options为我们提供了保存捕获图像的选项

  • 此类的生成器用于配置保存位置,可以是:

    • a文件(如代码示例中所示)
    • a Media Store或
    • 输出流。

步骤4:调用get Picture()

imageCapture.takePicture(outputFileOptions, cameraExecutor,
    object : ImageCapture.OnImageSavedCallback {

        override fun onImageSaved(outputFileResult: ImageCapture.OutputFileResults) {
             // yey!!! 🙂
        }

        override fun onError(exception: ImageCaptureException) {
            // ohhh!!! 😦
        }
    })
  • 此方法捕获图像并将其保存到作为参数提供的文件中(第一个)

  • 第二个参数表示将在其中运行回调方法的执行程序

  • 对于这个方法的每次调用,回调只会被调用一次,它包含了2个方法的实现

    • on Image Saved:当图像已成功保存时调用
  • on Error:当试图保存图像时发生错误时调用

步骤5:更新调用以绑定生命周期

// bind the image capture use case to camera
camera = cameraProvider.bindToLifecycle(
    this as LifecycleOwner,
    cameraSelector,
    preview,
    imageCapture
)

图像捕捉用例旨在捕捉高清晰度和高质量的照片,并提供自动白平衡、自动曝光和自动对焦(3A)功能,以及简单的手动相机控制。调用方负责决定如何使用捕获的图像:

  • [get Picture(Execitor, On Image Capture Callback](developer.android.com/reference/a…, androidx.camera.core.ImageCapture.OnImageCapturedCallback))):提供捕获图像的内存缓冲区。

  • get Picture(Output File Options, Execitor, On Image Saved Callback):将捕获的[图像保存](developer.android.com/reference/a…, java.util.concurrent.Executor, androidx.camera.core.ImageCapture.OnImageSavedCallback))到提供的文件位置。

如果有什么不清楚或有问题,请随时留下评论。如果你喜欢它,请分享!

感谢您的阅读!