IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Kotlin 集成 Camera2 API 在安卓上,非常棒 -> 正文阅读

[移动开发]Kotlin 集成 Camera2 API 在安卓上,非常棒

? ? 今天,我们将要去看一下怎么使用安卓创意集成的 Camera2 API 去得到一个集成相机。具体来说,我们将尽最大努力去用 SurfaceView 展示我们输入源的一张图片,和同时建立 ImageReader 处理来自相同来源的单独的帧。
?? ?
?? ?正如你所知,在Android平台上构建东西时,需要考虑很多未知因素。有这么多不同的设备类型,而且无法预测该设备的服务和硬件会是什么样子。这就是为什么我们需要与API接口,为我们解决这些未知值,我们需要安全地处理代码中的所有事件。Camera2让这一切变得简单。

?? ?因此,当您在某个设备上运行应用程序时,您可能想询问的第一件事是,我可以使用哪种设备?该设备是否同时具有正面和背面摄像头?它支持哪些决议?我们使用CameraManager回答这些问题。

????顺便说一下,我已经开始了一个新的、空的项目,我将从那里开始在MainActivity中构建。使用此API不需要任何外部依赖项。????????

需要权限

????????实际上,让我们回想一下。首先我们需要请求许可才能使用相机。这里有一个帮助您完成此操作的助手:

/** 请求相机权限助手  */
object CameraPermissionHelper {
    private const val CAMERA_PERMISSION_CODE = 0
    private const val CAMERA_PERMISSION = Manifest.permission.CAMERA

    /** 请检查我们是否拥有此应用程序所需的权限。 */
    fun hasCameraPermission(activity: Activity): Boolean {
        return ContextCompat.checkSelfPermission(activity, CAMERA_PERMISSION) == PackageManager.PERMISSION_GRANTED
    }

    /** 请检查我们是否拥有此应用程序所需的权限,如果没有,请求他们。  */
    fun requestCameraPermission(activity: Activity) {
        ActivityCompat.requestPermissions(
                activity, arrayOf(CAMERA_PERMISSION), CAMERA_PERMISSION_CODE)
    }

    /** 检查是否需要显示此许可的理由。  */
    fun shouldShowRequestPermissionRationale(activity: Activity): Boolean {
        return ActivityCompat.shouldShowRequestPermissionRationale(activity, CAMERA_PERMISSION)
    }

    /** 启动应用程序设置以授予权限  */
    fun launchPermissionSettings(activity: Activity) {
        val intent = Intent()
        intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
        intent.data = Uri.fromParts("package", activity.packageName, null)
        activity.startActivity(intent)
    }
}

实现这个回调

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    if (!CameraPermissionHelper.hasCameraPermission(this)) {
        Toast.makeText(this, "Camera permission is needed to run this application", Toast.LENGTH_LONG)
            .show()
        if (!CameraPermissionHelper.shouldShowRequestPermissionRationale(this)) {
            // 检查“不再询问”时权限被拒绝。
            CameraPermissionHelper.launchPermissionSettings(this)
        }
        finish()
    }

    recreate()
}

确保你加入了这一行在 Manifest 文件中

<uses-permission android:name="android.permission.CAMERA" />

然后调用

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    if (!CameraPermissionHelper.hasCameraPermission(this)) {
        CameraPermissionHelper.requestCameraPermission(this)
        return
    }
}

使用CameraManager访问设备

private fun startCameraSession() {
  val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
  if (cameraManager.cameraIdList.isEmpty()) {
    // 没有相机
    return
  }
  val firstCamera = cameraIdList[0]
  cameraManager.openCamera(firstCamera, object: CameraDevice.StateCallback() {
    override fun onDisconnected(p0: CameraDevice) { }
    override fun onError(p0: CameraDevice, p1: Int) { }

    override fun onOpened(cameraDevice: CameraDevice) {
        // 使用相机
        val cameraCharacteristics =    cameraManager.getCameraCharacteristics(cameraDevice.id)

        cameraCharacteristics[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP]?.let { streamConfigurationMap ->
            streamConfigurationMap.getOutputSizes(ImageFormat.YUV_420_888)?.let { yuvSizes ->
                val previewSize = yuvSizes.last()

            }

        }
    }
}, Handler { true })
}

????????在这里,我们使用 manager 抓取列表中的第一个相机。然后我们调用?CameraManager.openCamera(),传递 id 以访问该相机设备。一旦成功打开,我们将调用CameraManager.getCameraCharacteristics以查询该相机设备的详细信息。如果您想更详细地了解要使用哪台相机,可以在打开相机之前调用 getCameraCharacteristics 来检查它是否具有所需的属性。

????????在本例中,我检索相机的 StreamConfigurationMap ,其中包含有关设备支持的输出格式的所有信息。接下来,我获取YUV_420_888格式(如果存在),因为这是我的项目所需要的。您可以根据需要使用其他格式。我对此不太了解。然后,我从该列表中获取最后一个大小,这将是本示例所需的最低分辨率大小。

???????下一步,我们需要做一些新人可能不会想到的至关重要的事情。我们需要检查Android设备的方向和摄像头输出的数据的方向是否互换!设备可能是纵向的,但相机仍然根据相机传感器输出横向的图像。Camera2示例项目提供了一种方法来确定是否存在这种情况:?

