本文目录导读:

我来详细介绍如何使用OpenCV实现立体视觉,包括完整的案例步骤和代码。
立体视觉基础流程
立体视觉的核心是通过两个相机(或一个相机移动)拍摄同一场景,计算视差图,然后恢复深度信息。
完整实现案例
相机标定
import cv2
import numpy as np
import glob
def stereo_calibration():
"""
双目相机标定
"""
# 准备标定棋盘格参数
CHECKERBOARD = (6, 9) # 内角点数量
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 世界坐标系中的棋盘格点
objp = np.zeros((CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2)
# 存储左/右图像的点
objpoints = [] # 3D点
imgpoints_left = [] # 左图像2D点
imgpoints_right = [] # 右图像2D点
# 读入标定图像
left_images = glob.glob('calibration/left/*.jpg')
right_images = glob.glob('calibration/right/*.jpg')
for left_img, right_img in zip(left_images, right_images):
img_left = cv2.imread(left_img, cv2.IMREAD_GRAYSCALE)
img_right = cv2.imread(right_img, cv2.IMREAD_GRAYSCALE)
# 查找棋盘格角点
ret_left, corners_left = cv2.findChessboardCorners(img_left, CHECKERBOARD, None)
ret_right, corners_right = cv2.findChessboardCorners(img_right, CHECKERBOARD, None)
if ret_left and ret_right:
objpoints.append(objp)
# 角点亚像素优化
corners_left = cv2.cornerSubPix(img_left, corners_left, (11, 11), (-1, -1), criteria)
corners_right = cv2.cornerSubPix(img_right, corners_right, (11, 11), (-1, -1), criteria)
imgpoints_left.append(corners_left)
imgpoints_right.append(corners_right)
# 单目标定
ret_left, mtx_left, dist_left, _, _ = cv2.calibrateCamera(
objpoints, imgpoints_left, img_left.shape[::-1], None, None)
ret_right, mtx_right, dist_right, _, _ = cv2.calibrateCamera(
objpoints, imgpoints_right, img_right.shape[::-1], None, None)
# 双目标定
flags = cv2.CALIB_FIX_INTRINSIC
criteria_stereo = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-5)
ret, mtx_left, dist_left, mtx_right, dist_right, R, T, E, F = cv2.stereoCalibrate(
objpoints, imgpoints_left, imgpoints_right,
mtx_left, dist_left, mtx_right, dist_right,
img_left.shape[::-1], criteria=criteria_stereo, flags=flags)
return mtx_left, dist_left, mtx_right, dist_right, R, T
立体校正
def stereo_rectification(mtx_left, dist_left, mtx_right, dist_right, R, T, image_size):
"""
立体校正 - 使左右图像行对齐
"""
# 立体校正
R1, R2, P1, P2, Q, _, _ = cv2.stereoRectify(
mtx_left, dist_left, mtx_right, dist_right,
image_size, R, T, alpha=0)
# 计算映射表
map1_left, map2_left = cv2.initUndistortRectifyMap(
mtx_left, dist_left, R1, P1, image_size, cv2.CV_32FC1)
map1_right, map2_right = cv2.initUndistortRectifyMap(
mtx_right, dist_right, R2, P2, image_size, cv2.CV_32FC1)
return map1_left, map2_left, map1_right, map2_right, Q
视差图计算
def compute_disparity(img_left, img_right, method='SGBM'):
"""
计算视差图
"""
# 转换为灰度图
gray_left = cv2.cvtColor(img_left, cv2.COLOR_BGR2GRAY)
gray_right = cv2.cvtColor(img_right, cv2.COLOR_BGR2GRAY)
if method == 'SGBM':
# SGBM算法(更准确)
stereo = cv2.StereoSGBM_create(
minDisparity=0,
numDisparities=16 * 5, # 视差范围,16的倍数
blockSize=11,
P1=8 * 3 * 11 ** 2,
P2=32 * 3 * 11 ** 2,
disp12MaxDiff=1,
uniquenessRatio=10,
speckleWindowSize=100,
speckleRange=32
)
else:
# BM算法(更快)
stereo = cv2.StereoBM_create(
numDisparities=16 * 5,
blockSize=15
)
# 计算视差
disparity = stereo.compute(gray_left, gray_right).astype(np.float32) / 16.0
return disparity
深度恢复
def compute_depth(disparity, Q):
"""
从视差图恢复深度信息
"""
# 确保视差有效
disparity[disparity <= 0] = 0.1
# 使用reprojectImageTo3D恢复3D坐标
points_3d = cv2.reprojectImageTo3D(disparity, Q)
# 深度 = Z坐标
depth = points_3d[:, :, 2]
# 过滤无效深度
depth[depth > 1000] = 0 # 设置最大深度阈值
return depth, points_3d
完整主程序
def main():
"""
立体视觉主程序
"""
# 1. 标定相机(如果有标定参数文件可跳过)
try:
# 加载已标定的参数
calibration_data = np.load('calibration_data.npz')
mtx_left = calibration_data['mtx_left']
dist_left = calibration_data['dist_left']
mtx_right = calibration_data['mtx_right']
dist_right = calibration_data['dist_right']
R = calibration_data['R']
T = calibration_data['T']
print("已加载标定参数")
except:
print("进行相机标定...")
mtx_left, dist_left, mtx_right, dist_right, R, T = stereo_calibration()
# 保存标定参数
np.savez('calibration_data.npz',
mtx_left=mtx_left, dist_left=dist_left,
mtx_right=mtx_right, dist_right=dist_right,
R=R, T=T)
# 2. 读取左右图像
img_left = cv2.imread('left_image.jpg')
img_right = cv2.imread('right_image.jpg')
if img_left is None or img_right is None:
print("无法读取图像")
return
h, w = img_left.shape[:2]
# 3. 立体校正
map1_left, map2_left, map1_right, map2_right, Q = stereo_rectification(
mtx_left, dist_left, mtx_right, dist_right, R, T, (w, h))
# 应用校正
rectified_left = cv2.remap(img_left, map1_left, map2_left, cv2.INTER_LINEAR)
rectified_right = cv2.remap(img_right, map1_right, map2_right, cv2.INTER_LINEAR)
# 4. 计算视差图
disparity = compute_disparity(rectified_left, rectified_right, method='SGBM')
# 5. 恢复深度
depth, points_3d = compute_depth(disparity, Q)
# 6. 显示结果
# 归一化视差图用于显示
disparity_normalized = cv2.normalize(disparity, None, 0, 255, cv2.NORM_MINMAX)
disparity_normalized = np.uint8(disparity_normalized)
# 归一化深度图
depth_normalized = cv2.normalize(depth, None, 0, 255, cv2.NORM_MINMAX)
depth_normalized = np.uint8(depth_normalized)
# 显示
cv2.imshow('Left Image', rectified_left)
cv2.imshow('Right Image', rectified_right)
cv2.imshow('Disparity Map', disparity_normalized)
cv2.imshow('Depth Map', depth_normalized)
cv2.imshow('Color Depth', cv2.applyColorMap(depth_normalized, cv2.COLORMAP_JET))
# 7. 保存结果
cv2.imwrite('disparity_map.jpg', disparity_normalized)
cv2.imwrite('depth_map.jpg', depth_normalized)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
实时立体视觉(使用摄像头)
def realtime_stereo():
"""
实时立体视觉处理
"""
# 加载标定参数
calibration_data = np.load('calibration_data.npz')
mtx_left = calibration_data['mtx_left']
dist_left = calibration_data['dist_left']
mtx_right = calibration_data['mtx_right']
dist_right = calibration_data['dist_right']
R = calibration_data['R']
T = calibration_data['T']
# 打开左/右摄像头
cap_left = cv2.VideoCapture(0) # 左相机
cap_right = cv2.VideoCapture(1) # 右相机
h, w = 480, 640
# 计算校正映射
map1_left, map2_left, map1_right, map2_right, Q = stereo_rectification(
mtx_left, dist_left, mtx_right, dist_right, R, T, (w, h))
while True:
ret_left, frame_left = cap_left.read()
ret_right, frame_right = cap_right.read()
if not ret_left or not ret_right:
break
# 校正图像
rectified_left = cv2.remap(frame_left, map1_left, map2_left, cv2.INTER_LINEAR)
rectified_right = cv2.remap(frame_right, map1_right, map2_right, cv2.INTER_LINEAR)
# 计算视差
disparity = compute_disparity(rectified_left, rectified_right, method='BM')
# 计算深度
depth, _ = compute_depth(disparity, Q)
# 显示
disparity_vis = cv2.normalize(disparity, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
depth_vis = cv2.normalize(depth, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
cv2.imshow('Left', rectified_left)
cv2.imshow('Right', rectified_right)
cv2.imshow('Disparity', disparity_vis)
cv2.imshow('Depth', cv2.applyColorMap(depth_vis, cv2.COLORMAP_JET))
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap_left.release()
cap_right.release()
cv2.destroyAllWindows()
关键参数调优建议
def optimize_sgbm_parameters(img_left, img_right):
"""
SGBM参数调优
"""
# 关键参数说明
params = {
'minDisparity': 0, # 最小视差
'numDisparities': 16 * 5, # 视差范围(必须是16的倍数)
'blockSize': 11, # 匹配块大小(奇数,3-11)
'P1': 8 * 3 * 11 ** 2, # 平滑惩罚参数1
'P2': 32 * 3 * 11 ** 2, # 平滑惩罚参数2
'disp12MaxDiff': 1, # 左右一致性检查阈值
'uniquenessRatio': 10, # 唯一性比例
'speckleWindowSize': 100, # 去斑点窗口大小
'speckleRange': 32 # 去斑点范围
}
# 根据场景调整
# 近景:减小numDisparities,减小blockSize
# 远景:增大numDisparities
# 纹理丰富:减小blockSize
# 纹理稀疏:增大blockSize
return params
使用建议
- 硬件要求:建议使用相同型号的双目相机
- 标定质量:标定直接影响结果质量,至少需要20对图像
- 光照条件:保持左右图像光照一致
- 处理速度:BM算法比SGBM快3-5倍,适合实时应用
- 精度权衡:增大视差范围会降低处理速度但提高深度范围
这个案例涵盖了立体视觉的完整流程,你可以根据实际需求调整参数和算法选择。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播