import cv2
import numpy as np
import glob
import matplotlib.pyplot as plt
# ====================== 1. 相机标定 ======================
# ====================== 1. 相机标定 ======================
def stereo_calibration(left_images, right_images, checkerboard_size, square_size):
"""计算相机内参和外参"""
# 准备标定板角点坐标
objpoints = [] # 3D点
imgpoints_left = [] # 左相机2D点
imgpoints_right = [] # 右相机2D点
# 提前定义图像尺寸变量
img_size = None
# 创建标定板世界坐标 (0,0,0), (1,0,0), ... (8,5,0)
objp = np.zeros((checkerboard_size[0] * checkerboard_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:checkerboard_size[0], 0:checkerboard_size[1]].T.reshape(-1, 2) * square_size
# 检测左右图像角点
for left_img, right_img in zip(left_images, right_images):
# 转换为灰度图
gray_left = cv2.cvtColor(left_img, cv2.COLOR_BGR2GRAY)
gray_right = cv2.cvtColor(right_img, cv2.COLOR_BGR2GRAY)
# 保存图像尺寸(所有图像应相同)
img_size = gray_left.shape[::-1] # (width, height)
# 检测角点
ret_left, corners_left = cv2.findChessboardCorners(gray_left, checkerboard_size, None)
ret_right, corners_right = cv2.findChessboardCorners(gray_right, checkerboard_size, None)
if ret_left and ret_right:
# 亚像素级精确化
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
corners_left = cv2.cornerSubPix(gray_left, corners_left, (11, 11), (-1, -1), criteria)
corners_right = cv2.cornerSubPix(gray_right, corners_right, (11, 11), (-1, -1), criteria)
objpoints.append(objp)
imgpoints_left.append(corners_left)
imgpoints_right.append(corners_right)
# 检查是否检测到角点
if len(objpoints) == 0:
raise ValueError("未检测到任何有效的标定板角点!请检查:\n"
"1. 图像路径是否正确\n"
"2. 标定板尺寸设置是否正确\n"
"3. 标定板是否在图像中清晰可见")
# 检查图像尺寸是否已设置
if img_size is None:
# 如果所有图像都失败,使用第一张图像的尺寸
gray_left = cv2.cvtColor(left_images[0], cv2.COLOR_BGR2GRAY)
img_size = gray_left.shape[::-1]
# 单目标定(获取初始内参)
ret, mtx_left, dist_left, _, _ = cv2.calibrateCamera(
objpoints, imgpoints_left, img_size, None, None)
ret, mtx_right, dist_right, _, _ = cv2.calibrateCamera(
objpoints, imgpoints_right, img_size, None, None)
# 双目标定(获取外参)
flags = cv2.CALIB_FIX_INTRINSIC
ret, _, _, _, _, R, T, E, F = cv2.stereoCalibrate(
objpoints, imgpoints_left, imgpoints_right,
mtx_left, dist_left, mtx_right, dist_right,
img_size, flags=flags
)
return mtx_left, dist_left, mtx_right, dist_right, R, T
# ====================== 2. 立体校正 ======================
def stereo_rectification(left_img, right_img, mtx_left, dist_left, mtx_right, dist_right, R, T):
"""校正图像并计算重映射矩阵"""
# 计算校正矩阵
R1, R2, P1, P2, Q, roi_left, roi_right = cv2.stereoRectify(
mtx_left, dist_left, mtx_right, dist_right,
left_img.shape[:2], R, T, alpha=0
)
# 计算重映射矩阵
map_left_x, map_left_y = cv2.initUndistortRectifyMap(
mtx_left, dist_left, R1, P1, left_img.shape[:2], cv2.CV_16SC2)
map_right_x, map_right_y = cv2.initUndistortRectifyMap(
mtx_right, dist_right, R2, P2, right_img.shape[:2], cv2.CV_16SC2)
# 重映射图像
rect_left = cv2.remap(left_img, map_left_x, map_left_y, cv2.INTER_LINEAR)
rect_right = cv2.remap(right_img, map_right_x, map_right_y, cv2.INTER_LINEAR)
return rect_left, rect_right, Q
# ====================== 3. 视差计算 ======================
def compute_disparity(rect_left, rect_right):
"""使用SGBM算法计算视差图"""
# 转换为灰度图
gray_left = cv2.cvtColor(rect_left, cv2.COLOR_BGR2GRAY)
gray_right = cv2.cvtColor(rect_right, cv2.COLOR_BGR2GRAY)
# 创建SGBM对象
window_size = 5
min_disp = 0
num_disp = 16 * 5
stereo = cv2.StereoSGBM_create(
minDisparity=min_disp,
numDisparities=num_disp,
blockSize=window_size,
P1=8 * 3 * window_size ** 2,
P2=32 * 3 * window_size ** 2,
disp12MaxDiff=1,
uniquenessRatio=15,
speckleWindowSize=0,
speckleRange=2,
preFilterCap=63,
mode=cv2.STEREO_SGBM_MODE_SGBM_3WAY
)
# 计算视差
disp = stereo.compute(gray_left, gray_right).astype(np.float32) / 16.0
# 后处理:中值滤波去除噪声
disp = cv2.medianBlur(disp, 5)
return disp
# ====================== 4. 深度图生成 ======================
def disparity_to_depth(disparity, Q):
"""将视差图转换为深度图"""
# 使用重投影矩阵Q计算深度
depth = cv2.reprojectImageTo3D(disparity, Q)[:, :, 2]
# 处理无效值(视差为0的点)
depth[disparity == 0] = 0
# 归一化深度图用于显示
depth_norm = cv2.normalize(depth, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)
return depth, depth_norm
# ====================== 主程序 ======================
if __name__ == "__main__":
# 参数配置
# ---------------- 参数区 -----------------
CHESSBOARD = (8, 6) # 棋盘格内角点 (列, 行)
SQUARE_SIZE = 25.0 # 棋盘格单格物理尺寸,单位 mm
IMG_DIR = './stereo' # 图片目录
LEFT_IMG_PATH = 'left_*.png' # 左图标识(添加通配符)
RIGHT_IMG_PATH = 'right_*.png' # 右图标识(添加通配符)
# ---------------------------------------
# 1. 加载标定图像
left_images = [cv2.imread(img) for img in sorted(glob.glob(LEFT_IMG_PATH))]
right_images = [cv2.imread(img) for img in sorted(glob.glob(RIGHT_IMG_PATH))]
# 添加检查确保图像加载成功
if len(left_images) == 0 or len(right_images) == 0:
raise FileNotFoundError(f"未找到匹配的图像文件。请检查路径: {LEFT_IMG_PATH} 和 {RIGHT_IMG_PATH}")
# 2. 执行双目标定 (修正参数传递)
print("开始相机标定...")
mtx_left, dist_left, mtx_right, dist_right, R, T = stereo_calibration(
left_images, right_images, CHESSBOARD, SQUARE_SIZE # 添加了CHESSBOARD参数
)
# 打印标定结果
print(f"左相机内参:\n{mtx_left}")
print(f"左相机畸变系数:\n{dist_left}")
print(f"右相机内参:\n{mtx_right}")
print(f"右相机畸变系数:\n{dist_right}")
print(f"旋转矩阵:\n{R}")
print(f"平移向量:\n{T}")
# 3. 加载测试图像(需要添加测试图像加载)
TEST_LEFT = 'test_left.png'
TEST_RIGHT = 'test_right.png'
test_left = cv2.imread(TEST_LEFT)
test_right = cv2.imread(TEST_RIGHT)
if test_left is None or test_right is None:
raise FileNotFoundError(f"未找到测试图像: {TEST_LEFT} 或 {TEST_RIGHT}")
# 4. 立体校正
print("执行立体校正...")
rect_left, rect_right, Q = stereo_rectification(
test_left, test_right, mtx_left, dist_left, mtx_right, dist_right, R, T
)
# 5. 计算视差图
print("计算视差图...")
disparity = compute_disparity(rect_left, rect_right)
# 6. 生成深度图
print("生成深度图...")
depth, depth_norm = disparity_to_depth(disparity, Q)
# 7. 结果显示 (修正显示问题)
plt.figure(figsize=(15, 10))
plt.subplot(231), plt.imshow(cv2.cvtColor(test_left, cv2.COLOR_BGR2RGB))
plt.title('原始左图'), plt.axis('off')
plt.subplot(232), plt.imshow(cv2.cvtColor(rect_left, cv2.COLOR_BGR2RGB))
plt.title('校正左图'), plt.axis('off')
plt.subplot(233), plt.imshow(disparity, cmap='jet')
plt.title('视差图'), plt.axis('off'), plt.colorbar()
plt.subplot(234), plt.imshow(depth_norm, cmap='jet')
plt.title('深度图(归一化)'), plt.axis('off'), plt.colorbar()
plt.subplot(235), plt.imshow(cv2.cvtColor(rect_left, cv2.COLOR_BGR2RGB))
plt.imshow(depth_norm, cmap='jet', alpha=0.5)
plt.title('深度叠加图'), plt.axis('off')
plt.tight_layout()
plt.savefig('depth_result.png', dpi=300)
plt.show()
# 8. 保存结果
cv2.imwrite("disparity.png", disparity)
cv2.imwrite("depth.png", depth_norm)
np.savez("calibration_params.npz",
mtx_left=mtx_left, dist_left=dist_left,
mtx_right=mtx_right, dist_right=dist_right,
R=R, T=T, Q=Q)
print("处理完成! 结果已保存")
前面这段代码测试图像应该如何命名
最新发布