private fun areDimensionsSwapped(displayRotation: Int, cameraCharacteristics: CameraCharacteristics): Boolean {
    var swappedDimensions = false
    when (displayRotation) {
        Surface.ROTATION_0, Surface.ROTATION_180 -> {
            if (cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) == 90 || cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) == 270) {
                swappedDimensions = true
            }
        }
        Surface.ROTATION_90, Surface.ROTATION_270 -> {
            if (cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) == 0 || cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) == 180) {
                swappedDimensions = true
            }
        }
        else -> {
            // 显示旋转无效
        }
    }
    return swappedDimensions
}

然后返回 startCameraSession:

val previewSize = yuvSizes.last()
// 账户.
val displayRotation = windowManager.defaultDisplay.rotation
val swappedDimensions = areDimensionsSwapped(displayRotation, cameraCharacteristics)
// 如果需要交换长和宽
val rotatedPreviewWidth = if (swappedDimensions) previewSize.height else previewSize.width
val rotatedPreviewHeight = if (swappedDimensions) previewSize.width else previewSize.height

设置 SurfaceView

????????此时,我们准备好与 SurfaceView 集成。要做到这一点其实很简单。首先,我们用 ?SurfaceView?替换自动生成的“Hello World!”文本视图

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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=".MainActivity">

    <SurfaceView
            android:id="@+id/surfaceView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

????????我们已经有了相机设备特性给出的输出大小,所以我们只需将 holder 上的预览固定大小设置为输出大小:

surfaceView.holder.setFixedSize(rotatedPreviewWidth, rotatedPreviewHeight)

现在我们只需要连接输入源,即我们的相机设备和 surface。我们使用CameraCaptureSession.CaptureCallback()执行此操作:

val previewSurface = surfaceView.holder.surface

val captureCallback = object : CameraCaptureSession.StateCallback()
  {
    override fun onConfigureFailed(session: CameraCaptureSession) {}
 
   override fun onConfigured(session: CameraCaptureSession) {
     // 会话已配置
        val previewRequestBuilder =   cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
  .apply {
    addTarget(previewSurface) 
  }
        session.setRepeatingRequest(
          previewRequestBuilder.build(), 
          object: CameraCaptureSession.CaptureCallback() {}, 
          Handler { true }
        )
    }
}

cameraDevice.createCaptureSession(mutableListOf(previewSurface), captureCallback, Handler { true })

????????如果你试着运行这个,一切都会好起来的。但是你不会看到任何预览出现…这是因为我们还没有调用startCameraSession()!事实上,它并没有那么简单,因为SurfaceView必须异步准备自己,即使在“活动”是交互式的之后,它也可能没有准备好。
????????所以我们需要一个回调:

val surfaceReadyCallback = object: SurfaceHolder.Callback {
    override fun surfaceChanged(p0: SurfaceHolder?, p1: Int, p2: Int, p3: Int) { }
    override fun surfaceDestroyed(p0: SurfaceHolder?) { }

    override fun surfaceCreated(p0: SurfaceHolder?) {
        startCameraSession()
    }
}

????????回到onCreate(),访问Kotlin启用的同一合成视图引用:

surfaceView.holder.addCallback(surfaceReadyCallback)

????????现在,当你运行时,你应该会看到一个漂亮的小预览…

?使用ImageReader处理图像

????????让我们快速完成向应用程序添加另一个输出源的脚手架:ImageReader。Camera2最酷的地方是它可以将任意数量的输入连接到各种输出。因此,我们可以显示预览并处理来自以下来源的图像:

//账户 
surfaceView.holder.setFixedSize(rotatedPreviewWidth, rotatedPreviewHeight)

// 配置图像读取器
val imageReader = ImageReader.newInstance(rotatedPreviewWidth, rotatedPreviewHeight,
    ImageFormat.YUV_420_888, 2)
imageReader.setOnImageAvailableListener({
        // 做一些事
}, Handler { true })

我们配置 ImageReader 和 surface view 用相同的图像格式

ImageReader将其数据渲染到Surface,我们可以直接访问该Surface:

// cont. 
val previewSurface = surfaceView.holder.surface
val recordingSurface = imageReader.surface

????????将其添加到我们的回调中。通过这样做,我们确保对于进入预览的每个图像,也会将其发送到 ImageReader:

// cont.
val previewRequestBuilder = camera.createCaptureRequest(TEMPLATE_PREVIEW).apply {
   addTarget(previewSurface)
   addTarget(recordingSurface)
}

并将其添加到我们session中的surfaces列表中:

// cont.
cameraDevice.createCaptureSession(mutableListOf(previewSurface, recordingSurface), captureCallback, Handler { true })

????????生成的图像将是一个包含输入图像缓冲区的对象,如果您愿意,可以使用层和所有您需要的东西来分解和重新组合图像的每一位。
????????就是这样!我希望这简化了您理解此API的过程。请务必查看主要来源,尤其是样本项目,以获得进一步的澄清。还有一个关于我使用上述代码的应用程序的快速简介:
????????KanjiReader是一个图像处理应用程序,可以让您即时翻译日语汉字,并查看其含义和读数的读数。???????

实现效果:

Android:?https://play.google.com/store/apps/details?id=tylerwalker.io.kanjireader

iOS:??Kanji-Reader on the App?Store

参考文章1

引用

  1. ???????Camera2 Documentation
  2. Camera2 Sample Project

文章来源

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-11-05 00:39:30  更:2022-11-05 00:40:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 17:45:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码