首页 > 解决方案 > CameraX 和条码扫描仪 - 条码扫描仪无法读取

问题描述

我目前正在尝试使用 CameraX API 和 ML Kit 进行条码扫描。当我将相机对准二维码时,我没有从 PreviewView 收到任何数据。让我与您分享我的代码,我想我已经很接近了,但遗憾的是无法弄清楚。:/ 我正在使用预览和图像分析案例

带有 ImageAnalysis.Analyser 的 MainActivity

class MainActivity : AppCompatActivity() {

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

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

        outputDirectory = getOutputDirectory()
        cameraExecutor = Executors.newSingleThreadExecutor()

        // Request camera permissions
        if (allPermissionsGranted()) {
            startCamera()
        } else {
            ActivityCompat.requestPermissions(
                this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS
            )
        }

    }

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

    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(
            baseContext, it
        ) == PackageManager.PERMISSION_GRANTED
    }

    private fun startCamera() {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        cameraProviderFuture.addListener({
            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

            // Preview
            val preview = Preview.Builder()
                .build()
                .also {
                    it.setSurfaceProvider(previewView.surfaceProvider)
                }

            // Image Analyzer
            val imageAnalyzer = ImageAnalysis.Builder()
                .build()
                .also {
                    it.setAnalyzer(cameraExecutor, MyImageAnalyser())
                }

            val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

            try {
                cameraProvider.unbindAll()
                cameraProvider.bindToLifecycle(
                    this, cameraSelector, preview, imageAnalyzer
                )

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

        }, ContextCompat.getMainExecutor(this))
    }


    inner class MyImageAnalyser : ImageAnalysis.Analyzer {

        @SuppressLint("UnsafeExperimentalUsageError")
        override fun analyze(imageProxy: ImageProxy) {
            val mediaImage = imageProxy.image
            if (mediaImage != null) {
                val image =
                    InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
                initializeBarcodeScanner(image)
            }
        }

        private fun initializeBarcodeScanner(inputImage: InputImage) {
            val options = BarcodeScannerOptions.Builder()
                .setBarcodeFormats(
                    Barcode.QR_CODE,
                    Barcode.WIFI,
                    Barcode.URL
                )
                .build()

            val scanner = BarcodeScanning.getClient(options)

            scanner.process(inputImage)
                .addOnSuccessListener { barcodes ->
                    for (barcode in barcodes) {
                        val bounds = barcode.boundingBox
                        val corners = barcode.cornerPoints
                        val rawValue = barcode.rawValue

                        Log.d("BARCODE", barcode.valueType.toString())
                        when (barcode.valueType) {
                            Barcode.WIFI -> {
                                val ssid = barcode.wifi!!.ssid
                                val password = barcode.wifi!!.password
                                val type = barcode.wifi!!.encryptionType

                                Log.d("BARCODE", ssid.toString())
                                Log.d("BARCODE", password.toString())
                                Log.d("BARCODE", type.toString())
                            }
                            Barcode.URL -> {
                                val title = barcode.url!!.title
                                val url = barcode.url!!.url

                                Log.d("BARCODE", title.toString())
                                Log.d("BARCODE", url.toString())
                            }
                            Barcode.QR_CODE -> {
                                Log.d("BARCODE", barcode.rawValue.toString())
                            }
                            else -> {
                                Log.d("BARCODE", "Else")
                            }
                        }
                    }
                }
                .addOnFailureListener {
                    Log.d("BARCODE", it.message.toString())
                }
        }

    }

    override fun onDestroy() {
        super.onDestroy()
        cameraExecutor.shutdown()
    }

}

常量.kt

class Constants {

    companion object {
        const val TAG = "CameraXBasic"
        const val REQUEST_CODE_PERMISSIONS = 10
        val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
    }

}

activity_main.xml

<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">


    <androidx.camera.view.PreviewView
        android:id="@+id/previewView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/textView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

    </androidx.camera.view.PreviewView>

    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="200dp"
        android:layout_marginStart="20dp"
        android:layout_marginEnd="20dp"
        android:text="TextView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

依赖项

 // Use this dependency to bundle the model with your app
    implementation 'com.google.mlkit:barcode-scanning:16.0.3'


    def camerax_version = "1.0.0-beta10"
    //  CameraX core library using camera2 implementation
    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-alpha17"

标签: xmlkotlinandroid-cameraxgoogle-mlkit

解决方案


分析仪接收到的每张图像都必须先关闭,然后才能接收新图像。文档说:

完成后关闭图像是应用程序的责任。如果图像未关闭,则可能会阻止生成更多图像

关闭图像是通过 实现的ImageProxy.close()。由于您使用的是 MLKit,因此您可以添加一个任务完成侦听器,并关闭其中的图像。当任务(图像处理)成功完成或出现错误时,将调用侦听器。

Task.addOnSuccessListener { barcodes -> ... }
    .addOnFailureListener { exception -> }
    .addOnCompleteListener { imageProxy.close() }

推荐阅读