如何用Python+OpenCV测量视频中不同平面两物体的欧氏距离
跨平面物体欧氏距离测量实现(Python+OpenCV)
核心思路
跨平面测量的关键是获取两个物体的真实3D世界坐标,再计算欧氏距离。针对单目/双目场景,分别给出可行方案:
1. 单目相机方案(低成本,依赖参考尺寸)
如果只有单目相机,必须利用已知真实尺寸的物体(比如乙炔/氧气钢瓶的标准直径)作为参考,结合相机内参完成像素到真实3D坐标的转换。
步骤1:相机标定(获取内参)
先通过棋盘格完成相机标定,得到内参矩阵K和畸变系数dist,这是所有3D计算的基础。
示例代码:
import cv2 import numpy as np # 棋盘格参数(根据实际棋盘格调整) chessboard_size = (9,6) objp = np.zeros((chessboard_size[0]*chessboard_size[1],3), np.float32) objp[:,:2] = np.mgrid[0:chessboard_size[0],0:chessboard_size[1]].T.reshape(-1,2) objpoints = [] # 世界空间点集合 imgpoints = [] # 图像空间点集合 # 读取标定图片(替换为你的标定图路径) images = [cv2.imread(f'calib_img_{i}.jpg') for i in range(10)] for img in images: gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None) if ret: objpoints.append(objp) imgpoints.append(corners) # 执行标定 ret, K, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
步骤2:基于参考物体计算3D坐标
假设已知钢瓶的真实直径为real_diameter(比如0.2米),先检测钢瓶的像素直径pixel_diameter(通过目标识别的外接矩形宽度/高度),推导3D坐标后计算距离:
def get_3d_coords(center_pixel, pixel_diameter, real_diameter, K): # 计算深度Z:利用相似三角形原理 Z = (real_diameter * K[0][0]) / pixel_diameter # 像素坐标转归一化相机坐标 u, v = center_pixel cx, cy = K[0][2], K[1][2] fx, fy = K[0][0], K[1][1] X = (u - cx) * Z / fx Y = (v - cy) * Z / fy return np.array([X, Y, Z]) # 假设已通过目标识别得到以下参数 cylinder1_center = (250, 300) cylinder1_pixel_d = 80 cylinder2_center = (450, 350) cylinder2_pixel_d = 60 real_d = 0.2 # 钢瓶真实直径,单位米 # 获取两个钢瓶的3D坐标 coord1 = get_3d_coords(cylinder1_center, cylinder1_pixel_d, real_d, K) coord2 = get_3d_coords(cylinder2_center, cylinder2_pixel_d, real_d, K) # 计算欧氏距离 distance = np.linalg.norm(coord1 - coord2) print(f"两钢瓶欧氏距离:{distance:.2f}米")
2. 双目相机方案(高精度,无需参考物)
如果能部署双目相机,可通过视差直接计算深度,无需依赖参考物体,测量精度更高。
步骤1:双目标定与立体校正
先完成双目相机的标定,获取左右相机的内参、外参及立体校正映射:
# 假设已完成单目标定,得到左右相机内参K1/K2、畸变dist1/dist2,以及旋转矩阵R、平移矩阵T R1, R2, P1, P2, Q, _, _ = cv2.stereoRectify(K1, dist1, K2, dist2, gray.shape[::-1], R, T) map1x, map1y = cv2.initUndistortRectifyMap(K1, dist1, R1, P1, gray.shape[::-1], cv2.CV_32FC1) map2x, map2y = cv2.initUndistortRectifyMap(K2, dist2, R2, P2, gray.shape[::-1], cv2.CV_32FC1)
步骤2:计算视差图与3D坐标
通过立体匹配得到视差图,再利用重投影矩阵Q计算3D坐标:
# 读取左右帧(替换为你的视频帧路径) left_img = cv2.imread('left_frame.jpg') right_img = cv2.imread('right_frame.jpg') # 立体校正 left_rect = cv2.remap(left_img, map1x, map1y, cv2.INTER_LINEAR) right_rect = cv2.remap(right_img, map2x, map2y, cv2.INTER_LINEAR) # 计算视差图(使用SGBM算法,参数可根据场景调整) sgbm = cv2.StereoSGBM_create(minDisparity=0, numDisparities=16*5, blockSize=5) disparity = sgbm.compute(cv2.cvtColor(left_rect, cv2.COLOR_BGR2GRAY), cv2.cvtColor(right_rect, cv2.COLOR_BGR2GRAY)) # 视差图转3D坐标 points_3d = cv2.reprojectImageTo3D(disparity, Q) # 提取两个钢瓶中心对应的3D坐标 u1, v1 = cylinder1_center u2, v2 = cylinder2_center coord1 = points_3d[v1][u1] coord2 = points_3d[v2][u2] # 计算欧氏距离 distance = np.linalg.norm(coord1 - coord2) print(f"两钢瓶欧氏距离:{distance:.2f}米")
关键注意事项
- 相机稳定性:若相机固定,上述方案直接可用;若相机移动,需结合SLAM算法(如ORB-SLAM)估计相机姿态,否则3D坐标会出现偏移。
- 目标检测精度:钢瓶中心/特征点的检测误差会直接影响距离计算结果,建议用最小外接矩形中心或轮廓矩中心作为特征点。
- 畸变校正:所有输入图像必须先做畸变校正,否则会导致像素坐标偏差,影响3D计算精度。
内容的提问来源于stack exchange,提问作者BEAN
相关产品推荐
相关产品推荐

