You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何按物体实际尺寸绘制矩形?修正MLKit条码扫描识别框错位问题

问题原因

MLKit返回的boundingBox是基于相机输入的原始帧像素坐标,和你绘制用的画布尺寸、旋转角度、预览缩放规则不匹配,所以会出现大小、位置偏差,完全可以通过对齐坐标体系实现矩形的正确缩放定位。

修复方案

1. 优化初始化逻辑

首先把条码扫描器提取为类成员变量,不要每次分析图像都重复创建,既影响性能也可能增加坐标偏差风险:

// 类成员变量
private val scanner = BarcodeScanning.getClient()

2. 坐标对齐转换

你需要先拿到三个核心参数,再做坐标转换:

  • 相机原始帧的尺寸,要结合旋转角度调整宽高
  • 绘制控件(如SurfaceView、PreviewView)的实际显示尺寸
  • 预览所用的缩放规则(填充/自适应)

完整修正代码示例

override fun analyze(image: ImageProxy) {
    val img = image.image ?: return
    val rotation = image.imageInfo.rotationDegrees
    // 调整原始帧的宽高适配旋转角度
    val inputWidth = if (rotation == 90 || rotation == 270) image.height else image.width
    val inputHeight = if (rotation == 90 || rotation == 270) image.width else image.height
    // 此处获取绘制画布的实际宽高
    val viewWidth = mHolder.surfaceFrame.width()
    val viewHeight = mHolder.surfaceFrame.height()

    // 计算缩放比例,和预览缩放规则保持一致,此处用自适应(fit)模式举例
    val scaleX = viewWidth.toFloat() / inputWidth
    val scaleY = viewHeight.toFloat() / inputHeight
    val scale = min(scaleX, scaleY)
    // 自适应模式下的偏移量,修正黑边带来的位置偏差
    val offsetX = (viewWidth - inputWidth * scale) / 2
    val offsetY = (viewHeight - inputHeight * scale) / 2

    val inputImage = InputImage.fromMediaImage(img, rotation)
    scanner.process(inputImage)
        .addOnSuccessListener { barcodes ->
            val canvas = mHolder.lockCanvas() ?: return@addOnSuccessListener
            for (barcode in barcodes) {
                barcode.boundingBox?.let { rawRect ->
                    // 先缩放原始矩形
                    val scaledRect = RectF(
                        rawRect.left * scale,
                        rawRect.top * scale,
                        rawRect.right * scale,
                        rawRect.bottom * scale
                    )
                    // 根据旋转角度调整坐标
                    val adjustedRect = when (rotation) {
                        90 -> RectF(
                            scaledRect.top,
                            inputWidth * scale - scaledRect.right,
                            scaledRect.bottom,
                            inputWidth * scale - scaledRect.left
                        )
                        270 -> RectF(
                            inputHeight * scale - scaledRect.bottom,
                            scaledRect.left,
                            inputHeight * scale - scaledRect.top,
                            scaledRect.right
                        )
                        180 -> RectF(
                            inputWidth * scale - scaledRect.right,
                            inputHeight * scale - scaledRect.bottom,
                            inputWidth * scale - scaledRect.left,
                            inputHeight * scale - scaledRect.top
                        )
                        else -> scaledRect
                    }
                    // 加上偏移量适配黑边
                    adjustedRect.offset(offsetX, offsetY)
                    // 前置摄像头场景额外加水平镜像
                    // adjustedRect.offset(viewWidth - adjustedRect.right - adjustedRect.left, 0f)
                    // 绘制最终调整后的矩形
                    canvas.drawRect(adjustedRect, mPaint)
                }
            }
            mHolder.unlockCanvasAndPost(canvas)
        }
        .addOnCompleteListener {
            // 一定要关闭ImageProxy,否则后续帧不会回调
            image.close()
        }
}

3. 额外优化建议

  • 如果使用CameraX的PreviewView,可以直接调用PreviewView.getMeteringPointFactory()来转换坐标,不需要手动计算,准确性更高
  • 预览如果用的是填充模式,把缩放比例的计算改为val scale = max(scaleX, scaleY)即可适配

内容的提问来源于stack exchange,提问作者ladytoky0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 23:18:01