CameraX如何使ImageAnalysis输出的图像大小与Preview相同。

7
我希望能够在分析步骤中获得与预览相同的精确图像。 我有一个预览用例:
val metrics = DisplayMetrics().also { binding.codeScannerView.display.getRealMetrics(it) }
    val screenAspectRatio = Rational(metrics.widthPixels, metrics.heightPixels)
    val previewConfig = PreviewConfig.Builder().apply {
        setTargetAspectRatio(screenAspectRatio)
    }.build()

旁边是我分析用例配置的内容:

val analyzerConfig = ImageAnalysisConfig.Builder().apply {
        setTargetResolution(Size(metrics.heightPixels, metrics.widthPixels))
        setTargetAspectRatio(screenAspectRatio)
        val analyzerThread = HandlerThread(
                "QrCodeReader").apply { start() }
        setCallbackHandler(Handler(analyzerThread.looper))
        setImageReaderMode(
                ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE)
    }.build()

我的预览是全屏的,所以它的尺寸是1440x2560。但是如果我尝试从分析器中的ImageProxy获取尺寸,我得到的是1920x1050,这似乎有错误的尺寸,并且宽度和高度被交换了。为什么会这样,我该如何强制分析步骤具有与全屏相同的尺寸?


有没有解决这个问题的机会? - pascal sautot
当时我在这里描述了Camerax的许多其他问题:https://dev59.com/6LXna4cB1Zd3GeqPO62Y,所以我不再使用它。 - SMGhost
请检查此答案:https://dev59.com/pb3pa4cB1Zd3GeqPakBm#68208561 - Bruce
2个回答

5

简介:

implementation 'androidx.camera:camera-core:1.0.0-alpha10'
implementation 'androidx.camera:camera-camera2:1.0.0-alpha10'
implementation "androidx.camera:camera-lifecycle:1.0.0-alpha10"
implementation "androidx.camera:camera-view:1.0.0-alpha07"
implementation 'com.google.firebase:firebase-ml-vision:24.0.1'
implementation 'com.google.firebase:firebase-ml-vision-barcode-model:16.0.2'

我用以下方法解决了这个问题:FirebaseVisionImage.fromBitmap(bitmap),其中bitmap是根据预览配置手动裁剪和旋转的图像。步骤如下:
  1. when you setup ImageAnalysis.Builder() && Preview.Builder() obtain the on-screen rendered size for androidx.camera.view.PreviewView element:

    previewView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
    val previewSize = Size(previewView.width, previewView.height)
    

    Then pass the size into your own ImageAnalysis.Analyzer implementation (let's say it will be viewFinderSize variable, usage see below)

  2. when override fun analyze(mediaImage: ImageProxy){ occurs, do the manual crop of received ImageProxy. I am use the snippet from another SO question about distorted YUV_420_888 image: https://dev59.com/PVcP5IYBdhLWcg3wqL3A#45926852

在进行裁剪时,请记住,ImageAnalysis使用场景接收以垂直中心为基础对齐的图像,这些图像在您的预览使用场景中。换句话说,在旋转后,接收的图像将在垂直方向上居中,就好像它将在您的预览区域内(即使您的预览区域小于传递到分析中的图像)。因此,裁剪区域应该从垂直中心在两个方向上计算:向上和向下。

剪裁的垂直尺寸(高度)应该根据水平尺寸手动计算。这意味着,在将图像传入分析时,它具有预览区域内的全水平尺寸(预览中的100%宽度等于分析中的100%宽度)。因此,在水平维度上没有隐藏区域。这为垂直裁剪计算打开了大门。我用下面的代码完成了这个过程:

var bitmap = ... <- obtain the bitmap as suggested in the SO link above
val matrix = Matrix()
matrix.postRotate(90f)
bitmap = Bitmap.createBitmap(bitmap, 0, 0, image.width, image.height, matrix, true)
val cropHeight = if (bitmap.width < viewFinderSize!!.width) {
    // if preview area larger than analysing image
    val koeff = bitmap.width.toFloat() / viewFinderSize!!.width.toFloat()
    viewFinderSize!!.height.toFloat() * koeff
} else {
    // if preview area smaller than analysing image
    val prc = 100 - (viewFinderSize!!.width.toFloat()/(bitmap.width.toFloat()/100f))
    viewFinderSize!!.height + ((viewFinderSize!!.height.toFloat()/100f) * prc)
}

val cropTop = (bitmap.height/2)-((cropHeight)/2)
bitmap = Bitmap.createBitmap(bitmap, 0, cropTop.toInt(), bitmap.width, cropHeight.toInt())
bitmap变量中的最终值是裁剪后的图像,可直接传递给FirebaseVisionImage.fromBitmap(bitmap)方法。
PS. 欢迎改进建议的变体。

0
也许我来得晚了,但是这段代码对我有效。有时候,如果cropTop的值是负数,那么当出现负值时,你应该处理来自ImageProxy的图像;在其他情况下,你可以处理裁剪后的位图。

        val mediaImage = imageProxy.image ?: return
        var bitmap = ImageUtils.convertYuv420888ImageToBitmap(mediaImage)

        val rotationDegrees = imageProxy.imageInfo.rotationDegrees

        val matrix = Matrix()
        matrix.postRotate(rotationDegrees.toFloat())

        bitmap =
            Bitmap.createBitmap(bitmap, 0, 0, mediaImage.width, mediaImage.height, matrix, true)
        val cropHeight = if (bitmap.width < previewView.width) {
            // if preview area larger than analysing image
            val koeff = bitmap.width.toFloat() / previewView.width.toFloat()
            previewView.height.toFloat() * koeff
        } else {
            // if preview area smaller than analysing image
            val prc = 100 - (previewView.width.toFloat() / (bitmap.width.toFloat() / 100f))
            previewView.height + ((previewView.height.toFloat() / 100f) * prc)
        }

        val cropTop = (bitmap.height / 2) - ((cropHeight) / 2)

        if (cropTop > 0) {
            Bitmap.createBitmap(bitmap, 0, cropTop.toInt(), bitmap.width, cropHeight.toInt())
                .also { process(it, imageProxy) }
        } else {
            imageProxy.image?.let { process(it, imageProxy) }
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接