如何按物体实际尺寸绘制矩形?修正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
相关产品推荐
相关产品推荐

