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

如何用OpenCV实现相机距离变化时的物体尺寸精准测量?

问题描述

我正在编写基于Python的代码,需求是无论相机与物体的距离如何变化,都能精准测量物体的高度和宽度——即若物体实际尺寸为5cm×5cm,无论相机靠近或远离,都应显示该真实尺寸。

我尝试了如下代码,查阅资料得知需要用到相关公式,但无法理解其原理。运行后发现距离变化时,测量出的高度和宽度也随之改变,希望得到技术帮助或改进建议。

原尝试代码

import cv2
import numpy as np

def get_object_dimensions(contour, reference_width, reference_distance):
    # Calculate the bounding box around the contour
    x, y, w, h = cv2.boundingRect(contour)

    # Assuming the camera calibration is done, convert pixel dimensions to centimeters
    pixel_width = w
    pixel_height = h
    # You may need to calibrate these conversion factors based on your camera and setup
    width_cm = pixel_width * pixel_to_cm_width
    height_cm = pixel_height * pixel_to_cm_height

    # Calculate the current distance based on the reference width and current measured width
    current_distance = (reference_width * reference_distance) / width_cm

    return width_cm, height_cm, current_distance

# Camera setup
cap = cv2.VideoCapture(0)  # Use 0 for the default camera

# Calibration factors for converting pixel dimensions to centimeters
pixel_to_cm_width = 0.1  # Adjust based on your calibration
pixel_to_cm_height = 0.1  # Adjust based on your setup

# Initial reference distance (distance at which the width measurement is accurate)
initial_reference_distance = 50.0  # Adjust based on your setup
reference_width = 10.0  # Adjust based on your setup

while True:
    # Capture a frame
    ret, frame = cap.read()

    # Convert the frame to grayscale
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # Apply Gaussian blur to reduce noise and improve edge detection
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)

    # Perform edge detection using Canny
    edges = cv2.Canny(blurred, 50, 150)

    # Find contours in the edged image
    contours, _ = cv2.findContours(edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # Iterate over detected contours
    for contour in contours:
        # Ignore small contours
        if cv2.contourArea(contour) > 1000:
            # Draw a bounding box around the contour
            x, y, w, h = cv2.boundingRect(contour)
            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

            # Get object dimensions and current distance in centimeters
            width_cm, height_cm, current_distance = get_object_dimensions(contour, reference_width, initial_reference_distance)

            # Display the dimensions and current distance
            cv2.putText(frame, f'Width: {width_cm:.2f} cm', (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
            cv2.putText(frame, f'Height: {height_cm:.2f} cm', (x, y + h + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
            cv2.putText(frame, f'Distance: {current_distance:.2f} cm', (x, y + h + 50), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

    # Display the frame
    cv2.imshow('Object Detection and Measurement', frame)

    # Break the loop when 'q' is pressed
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# Release the camera and close all windows
cap.release()
cv2.destroyAllWindows() 

原代码问题分析

你的核心问题在于固定的像素转厘米系数(pixel_to_cm_width/pixel_to_cm_height)仅在特定距离下有效。当相机与物体距离变化时,物体在画面中的像素尺寸会线性缩放,固定系数无法适配这种变化,导致测量结果随距离波动。

核心原理:相似三角形成像法则

相机成像遵循相似三角形原理,这是实现距离无关尺寸测量的基础:

  • 设物体真实宽度为W,到相机的距离为D
  • 物体在相机传感器上的像素宽度为P,相机焦距为f(像素单位)
  • 三者满足关系:W/D = P/f
  • 推导可得:
    • 真实尺寸:W = (P * D) / f
    • 物体距离:D = (W * f) / P

要实现稳定的真实尺寸测量,必须先通过标定获取相机焦距,再结合实时检测的像素尺寸和距离(或参考物体)计算真实尺寸。

改进方案

以下提供两种可直接落地的改进思路,优先推荐第一种(无需复杂的相机内参标定):

方案1:基于参考物体的实时校准

在画面中放置一个已知真实尺寸的参考物体(如10cm宽的标准卡片),先通过它标定相机焦距,再用该焦距计算目标物体的真实尺寸,步骤如下:

改进后代码

import cv2
import numpy as np

def calculate_real_dimensions(pixel_size, current_distance, focal_length):
    # 相似三角形公式计算真实尺寸
    return (pixel_size * current_distance) / focal_length

def calibrate_focal_length(reference_pixel_width, reference_real_width, reference_distance):
    # 通过参考物体标定相机焦距(像素单位)
    return (reference_pixel_width * reference_distance) / reference_real_width

# 相机初始化
cap = cv2.VideoCapture(0)

# 标定参数(提前精确测量)
REFERENCE_REAL_WIDTH = 10.0  # 参考物体真实宽度(单位:cm)
REFERENCE_DISTANCE = 50.0     # 标定时参考物体到相机的距离(单位:cm)
calibrated_focal_length = None  # 存储标定后的焦距

while True:
    ret, frame = cap.read()
    if not ret:
        break

    # 预处理流程
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    edges = cv2.Canny(blurred, 50, 150)
    contours, _ = cv2.findContours(edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    for contour in contours:
        # 过滤小轮廓,避免误检测
        if cv2.contourArea(contour) > 1000:
            x, y, w, h = cv2.boundingRect(contour)
            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)

            # 第一步:完成焦距标定(仅执行一次,可通过形状/颜色区分参考物体)
            if calibrated_focal_length is None:
                calibrated_focal_length = calibrate_focal_length(w, REFERENCE_REAL_WIDTH, REFERENCE_DISTANCE)
                cv2.putText(frame, "Calibration Done!", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
            else:
                # 计算当前物体到相机的距离
                current_distance = (REFERENCE_REAL_WIDTH * calibrated_focal_length) / w
                # 计算物体真实宽高
                real_width = calculate_real_dimensions(w, current_distance, calibrated_focal_length)
                real_height = calculate_real_dimensions(h, current_distance, calibrated_focal_length)

                # 显示测量结果
                cv2.putText(frame, f'Width: {real_width:.2f} cm', (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
                cv2.putText(frame, f'Height: {real_height:.2f} cm', (x, y + h + 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
                cv2.putText(frame, f'Distance: {current_distance:.2f} cm', (x, y + h + 50), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

    cv2.imshow('Stable Object Measurement', frame)
    # 按q退出
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 释放资源
cap.release()
cv2.destroyAllWindows()

方案2:相机内参标定(高精度场景)

如果需要更高测量精度,可通过OpenCV的棋盘格标定工具获取相机内参矩阵(包含精确焦距),步骤如下:

  1. 打印标准棋盘格标定板(如8×6角点)
  2. 拍摄10-20张不同角度、距离的棋盘格图片
  3. 使用cv2.findChessboardCorners()和cv2.calibrateCamera()计算内参矩阵,提取焦距值
  4. 结合实时检测的像素尺寸和物体距离(可通过TOF相机或其他测距方式获取)计算真实尺寸
关键注意事项
  • 标定精度:参考物体的尺寸、标定时的距离必须精确测量,否则会直接影响后续所有测量结果
  • 姿态校正:以上方法仅适用于物体与相机镜头平行的场景,若物体倾斜,需加入透视变换进行姿态校正
  • 轮廓优化:可通过颜色过滤、形状匹配(如矩形检测)等方式精准定位目标物体,减少误检测干扰

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 19:18:10