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

基于Kalman Filter的安卓实时相机尺度估算及花园面积计算咨询

方案与代码示例

一、安卓相机预览物体尺度归一化(保持显示尺寸一致)

核心原理

相机成像遵循透视投影规律:物体的像素尺寸与相机到物体的距离成反比。要固定显示尺寸,需通过相机内参+物体真实尺寸/距离信息计算实时缩放因子,对预览帧或物体检测结果进行缩放。

实现步骤

  • 步骤1:获取相机内参
    用CameraX(安卓推荐方案)获取焦距、传感器物理尺寸,转换为像素单位的焦距:
    val cameraCharacteristics = cameraInfo.cameraCharacteristics
    // 物理焦距(mm)
    val focalLengthMm = cameraCharacteristics.get(CameraCharacteristics.LENS_INFO_FOCAL_LENGTH)!!
    // 传感器物理尺寸(mm)
    val sensorSize = cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)!!
    // 预览帧宽度(像素)
    val previewWidth = previewView.width
    // 像素焦距 = 物理焦距 * 预览宽度 / 传感器宽度
    val focalLengthPixel = focalLengthMm * previewWidth / sensorSize.width
    
  • 步骤2:检测物体并计算缩放因子
    假设已知目标物体的真实宽度(比如花园花盆真实宽度为20cm),通过ML Kit/OpenCV检测出物体的像素宽度detectedPixelWidth,计算相机到物体的距离,再推导缩放因子:
    val realObjectWidthCm = 20.0
    // 距离(cm)= (真实宽度 * 像素焦距) / 检测到的像素宽度
    val distanceCm = (realObjectWidthCm * focalLengthPixel) / detectedPixelWidth
    
    // 目标固定显示像素宽度(比如100px)
    val targetPixelWidth = 100.0
    // 实时缩放因子:让物体显示为目标宽度
    val scaleFactor = targetPixelWidth / detectedPixelWidth
    
  • 步骤3:应用缩放
    在预览帧的绘制回调中,对物体边界框或帧本身应用缩放:
    previewView.setOnDrawListener { canvas ->
        // 绘制检测到的物体框,应用缩放因子
        val scaledRect = RectF(
            detectedRect.left * scaleFactor,
            detectedRect.top * scaleFactor,
            detectedRect.right * scaleFactor,
            detectedRect.bottom * scaleFactor
        )
        canvas.drawRect(scaledRect, Paint().apply { 
            color = Color.RED 
            style = Paint.Style.STROKE 
            strokeWidth = 3f 
        })
    }
    

补充说明

如果没有已知真实尺寸的物体,可通过场景参考物(比如标准尺寸的地面瓷砖)校准;或使用双目/TOF传感器直接获取距离。


二、卡尔曼滤波实现花园面积平滑计算

核心思路

花园面积计算易受边缘检测噪声、相机抖动影响,卡尔曼滤波通过预测-更新循环,融合历史状态与当前观测值,输出平滑的面积估计值。

卡尔曼滤波实现(Kotlin)

class KalmanFilter {
    // 状态向量:当前估计面积
    private var x: Double = 0.0
    // 状态协方差矩阵(衡量估计不确定性)
    private var p: Double = 1.0
    // 过程噪声协方差(面积变化的不确定性)
    private val q: Double = 0.01
    // 观测噪声协方差(检测误差的不确定性)
    private val r: Double = 0.1
    // 状态转移矩阵(假设面积变化缓慢)
    private val a: Double = 1.0
    // 观测矩阵(观测值直接对应状态)
    private val h: Double = 1.0

    fun update(measurement: Double): Double {
        // 预测步骤:根据历史状态预测当前状态
        val xPred = a * x
        val pPred = a * p * a + q

        // 更新步骤:用观测值修正预测结果
        val k = pPred * h / (h * pPred * h + r)
        x = xPred + k * (measurement - h * xPred)
        p = (1 - k * h) * pPred

        return x
    }
}

结合面积计算的流程

  1. 提取花园轮廓并计算像素面积:用OpenCV处理预览帧:
    fun calculateGardenPixelArea(frame: Mat): Double {
        val gray = Mat()
        Imgproc.cvtColor(frame, gray, Imgproc.COLOR_BGR2GRAY)
        val blurred = Mat()
        Imgproc.GaussianBlur(gray, blurred, Size(5.0,5.0), 0.0)
        val edges = Mat()
        Imgproc.Canny(blurred, edges, 50.0, 150.0)
        val contours = mutableListOf<MatOfPoint>()
        val hierarchy = Mat()
        Imgproc.findContours(edges, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE)
        // 取最大轮廓作为花园区域
        val maxContour = contours.maxByOrNull { Imgproc.contourArea(it) } ?: return 0.0
        return Imgproc.contourArea(maxContour)
    }
    
  2. 像素面积转真实面积:结合相机内参和距离转换为平方米:
    fun pixelToRealArea(pixelArea: Double, distanceCm: Double, focalLengthPixel: Double, sensorWidthMm: Double): Double {
        val previewWidth = previewView.width
        // 计算每像素对应的真实长度(米)
        val pixelPerMm = previewWidth / sensorWidthMm
        val realMeterPerPixel = (distanceCm / (focalLengthPixel / pixelPerMm)) / 100
        // 真实面积 = 像素面积 * 单像素真实长度^2
        return pixelArea * realMeterPerPixel * realMeterPerPixel
    }
    
  3. 应用卡尔曼滤波平滑结果:
    val kalmanFilter = KalmanFilter()
    // 每一帧处理逻辑
    fun processFrame(frame: Mat) {
        val pixelArea = calculateGardenPixelArea(frame)
        val realArea = pixelToRealArea(pixelArea, distanceCm, focalLengthPixel, sensorSize.width)
        val smoothedArea = kalmanFilter.update(realArea)
        // 显示平滑后的面积
        textView.text = "花园面积:${String.format("%.2f", smoothedArea)} ㎡"
    }
    

调优建议

  • 根据实际噪声情况调整q和r:噪声大时增大r,面积变化慢时减小q;
  • 若相机移动导致面积变化有规律,可扩展状态向量为[面积, 面积变化率],提升预测精度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 13:18:20