前言
对照片进行光学畸变矫正后,因为是在研究双目视觉,所以就要进行立体矫正了。
一、立体校正是什么?
标定后得到了左右相机的内参数:焦距、主点坐标以及径向畸变和切向畸变,通过相机的内参数和畸变系数可校正左右拍摄图像的畸变,得到对应环境场景正确的图像。同时实验还得到了相机外参,外参用于立体校正,使左右图像处于同一平面内,且实现行对准,这一过程被称为极线校正。
二、校准步骤
1.照片准备
因为还没有实地拍摄照片,只能先用opencv的自带实例。
2.立体匹配
stereoCalibrate() 是用来标定一个立体摄像头的,也就是同时标定两个摄像头。标定的结果除了能够求出两个摄像头的内外参数矩阵,跟能够得出两个摄像头的位置关系R,T。R– 输出第一和第二相机坐标系之间的旋转矩阵,T– 输出第一和第二相机坐标系之间的旋转矩阵平移向量。
flag-CV_CALIB_FIX_INTRINSIC 如果该标志被设置,那么就会固定输入的cameraMatrix和distCoeffs不变,只求解R,T,E,F。
initUndistortRectifyMap()是计算无畸变和修正转换映射。
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
# 终止标准
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 准备对象点, 如 (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
# 用于存储所有图像对象点与图像点的矩阵
objpoints = [] # 在真实世界中的 3d 点
imgpointsR = [] # 在图像平面中的 右2d 点
imgpointsL = [] # 在图像平面中的 左2d 点
#左图13张右图13张
for i in range(1, 14):
t = str(i)
ChessImaR = cv.imread(r'D:\test\venv\data\right' + t + '.jpg', 0) # 右视图
ChessImaL = cv.imread(r'D:\test\venv\data\left' + t + '.jpg', 0) # 左视图
# 提取每一张图片的角点
retR, cornersR = cv.findChessboardCorners(ChessImaR, (7, 6), None)
retL, cornersL = cv.findChessboardCorners(ChessImaL, (7, 6), None)
if (True == retR) & (True == retL):
objpoints.append(objp)
# 亚像素精确化,对粗提取的角点进行精确化
cv.cornerSubPix(ChessImaR, cornersR, (11, 11), (-1, -1), criteria)
cv.cornerSubPix(ChessImaL, cornersL, (11, 11), (-1, -1), criteria)
imgpointsR.append(cornersR)
imgpointsL.append(cornersL)
#通过多个视角的2D/3D对应,求解出该相机的内参数和每一个视角的外参数
retR, mtxR, distR, rvecsR, tvecsR = cv.calibrateCamera(objpoints, imgpointsR, ChessImaR.shape[::-1], None, None)
hR, wR = ChessImaR.shape[:2]
OmtxR, roiR = cv.getOptimalNewCameraMatrix(mtxR, distR, (wR, hR), 1, (wR, hR))
retL, mtxL, distL, rvecsL, tvecsL = cv.calibrateCamera(objpoints, imgpointsL, ChessImaL.shape[::-1], None, None)
hL, wL = ChessImaL.shape[:2]
OmtxL, roiL = cv.getOptimalNewCameraMatrix(mtxL, distL, (wL, hL), 1, (wL, hL))
retS, MLS, dLS, MRS, dRS, R, T, E, F = cv.stereoCalibrate(objpoints,imgpointsL,imgpointsR,OmtxL,distL,OmtxR,distR,
ChessImaR.shape[::-1], criteria,0) #ChessImaR.shape[::-1]是图像的大小
# 利用stereoRectify()计算立体校正的映射矩阵
rectify_scale = 1 # 设置为0的话,对图片进行剪裁,设置为1则保留所有原图像像素
RL, RR, PL, PR, Q, roiL, roiR = cv.stereoRectify(MLS, dLS, MRS, dRS,
ChessImaR.shape[::-1], R, T,
rectify_scale, (0, 0))
# 利用initUndistortRectifyMap函数计算畸变矫正和立体校正的映射变换,实现极线对齐。
Left_Stereo_Map = cv.initUndistortRectifyMap(MLS, dLS, RL, PL,
ChessImaR.shape[::-1], cv.CV_16SC2)
Right_Stereo_Map = cv.initUndistortRectifyMap(MRS, dRS, RR, PR,
ChessImaR.shape[::-1], cv.CV_16SC2)
# 立体校正效果显示
for i in range(1, 13):
t = str(i)
frameR = cv.imread(r'D:\test\venv\data\right' + t + '.jpg', 0)
frameL = cv.imread(r'D:\test\venv\data\left' + t + '.jpg', 0)
Left_rectified = cv.remap(frameL, Left_Stereo_Map[0], Left_Stereo_Map[1], cv.INTER_LANCZOS4, cv.BORDER_CONSTANT,
0) # 使用remap函数完成映射
im_L = Image.fromarray(Left_rectified) # numpy 转 image类
Right_rectified = cv.remap(frameR, Right_Stereo_Map[0], Right_Stereo_Map[1], cv.INTER_LANCZOS4,
cv.BORDER_CONSTANT, 0)
im_R = Image.fromarray(Right_rectified) # numpy 转 image 类
# 创建一个能同时并排放下两张图片的区域,后把两张图片依次粘贴进去
width = im_L.size[0] * 2
height = im_L.size[1]
img_compare = Image.new('RGBA', (width, height))
img_compare.paste(im_L, box=(0, 0))
img_compare.paste(im_R, box=(640, 0))
# 在已经极线对齐的图片上均匀画线
for i in range(1, 20):
len = 480 / 20
plt.axhline(y=i * len, color='r', linestyle='-')
plt.imshow(img_compare)
plt.savefig(r'D:\test\venv\result\result' + t + '.jpg')
plt.show()
总结
参考了opencv官方中文手册,https://blog.csdn.net/qq_22059843/article/details/103400094大佬的代码,以及https://docs.opencv.org/3.4/dc/dbb/tutorial_py_calibration.html官方例子。