CameraX图像分析的imageproxy大小和PreviewView大小不一样

我正在尝试使用 Firebase 的 MLKit 与 Camerax 进行人脸检测。我很难让图像分析的 imageproxy 大小与 PreviewView 的大小相匹配。对于图像分析和 PreviewView,我将 setTargetResolution() 设置为 PreviewView 的宽度和高度。但是,当我在分析器中检查 Imageproxy 的大小时,它给我的宽度是 1920,高度是 1080。我的 PreviewView 宽度为 1080,高度为 2042。当我在 setTargetResolution() 中交换宽度和高度以进行图像分析时,图像代理中的宽度和高度都为 1088。我的预览视图也锁定为纵向模式。

最终,我需要将原始图像代理数据和面部点数据输入到 AR 代码中。因此,仅放大绘制面部点的图形叠加层对我不起作用。

问: 如果无法在 camerax 库中解决此问题,如何缩放从分析器返回的 imageproxy 以匹配预览视图?

我正在使用 Java 和最新的 Camerax 库:

def camerax_version = "1.0.0-beta08"

stack overflow CameraX Image analysis's imageproxy size and PreviewView size are not the same
原文答案

答案:

作者头像

这个答案来自 @侯赛因的答案。我添加了相关的示例代码部分。

出于各种原因(示例设备特定的显示大小/硬件/相机或应用程序特定视图和处理),用于预览和分析的Camerax图像大小各不相同)但是,有一些选项可以映射处理图像大小和结果XY坐标以预览大小和预览XY坐标。

带有DimensionRatio 3:4的设置布局,用于预览和分析覆盖,在布局中,

例子:

<androidx.camera.view.PreviewView
  android:id="@+id/view_finder"
  android:layout_width="match_parent"
  android:layout_height="0dp"
  app:layout_constraintBottom_toBottomOf="parent"
  app:layout_constraintDimensionRatio="3:4"
  app:layout_constraintTop_toTopOf="parent"/>

<com.loa.sepanex.scanner.view.GraphicOverlay
  android:id="@+id/graphic_overlay"
  android:layout_width="match_parent"
  android:layout_height="0dp"
  app:layout_constraintBottom_toBottomOf="parent"
  app:layout_constraintDimensionRatio="3:4"
  app:layout_constraintTop_toTopOf="parent"/>

设置预览和分析用例

例子:

viewFinder = view.findViewById(R.id.view_finder)
graphicOverlay = view.findViewById(R.id.graphic_overlay)
//...
preview = Preview.Builder()
          .setTargetAspectRatio(AspectRatio.RATIO_4_3)
          .setTargetRotation(rotation)
          .build()

imageAnalyzer = ImageAnalysis.Builder()
                .setTargetAspectRatio(AspectRatio.RATIO_4_3)
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                .setTargetRotation(rotation)
                .build()
                .also {
                    it.setAnalyzer(cameraExecutor, ImageAnalysis.Analyzer { 
                     image ->
                        //val rotationDegrees = image.imageInfo.rotationDegrees
                        try {
                            val mediaImage: Image? = image.image
                            if (mediaImage != null) {
                                val imageForFaceDetectionProcess = InputImage.fromMediaImage(mediaImage, image.getImageInfo().getRotationDegrees())
                                //...
                            }
                         }
                      }
                  }

定义比例尺和Traslate API,以获取分析图像XY坐标的映射预览XY坐标,如下所示

            val preview = viewFinder.getChildAt(0)
            var previewWidth = preview.width * preview.scaleX
            var previewHeight = preview.height * preview.scaleY
            val rotation = preview.display.rotation
            if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
                val temp = previewWidth
                previewWidth = previewHeight
                previewHeight = temp
            }
            val isImageFlipped = lensFacing == CameraSelector.LENS_FACING_FRONT
            val rotationDegrees: Int = imageProxy.getImageInfo().getRotationDegrees()
            if (rotationDegrees == 0 || rotationDegrees == 180) {
                graphicOverlay!!.setImageSourceInfo(
                        imageProxy.getWidth(), imageProxy.getHeight(), isImageFlipped)
            } else {
                graphicOverlay!!.setImageSourceInfo(
                        imageProxy.getHeight(), imageProxy.getWidth(), isImageFlipped)
            }
    :::
    :::
    float viewAspectRatio = (float) previewWidth / previewHeight;
    float imageAspectRatio = (float) imageWidth / imageHeight;
    postScaleWidthOffset = 0;
    postScaleHeightOffset = 0;

    if (viewAspectRatio > imageAspectRatio) {
        // The image needs to be vertically cropped to be displayed in this view.
        scaleFactor = (float) previewWidth / imageWidth;
        postScaleHeightOffset = ((float) previewWidth / imageAspectRatio - previewHeight) / 2;
    } else {
        // The image needs to be horizontally cropped to be displayed in this view.
        scaleFactor = (float) previewHeight / imageHeight;
        postScaleWidthOffset = ((float) previewHeight * imageAspectRatio - previewWidth) / 2;
    }
    transformationMatrix.reset();
    transformationMatrix.setScale(scaleFactor, scaleFactor);
    transformationMatrix.postTranslate(-postScaleWidthOffset, -postScaleHeightOffset);
    if (isImageFlipped) {
        transformationMatrix.postScale(-1f, 1f, previewWidth / 2f, previewHeight / 2f);
    }
    :::
    :::
    public float scale(float imagePixel) {
        return imagePixel * overlay.scaleFactor;
    }
    public float translateX(float x) {
        if (overlay.isImageFlipped) {
            return overlay.getWidth() - (scale(x) - overlay.postScaleWidthOffset);
        } else {
            return scale(x) - overlay.postScaleWidthOffset;
        }
    }
    public float translateY(float y) {
        return scale(y) - overlay.postScaleHeightOffset;
    }

使用Translatex和Translatey方法将基于分析图像的数据绘制到预览中

例子:

        for (FaceContour contour : face.getAllContours()) {
            for (PointF point : contour.getPoints()) {
                canvas.drawCircle(translateX(point.x), translateY(point.y), FACE_POSITION_RADIUS, facePositionPaint);
            }
        }