使用Firebase ML Kit和CameraX实现二维码扫描

一.介绍

1.什么是CameraX?

CameraX是在Google I / O 2019上宣布的Jetpack支持库。该库的主要目的是通过提供一致且易于使用的API来帮助开发人员简化相机应用程序的开发。

2.什么是Firebase ML Kit?

Firebase ML Kit是适用于Android和iOS的移动SDK,已在Google I / O 2018上发布。ML Kit随附了自然语言(文本识别,面部检测,条形码扫描,图像标记,对象检测和跟踪,地标识别)和视觉(识别文本的语言,翻译文本,生成智能回复)的常见用例。在这里插入图片描述

二.设置项目

1.在Android Studio中,从File⇒New Project创建一个新项目,然后从模板中选择Empty Activity,我的包名称是com.natigbabayev.qrscanner。
2.将 Firebase 添加到你的 Android 项目
3.打开app / build.gradle并添加Firebase ML Vision和Jetpack CameraX依赖项:

dependencies {
    //...

    // Make sure you have correct version of appcompat library
    implementation 'androidx.appcompat:appcompat:1.1.0-rc01'

    // Firebase ML Kit dependencies
    implementation 'com.google.firebase:firebase-ml-vision:21.0.0'

    // CameraX
    def camerax_version = "1.0.0-alpha03"
    implementation "androidx.camera:camera-core:${camerax_version}"
    implementation "androidx.camera:camera-camera2:${camerax_version}"
}

4.打开AndroidManifest.xml文件添加所需的权限:

<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
    <uses-permission android:name="android.permission.CAMERA" />
    ...
</manifest>

5.将以下代码添加到你的AndroidManifest.xml文件中,以配置你的应用,以便在从Play商店安装应用后将ML模型自动下载到设备中:

<application ...>
  ...
  <meta-data
      android:name="com.google.firebase.ml.vision.DEPENDENCIES"
      android:value="barcode" />
</application>

6.打开activity_main.xml并添加TextureView,我们将使用它来传输摄像机输入:

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

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

</androidx.constraintlayout.widget.ConstraintLayout>

7.作为项目设置的最后一步,我们需要检查用户是否已授予摄像头许可。所以,你可以转到MainActivity.kt文件并添加以下代码:

class MainActivity : AppCompatActivity() {

    companion object {
        private const val REQUEST_CAMERA_PERMISSION = 10
    }

    private lateinit var textureView: TextureView

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

        textureView = findViewById(R.id.texture_view)

        // Request camera permissions
        if (isCameraPermissionGranted()) {
            textureView.post { startCamera() }
        } else {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)
        }
    }

    private fun startCamera() {
        // We will implement this in next steps.
    }

    private fun isCameraPermissionGranted(): Boolean {
        val selfPermission = ContextCompat.checkSelfPermission(baseContext, Manifest.permission.CAMERA)
        return selfPermission == PackageManager.PERMISSION_GRANTED
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        if (requestCode == REQUEST_CAMERA_PERMISSION) {
            if (isCameraPermissionGranted()) {
                textureView.post { startCamera() }
            } else {
                Toast.makeText(this, "Camera permission is required.", Toast.LENGTH_SHORT).show()
                finish()
            }
        }
    }
}

三.在屏幕上显示摄像机输入

CameraX有一个称为用例的抽象,使您可以与设备的摄像头进行交互。当前有以下用例:

  • 预览:允许您访问摄像机输入流,可用于在TextureView中显示摄像机流。
  • 图像分析:允许您分析摄像机输入的每一帧。我们将使用此用例执行图像分析,以使用Firebase ML Kit检测帧中的二维码。
  • 图像捕获:捕获和保存照片。
    1.如上所述,要在屏幕上显示摄像机流,我们需要使用“ 预览”用例。当创建Preview用例的实例时,我们需要传递PreviewConfig作为构造函数参数。因此,我们需要在startCamera()函数中添加以下代码:
val previewConfig = PreviewConfig.Builder()
    // We want to show input from back camera of the device
    .setLensFacing(CameraX.LensFacing.BACK)
    .build()

val preview = Preview(previewConfig)

2.预览用例提供了一个SurfaceTexture供显示。为了在我们的中显示摄像机流textureView,我们需要使用setOnPreviewOutputUpdateListener()方法将侦听器添加到预览实例:

// ...
preview.setOnPreviewOutputUpdateListener { previewOutput ->
    textureView.surfaceTexture = previewOutput.surfaceTexture
}

3.当CameraX观察到生命周期来管理摄像机资源时,我们需要使用绑定我们的用例CameraX.bindToLifecycle,所以要将startCamera()函数写入MainActivity中:

