在 Kotlin (Android Studio) 中检测二维码

问题描述 投票:0回答:1

经过几个小时的绝望寻找解决方案后,我来找你,剧透:我还没有找到。 让我解释一下,在我的公司,我负责开发一家通过扫描二维码来简化技术人员工作的公司。我已经开发了相机显示和缩放/取消缩放功能,没有任何问题。棘手的部分是检测二维码。 我什至没有任何错误,只有经典的相机显示。有一次,它偶然检测到了我的二维码,但我再也没有设法让它再次工作。 我确信相机已获得许可,因为我一打开应用程序就授予了它(并且我有相机)。 警报对话框对于调试很有用,但它们根本不出现,就像我尝试使用 Toast 时一样……我认为确实存在检测问题。

我使用了两个库。 第一的 : 实施“com.google.zxing:核心:3.4.1” 当我发现检测效果不佳或根本不起作用时,我尝试了: 实施“com.google.mlkit:条形码扫描:16.1.1” 它并没有工作得更好,所以对我来说,问题来自侦听器或检测而不是库。 有了这个问题,我什至设法达到了 GPT-4 的限制,它告诉我这个 QR 代码 (https://t3.gstatic.com/licensed-image?q=tbn:ANd9GcSh-wrQu254qFaRcoYktJ5QmUhmuUedlbeMaQeaozAVD4lh4ICsGdBNubZ8UlMvWjKC) 是质量不够好/不可见,即使我的相机毫无问题地检测到它。

package com.example.tekanalyzer

import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.util.Size
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.Camera
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.doOnLayout
import androidx.lifecycle.LifecycleOwner
import com.google.mlkit.vision.barcode.Barcode
import com.google.mlkit.vision.barcode.BarcodeScanner
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.common.InputImage
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

class QRCodeScannerActivity : AppCompatActivity(), LifecycleOwner {

    private lateinit var viewFinder: PreviewView
    private lateinit var cameraExecutor: ExecutorService
    private var camera: Camera? = null
    private lateinit var scaleGestureDetector: ScaleGestureDetector
    private lateinit var gestureDetector: GestureDetector

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

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

        viewFinder = findViewById(R.id.viewFinder)
        cameraExecutor = Executors.newSingleThreadExecutor()

        scaleGestureDetector = ScaleGestureDetector(this, ScaleListener())
        gestureDetector = GestureDetector(this, GestureListener())

        viewFinder.doOnLayout {
            if (allPermissionsGranted()) {
                startCamera()
            } else {
                ActivityCompat.requestPermissions(
                    this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS
                )
            }
        }
    }

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

    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<String>, grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)

        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (allPermissionsGranted()) {
                startCamera()
            } else {
                //TO DO
            }
        }
    }

    private fun startCamera() {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

        cameraProviderFuture.addListener(Runnable {
            val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
            val preview = Preview.Builder().build()

            val imageAnalysis = ImageAnalysis.Builder()
                .setTargetResolution(Size(1280, 720))
                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                .build()

            val qrCodeAnalyzer = QRCodeImageAnalyzer(this)

            imageAnalysis.setAnalyzer(cameraExecutor, qrCodeAnalyzer)

            val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

            try {
                cameraProvider.unbindAll()
                camera = cameraProvider.bindToLifecycle(
                    this, cameraSelector, preview, imageAnalysis
                )

                preview.setSurfaceProvider(viewFinder.surfaceProvider)
            } catch (exc: Exception) {
                Log.e("QRCodeScannerActivity", "Erreur lors de la liaison de l'aperçu de la caméra", exc)
            }

        }, ContextCompat.getMainExecutor(this))
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        scaleGestureDetector.onTouchEvent(event)
        gestureDetector.onTouchEvent(event)
        return super.onTouchEvent(event)
    }

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

    private inner class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
        override fun onScale(detector: ScaleGestureDetector): Boolean {
            val zoomRatio = camera?.cameraInfo?.zoomState?.value?.zoomRatio ?: 1f
            val scaleFactor = detector.scaleFactor

            val newZoomRatio = zoomRatio * scaleFactor
            camera?.cameraControl?.setZoomRatio(newZoomRatio)

            return true
        }
    }

    private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
        override fun onDoubleTap(e: MotionEvent): Boolean {
            // Ajoutez ici la logique pour le double-tap
            return super.onDoubleTap(e)
        }
    }
}

class QRCodeImageAnalyzer(private val context: Context) : ImageAnalysis.Analyzer {

    private val scanner: BarcodeScanner = BarcodeScanning.getClient()

    override fun analyze(image: ImageProxy) {
        val rotationDegrees = image.imageInfo.rotationDegrees
        val mediaImage = image.planes
        if (mediaImage.isNotEmpty()) {
            val buffer = mediaImage[0].buffer
            val data = ByteArray(buffer.remaining())
            buffer.get(data)

            val inputImage = InputImage.fromByteArray(
                data,
                image.width,
                image.height,
                rotationDegrees,
                InputImage.IMAGE_FORMAT_NV21
            )

            scanner.process(inputImage)
                .addOnSuccessListener { barcodes ->
                    for (barcode in barcodes) {
                        if (barcode.valueType == Barcode.TYPE_TEXT) {
                            (context as Activity).runOnUiThread {
                                showQRCodeDetectedDialog(barcode.displayValue)
                            }
                        }
                    }
                }
                .addOnFailureListener {
                    (context as Activity).runOnUiThread {
                        showNoQRCodeRecognizedDialog()
                    }
                }
                .addOnCompleteListener {
                    image.close()
                }
        } else {
            (context as Activity).runOnUiThread {
                showAnalyzerCalledDialog()
            }

        }
    }
    private fun showQRCodeDetectedDialog(qrCodeValue: String) {
        val dialogBuilder = AlertDialog.Builder(context)
        dialogBuilder.setMessage("QR Code Detected: $qrCodeValue")
            .setPositiveButton("OK") { dialog, _ ->
                dialog.dismiss()
            }
        val dialog = dialogBuilder.create()
        dialog.show()
    }

    private fun showNoQRCodeRecognizedDialog() {
        val dialogBuilder = AlertDialog.Builder(context)
        dialogBuilder.setMessage("No QR code recognized")
            .setPositiveButton("OK") { dialog, _ ->
                dialog.dismiss()
            }
        val dialog = dialogBuilder.create()
        dialog.show()
    }

    private fun showAnalyzerCalledDialog() {
        val dialogBuilder = AlertDialog.Builder(context)
        dialogBuilder.setMessage("Analyzer called")
            .setPositiveButton("OK") { dialog, _ ->
                dialog.dismiss()
            }
        val dialog = dialogBuilder.create()
        dialog.show()
    }
}

预先感谢您,如果有人有任何线索,那将是一个很大的帮助!

致以诚挚的问候

西风神

android kotlin camera qr-code detection
1个回答
0
投票

看起来您正在使用彩色图像的单色平面进行分析。抱歉没有足够的时间来准确说明,但可能您需要的只是先将图像转换为灰度,或手动组合颜色平面。我不知道你在这里得到的是哪个颜色平面:

val mediaImage = image.planes
        if (mediaImage.isNotEmpty()) {
            val buffer = mediaImage[0].buffer

但很可能您会通过这种方式丢失大部分像素数据,因此扫描结果无法按预期工作。首先请参阅

ImageProxy.getPlanes
文档。希望它会有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.