private fun startCamera() {
    val previewConfig = PreviewConfig.Builder()
        // We want to show input from back camera of the device
        .setLensFacing(CameraX.LensFacing.BACK)
        .build()

    val preview = Preview(previewConfig)

    preview.setOnPreviewOutputUpdateListener { previewOutput ->
        textureView.surfaceTexture = previewOutput.surfaceTexture
    }

    CameraX.bindToLifecycle(this as LifecycleOwner, preview)
}

四.检测二维码

现在,我们需要使用ImageAnalysis用例从摄像机输入中检测二维码。为此,我们需要创建一个名为Image的类,该类QrCodeAnalyzer实现ImageAnalysis.Analyzer接口。ImageAnalysis.Analyzer具有名为analyze(ImageProxy image, int rotationDegrees)的功能,在这里我们将添加与QR码检测相关的代码。
1.创建QrCodeAnalyzer并添加回调以在检测到二维码时获取通知:

class QrCodeAnalyzer(
    private val onQrCodesDetected: (qrCodes: List<FirebaseVisionBarcode>) -> Unit
) : ImageAnalysis.Analyzer {
    override fun analyze(image: ImageProxy, rotationDegrees: Int) {
        // ...
    }
}

2.获取FirebaseVisionBarcodeDetector的实例:

val options = FirebaseVisionBarcodeDetectorOptions.Builder()
    // We want to only detect QR codes.
    .setBarcodeFormats(FirebaseVisionBarcode.FORMAT_QR_CODE)
    .build()

val detector = FirebaseVision.getInstance().getVisionBarcodeDetector(options)

3.从框架创建FirebaseVisionImage:

val rotation = rotationDegreesToFirebaseRotation(rotationDegrees)
val visionImage = FirebaseVisionImage.fromMediaImage(image.image!!, rotation)

在这一步中,我们还需要通过添加以下函数将ImageAnalysis.Analyzer的旋转度转换为firebase的旋转度:

 private fun rotationDegreesToFirebaseRotation(rotationDegrees: Int): Int {
     return when (rotationDegrees) {
         0 -> FirebaseVisionImageMetadata.ROTATION_0
         90 -> FirebaseVisionImageMetadata.ROTATION_90
         180 -> FirebaseVisionImageMetadata.ROTATION_180
         270 -> FirebaseVisionImageMetadata.ROTATION_270
         else -> throw IllegalArgumentException("Not supported")
     }
 }

(4)传递visionImage到detector并通知onQrCodesDetected与检测到的二维码列表:

detector.detectInImage(visionImage)
    .addOnSuccessListener { barcodes ->
        onQrCodesDetected(barcodes)
    }
    .addOnFailureListener {
        Log.e("QrCodeAnalyzer", "something went wrong", it)

(5)使用MainActivity中startCamera()函数的QrCodeAnalyzer功能:

private fun startCamera() {
    // ...
    val imageAnalysisConfig = ImageAnalysisConfig.Builder()
        .build()
    val imageAnalysis = ImageAnalysis(imageAnalysisConfig)

    val qrCodeAnalyzer = QrCodeAnalyzer { qrCodes ->
        qrCodes.forEach {
            Log.d("MainActivity", "QR Code detected: ${it.rawValue}.")
        }
    }

    imageAnalysis.analyzer = qrCodeAnalyzer

    // We need to bind preview and imageAnalysis use cases
    CameraX.bindToLifecycle(this as LifecycleOwner, preview, imageAnalysis)
}

现在,您可以运行该项目,并且应该可以QR Code detected: …在检测到二维码时在logcat中看到

五.运行效果

在这里插入图片描述

六.常见问题

  • 对于setLaserFrameTopMargin方法,扫描区域偏移的问题issues-13
    解决方法:可以在扫描成功后,调用restartPreviewAfterDelay连续扫描对于加密后的二维码,判断二维码类型可以如下:
//重新包装`Result`,`decryptText`为解密后的内容
        Result decryptResult = new Result(decryptText, rawResult.getRawBytes(),
                rawResult.getNumBits(), rawResult.getResultPoints(), 
                rawResult.getBarcodeFormat(), rawResult.getTimestamp());
	//转换扫描结果为类型枚举
        ParsedResult decryptParsedResult = Scanner.parseResult(decryptResult);
        final ParsedResultType decryptType = decryptParsedResult.getType();
        switch (decryptType) {
            //类型分支
	    case :
	    	break;
        }

七.总结反思

CameraX 是 Google 推出的一个挺不错的 JetPack 组件,入门真的非常简单,使用起来很方便,节省了很多开发时间。而且绑定了LifeCycle ,因此生命周期管理也挺省事。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值