【双目标定和立体矫正】

张正友标定法:

(1)首先求解单应矩阵,单应矩阵是相机内参矩阵与相机外参矩阵的混合矩阵;

(2)将混合矩阵分离成为相机内参矩阵和相机外参矩阵,首先求解相机内参矩阵;

(3)然后求解相机外参矩阵。

双目标定和立体匹配

步骤

  1. 采集左右视图的棋盘格数据
  2. 分别对单目进行标定, 得到每个相机的 内参数矩阵、 畸变系数 、旋转矩阵和 平移向量
  3. 使用 cv2.stereoCalibrate()函数进行双目校正,得到左右相机间的旋转矩阵、平移矩阵
  4. 计算立体校正的映射矩阵,进行左右视图的极限校正
import cv2
import numpy as np
import os
import matplotlib.pyplot as plt
from PIL import Image

def getImageList(img_dir):
    # 获取图片文件夹位置,方便opencv读取
    # 参数:照片文件路径
    # 返回值:数组,每一个元素表示一张照片的绝对路径
    imgPath = []
    if os.path.exists(img_dir) is False:
        print('error dir')
    else:
        for parent, dirNames, fileNames in os.walk(img_dir):
            for fileName in fileNames:
                imgPath.append(os.path.join(parent, fileName))
    return imgPath

def getObjectPoints(m, n, k):
    # 计算真实坐标
    # 参数:内点行数,内点列, 标定板大小
    # 返回值:数组,(m*n行,3列),真实内点坐标
    objP = np.zeros(shape=(m * n, 3), dtype=np.float32)
    for i in range(m * n):
        objP[i][0] = i % m
        objP[i][1] = int(i / m)
    return objP * k

# 立体校正后画直线进行可视化
def drawLine(img, num=16):
    h, w, *_ = img.shape
    for i in range(0, h, h // num):
        cv2.line(img, (0, i), (w, i), (0, 255, 0), 3, 8)
    return img


def make_dir(dir_path):
    if not os.path.exists(dir_path):
        os.makedirs(dir_path)


# 相机标定参数设定(单目,双目)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
criteria_stereo = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 计算标定板真实坐标,标定板内点9*6,大小30mm*30mm
board_size = (11, 8, 30)
objPoint = getObjectPoints(board_size[0], board_size[1], board_size[2])

objPoints = []
imgPointsL = []
imgPointsR = []
# 相片路径,自行修改
imgPathL = './shuangmu_biaoding/11_10-01/left_1/'
imgPathR = './shuangmu_biaoding/11_10-01/right_1/'
filePathL = getImageList(imgPathL)
filePathR = getImageList(imgPathR)

findCorners_left_save = './shuangmu_biaoding/11_10-01/findCorners_left/'  # 保存左视图棋盘格检测的角点的可视化结果
make_dir(findCorners_left_save)
findCorners_right_save = './shuangmu_biaoding/11_10-01/findCorners_right/'    # 保存右视图棋盘格检测的角点的可视化结果
make_dir(findCorners_right_save)
file_save = './shuangmu_biaoding/11_10-01/files_save/'     # 用于保存得到的各种参数文件
make_dir(file_save)

for i in range(len(filePathL)):
    # 分别读取每张图片并转化为灰度图
    imgL = cv2.imread(filePathL[i])
    imgR = cv2.imread(filePathR[i])
    grayL = cv2.cvtColor(imgL, cv2.COLOR_BGR2GRAY)
    grayR = cv2.cvtColor(imgR, cv2.COLOR_BGR2GRAY)
    # opencv寻找角点
    retL, cornersL = cv2.findChessboardCorners(grayL, board_size[0:2], None)
    retR, cornersR = cv2.findChessboardCorners(grayR, board_size[0:2], None)
    if (retL & retR) is True:
        # opencv对真实坐标格式要求,vector<vector<Point3f>>类型
        objPoints.append(objPoint)
        # 角点细化
        cornersL2 = cv2.cornerSubPix(grayL, cornersL, (11, 11), (-1, -1), criteria)
        cornersR2 = cv2.cornerSubPix(grayR, cornersR, (11, 11), (-1, -1), criteria)
        imgPointsL.append(cornersL2)
        imgPointsR.append(cornersR2)
        # # findHomography 函数是计算变换矩阵
        # # 参数cv2.RANSAC是使用RANSAC算法寻找一个最佳单应性矩阵H,即返回值M
        # # 返回值:M 为变换矩阵,mask是掩模
        # M, mask = cv2.findHomography(cornersL2, cornersR2, cv2.RANSAC, 5.0)
        # # ravel方法将数据降维处理,最后并转换成列表格式
        # matchesMask = mask.ravel().tolist()
        cv2.drawChessboardCorners(imgL, board_size[0:2], cornersL, retL)
        cv2.drawChessboardCorners(imgR, board_size[0:2], cornersR, retR)
        img_L_name = os.path.basename(filePathL[i])
        img_R_name = os.path.basename(filePathR[i])
        cv2.imwrite(findCorners_left_save+ img_L_name, imgL)    # 保存棋盘格检测的角点的可视化结果
        cv2.imwrite(findCorners_right_save+ img_R_name, imgR)   # 保存棋盘格检测的角点的可视化结果


# 对左右相机分别进行单目相机标定(复制时格式可能有点问题,用pycharm自动格式化)
# 标定结果 相机的内参数矩阵 畸变系数 旋转矩阵 平移向量
retL, cameraMatrixL, distMatrixL, RL, TL = cv2.calibrateCamera(objPoints, imgPointsL, grayL.shape[::-1], None, None)
retR, cameraMatrixR, distMatrixR, RR, TR = cv2.calibrateCamera(objPoints, imgPointsR, grayR.shape[::-1], None, None)
# 双目相机校正
retS, mLS, dLS, mRS, dRS, R, T, E, F = cv2.stereoCalibrate(objPoints, imgPointsL,
                                                           imgPointsR, cameraMatrixL,distMatrixL, cameraMatrixR, distMatrixR, grayL.shape[::-1],
                                                           criteria_stereo, flags=cv2.CALIB_USE_INTRINSIC_GUESS)

np.save(file_save+ 'cameraMatrixL', cameraMatrixL)
np.save(file_save+ 'distMatrixL', distMatrixL)
np.save(file_save+ 'RL', RL)
np.save(file_save+ 'TL', TL)

np.save(file_save+ 'cameraMatrixR', cameraMatrixR)
np.save(file_save+ 'distMatrixR', distMatrixR)
np.save(file_save+ 'RR', RR)
np.save(file_save+ 'TR', TR)

np.save(file_save+ 'mLS', mLS)
np.save(file_save+ 'dLS', dLS)
np.save(file_save+ 'mRS', mRS)
np.save(file_save+ 'dRS', dRS)
np.save(file_save+ 'R', R)
np.save(file_save+ 'T', T)

# mLS = np.array([[3.57779836e+03, 0.00000000e+00, 2.19388986e+03],
#                 [0.00000000e+00, 3.57627959e+03, 1.21979501e+03],
#                 [0.00000000e+00, 0.00000000e+00, 1.00000000e+00]])
# dLS = np.array([[-6.69762889e-03,  -1.27925228e-01,  -2.71378037e-04 , -1.71752147e-03, 2.99125637e-01]])
# mRS = np.array([[3.58048460e+03,  0.00000000e+00 , 2.13889596e+03],
#                 [0.00000000e+00,  3.58498975e+03,  1.25564055e+03],
#                 [0.00000000e+00,  0.00000000e+00,  1.00000000e+00]])
# dRS = np.array([[-0.04610254 , -0.07880463,  0.00058363, -0.00497765,  0.2171918 ]])
# R = np.array([[ 0.94783553,  0.00787214, -0.31866256], [-0.01536381, 0.99966136, -0.02100307], [ 0.31838931,  0.02480332,  0.9476355 ]])
# T = np.array([[-307.01659062], [-9.08185422], [-62.94997066]])
mLS = np.load(file_save+ 'mLS.npy')
dLS = np.load(file_save+ 'dLS.npy')
mRS = np.load(file_save+ 'mRS.npy')
dRS = np.load(file_save+ 'dRS.npy')
R = np.load(file_save+ 'R.npy')
T = np.load(file_save+ 'T.npy')

img_shape = (4352, 2448)
# img_shape = grayL.shape[::-1]
# 利用stereoRectify()计算立体校正的映射矩阵
rectify_scale = 1  # 设置为0的话,对图片进行剪裁,设置为1则保留所有原图像像素
RL, RR, PL, PR, Q, roiL, roiR = cv2.stereoRectify(mLS, dLS, mRS, dRS,
                                                  img_shape, R, T,
                                                  rectify_scale, (0, 0))

# 利用initUndistortRectifyMap函数计算畸变矫正和立体校正的映射变换,实现极线对齐。
Left_Stereo_Map = cv2.initUndistortRectifyMap(mLS, dLS, RL, PL,
                                              img_shape, cv2.CV_16SC2)

Right_Stereo_Map = cv2.initUndistortRectifyMap(mRS, dRS, RR, PR,
                                               img_shape, cv2.CV_16SC2)

rectified_img_save = './shuangmu_biaoding/11_10-01/rectified_img_save/'   # 校正后将左右视图平铺在一起的大图保存
rectified_line_save = './shuangmu_biaoding/11_10-01/rectified_line_save/'
rectified_left_save = './shuangmu_biaoding/11_10-01/rectified_left_save/' # 校正的左视图保存
rectified_right_save = './shuangmu_biaoding/11_10-01/rectified_right_save/'   # 校正后的右视图保存
make_dir(rectified_img_save)
make_dir(rectified_line_save)
make_dir(rectified_left_save)
make_dir(rectified_right_save)

# 立体校正效果显示
for i in range(len(filePathL)):
    # 分别读取每张图片并转化为灰度图
    frameL = cv2.imread(filePathL[i],)
    frameR = cv2.imread(filePathR[i],)
    img_L_name = os.path.basename(filePathL[i])
    img_R_name = os.path.basename(filePathR[i])

    Left_rectified = cv2.remap(frameL, Left_Stereo_Map[0], Left_Stereo_Map[1], cv2.INTER_LANCZOS4, cv2.BORDER_CONSTANT,
                               0)  # 使用remap函数完成映射
    im_L = Image.fromarray(Left_rectified)  # numpy 转 image类

    Right_rectified = cv2.remap(frameR, Right_Stereo_Map[0], Right_Stereo_Map[1], cv2.INTER_LANCZOS4,
                                cv2.BORDER_CONSTANT, 0)
    im_R = Image.fromarray(Right_rectified)  # numpy 转 image 类
    res = np.hstack((Left_rectified, Right_rectified))  # 将左右视图以水平方向拼在一起
    cv2.imwrite(rectified_img_save+ img_L_name, res)
    cv2.imwrite(rectified_left_save+ img_L_name, Left_rectified)
    cv2.imwrite(rectified_right_save + img_R_name, Right_rectified)

    # 画直线进行可视化
    res1 = np.concatenate((Left_rectified, Right_rectified), axis=1).copy()
    img_line = drawLine(res1, 24)
    cv2.imwrite(rectified_line_save+ img_L_name, img_line)

单目标定

代码:

import os
import cv2
import numpy as np
import glob

# 找棋盘格角点
# 阈值
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 棋盘格模板规格
w = 11  # 内角点个数,内角点是和其他格子连着的点
h = 8

# 世界坐标系中的棋盘格点,例如(0,0,0), (1,0,0), (2,0,0) ....,(8,5,0),去掉Z坐标,记为二维矩阵
objp = np.zeros((w * h, 3), np.float32)
objp[:, :2] = np.mgrid[0:w, 0:h].T.reshape(-1, 2)
# 储存棋盘格角点的世界坐标和图像坐标对
objpoints = []  # 在世界坐标系中的三维点
imgpoints = []  # 在图像平面的二维点

imgs_path = './shuangmu_biaoding/11_3/11_3_right_img/'
# images = glob.glob('shuangmu_biaoding/left/*.JPG')  # 标定所用图像
images = os.listdir(imgs_path)

findCorners_imgs_left = './shuangmu_biaoding/11_3/findCorners_11_3_right/'
if not os.path.exists(findCorners_imgs_left):
    os.makedirs(findCorners_imgs_left)

for fname in images:
    img = cv2.imread(imgs_path+ fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # cv2.imshow('findCorners', img)
    # 找到棋盘格角点
    # 棋盘图像(8位灰度或彩色图像)  棋盘尺寸  存放角点的位置
    ret, corners = cv2.findChessboardCorners(gray, (w, h), None)
    # 如果找到足够点对,将其存储起来
    if ret:
        # 角点精确检测
        # 输入图像 角点初始坐标 搜索窗口为2*winsize+1 死区 求角点的迭代终止条件
        cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
        objpoints.append(objp)
        imgpoints.append(corners)
        # 将角点在图像上显示
        cv2.drawChessboardCorners(img, (w, h), corners, ret)
        # cv2.imshow('findCorners', img)
        # cv2.waitKey(1000)
        cv2.imwrite(findCorners_imgs_left+ fname, img)
cv2.destroyAllWindows()
# 标定、去畸变
# 输入:世界坐标系里的位置 像素坐标 图像的像素尺寸大小 3*3矩阵,相机内参数矩阵 畸变矩阵
# 输出:标定结果 相机的内参数矩阵 畸变系数 旋转矩阵 平移向量
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
# mtx:内参数矩阵
# dist:畸变系数
# rvecs:旋转向量 (外参数)
# tvecs :平移向量 (外参数)
print(("ret:"), ret)
print(("mtx:\n"), mtx)  # 内参数矩阵
print(("dist:\n"), dist)  # 畸变系数   distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
print(("rvecs:\n"), rvecs)  # 旋转向量  # 外参数
print(("tvecs:\n"), tvecs)  # 平移向量  # 外参数


# # 去畸变
# img2 = cv2.imread('shuangmu_biaoding/right/IMG_1596.JPG')
# h, w = img2.shape[:2]
# # 我们已经得到了相机内参和畸变系数,在将图像去畸变之前,
# # 我们还可以使用cv.getOptimalNewCameraMatrix()优化内参数和畸变系数,
# # 通过设定自由自由比例因子alpha。当alpha设为0的时候,
# # 将会返回一个剪裁过的将去畸变后不想要的像素去掉的内参数和畸变系数;
# # 当alpha设为1的时候,将会返回一个包含额外黑色像素点的内参数和畸变系数,并返回一个ROI用于将其剪裁掉
# newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 0, (w, h))  # 自由比例参数
#
# dst = cv2.undistort(img2, mtx, dist, None, newcameramtx)
# # 根据前面ROI区域裁剪图片
# x, y, w, h = roi
# dst = dst[y:y + h, x:x + w]
# cv2.imwrite('shuangmu_biaoding/calibresult_right.jpg', dst)

# 反投影误差
# 通过反投影误差,我们可以来评估结果的好坏。越接近0,说明结果越理想。
# 通过之前计算的内参数矩阵、畸变系数、旋转矩阵和平移向量,使用cv2.projectPoints()计算三维点到二维图像的投影,
# 然后计算反投影得到的点与图像上检测到的点的误差,最后计算一个对于所有标定图像的平均误差,这个值就是反投影误差。
total_error = 0
for i in range(len(objpoints)):
    imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
    error = cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
    total_error += error
print(("total error: "), total_error / len(objpoints))

error_value =total_error / len(objpoints)
output_list = [ret, mtx, dist, rvecs, tvecs, error_value]
with open('shuangmu_biaoding/11_3/output_right.txt', 'a')as f:
    f.write('ret'+'\t'+ 'mtx'+'\t'+ 'dist'+'\t'+ 'rvecs'+'\t'+ 'tvecs'+'\t'+ 'error'+ '\n'*2)
    for item in output_list:
        f.write(str(item)+ '\n'*2)

单应矩阵

单应矩阵代码一:

import cv2
import numpy as np

MIN_MATCH_COUNT = 10
img1 = cv2.imread('left_right_frame/129_left.png')          # queryImage
img2 = cv2.imread('left_right_frame/129_right.png')          # trainImage


# 通过SIFT算法寻找匹配的点
def SIFT():
    # Initiate SIFT detector
    sift = cv2.xfeatures2d.SIFT_create()
    # find the keypoints and descriptors with SIFT
    kp1, des1 = sift.detectAndCompute(img2,None)
    kp2, des2 = sift.detectAndCompute(img1,None)
    # BFMatcher with default params
    bf = cv2.BFMatcher()
    matches = bf.knnMatch(des1,des2, k=2)
    # Apply ratio test
    good = []
    for m,n in matches:
        if m.distance < 0.75*n.distance:

          good.append(m)
    # cv2.drawMatchesKnn expects list of lists as matches.
    good_2 = np.expand_dims(good, 1)
    matching = cv2.drawMatchesKnn(img1,kp1,img2,kp2,good_2[:20],None, flags=2)

    if len(good)>MIN_MATCH_COUNT:
        # 获取关键点的坐标
        src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2)
        dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2)

        H, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)
        wrap = cv2.warpPerspective(img2, H, (img2.shape[1]+img2.shape[1] , img2.shape[0]+img2.shape[0]))
        wrap[0:img2.shape[0], 0:img2.shape[1]] = img1

        rows, cols = np.where(wrap[:,:,0] !=0)
        min_row, max_row = min(rows), max(rows) +1
        min_col, max_col = min(cols), max(cols) +1
        result = wrap[min_row:max_row,min_col:max_col,:]    # 去除黑色无用部分

        return matching, result

if __name__ == '__main__':
    matching, result = SIFT()
    # cv2.imshow('img3.jpg',matching)
    cv2.imwrite('left_right_frame/matching.jpg', matching)
    # cv2.imshow('shuangmu_biaoding/result.jpg',result)
    cv2.imwrite('left_right_frame/result.jpg', result)
    # cv2.waitKey(0)
    # cv2.destroyAllWindows()
    # cv2.waitKey(1)
    # cv2.waitKey(1)
    # cv2.waitKey(1)
    # cv2.waitKey(1)

单应矩阵代码二:

# orb特征点检测,暴力匹配
import cv2
import numpy as np
import matplotlib.pyplot as plt

# 也能输入RGB图像,不过匹配不到目标
train = cv2.imread('shuangmu_biaoding/11_3/11_3_left_img/9.JPG', 0)
query = cv2.imread('shuangmu_biaoding/11_3/11_3_right_img/9.JPG', 0)

exx = 1
exy = 1

# 不改变尺寸匹配不到
train = cv2.resize(train, dsize=None, fx=exx, fy=exy)
query = cv2.resize(query, dsize=None, fx=exx, fy=exy)

# 暴力匹配
orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(train, None)
kp2, des2 = orb.detectAndCompute(query, None)

# 针对ORB算法 NORM_HAMMING 计算特征距离 True判断交叉验证
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# 特征描述子匹配
matches = bf.match(des1, des2)
matches = sorted(matches, key=lambda x: x.distance)

# 改变数组的表现形式,不改变数据内容,数据内容是每个关键点的坐标位置
src_pts = np.float32([kp1[m.queryIdx].pt for m in matches]).reshape(-1, 1, 2)  # 测试图像的关键点的坐标位置
dst_pts = np.float32([kp2[m.trainIdx].pt for m in matches]).reshape(-1, 1, 2)  # 样本图像的关键点的坐标位置
# findHomography 函数是计算变换矩阵
# 参数cv2.RANSAC是使用RANSAC算法寻找一个最佳单应性矩阵H,即返回值M
# 返回值:M 为变换矩阵,mask是掩模
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
# ravel方法将数据降维处理,最后并转换成列表格式
matchesMask = mask.ravel().tolist()
# 获取train的图像尺寸
h, w = train.shape

# pts是图像train的四个顶点
pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)
# 计算变换后的四个顶点坐标位置
# 使用得到的变换矩阵对原图像的四个角进行变换,获得在目标图像上对应的坐标
dst = cv2.perspectiveTransform(pts, M)
# 根据四个顶点坐标位置在query图像画出变换后的边框
query = cv2.polylines(query, [np.int32(dst)], True, (255, 255, 255), 3, cv2.LINE_AA)

img3 = cv2.drawMatches(train, kp1, query, kp2, matches[:20], None, flags=2)
cv2.imwrite('Homography_test/000.png', img3)
plt.imshow(cv2.cvtColor(img3, cv2.COLOR_BGR2RGB))
plt.show()

双目单应矩阵代码一

import cv2
import numpy as np
import os
import matplotlib.pyplot as plt
from PIL import Image

def getImageList(img_dir):
    # 获取图片文件夹位置,方便opencv读取
    # 参数:照片文件路径
    # 返回值:数组,每一个元素表示一张照片的绝对路径
    imgPath = []
    if os.path.exists(img_dir) is False:
        print('error dir')
    else:
        for parent, dirNames, fileNames in os.walk(img_dir):
            for fileName in fileNames:
                imgPath.append(os.path.join(parent, fileName))
    return imgPath

def getObjectPoints(m, n, k):
    # 计算真实坐标
    # 参数:内点行数,内点列, 标定板大小
    # 返回值:数组,(m*n行,3列),真实内点坐标
    objP = np.zeros(shape=(m * n, 3), dtype=np.float32)
    for i in range(m * n):
        objP[i][0] = i % m
        objP[i][1] = int(i / m)
    return objP * k



#
# # 相机标定参数设定(单目,双目)
# criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# criteria_stereo = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# # 计算标定板真实坐标,标定板内点9*6,大小30mm*30mm
# board_size = (11, 8, 30)
# objPoint = getObjectPoints(board_size[0], board_size[1], board_size[2])
#
# objPoints = []
# imgPointsL = []
# imgPointsR = []
# # 相片路径,自行修改
# imgPathL = './shuangmu_biaoding/11_10-01/left_1/'
# imgPathR = './shuangmu_biaoding/11_10-01/right_1/'
# filePathL = getImageList(imgPathL)
# filePathR = getImageList(imgPathR)
#
# findCorners_left_save = './shuangmu_biaoding/11_10-01/findCorners_left/'  # 保存左视图棋盘格检测的角点的可视化结果
# if not os.path.exists(findCorners_left_save):
#     os.makedirs(findCorners_left_save)
# findCorners_right_save = './shuangmu_biaoding/11_10-01/findCorners_right/'    # 保存右视图棋盘格检测的角点的可视化结果
# if not os.path.exists(findCorners_right_save):
#     os.makedirs(findCorners_right_save)
file_save = './Homography_test/11_10-01/files_save/'     # 用于保存得到的各种参数文件
# if not os.path.exists(file_save):
#     os.makedirs(file_save)
#
# for i in range(len(filePathL)):
#     # if i == 3:
#     #     break
#     # 分别读取每张图片并转化为灰度图
#     imgL = cv2.imread(filePathL[i])
#     imgR = cv2.imread(filePathR[i])
#     grayL = cv2.cvtColor(imgL, cv2.COLOR_BGR2GRAY)
#     grayR = cv2.cvtColor(imgR, cv2.COLOR_BGR2GRAY)
#     # opencv寻找角点
#     retL, cornersL = cv2.findChessboardCorners(grayL, board_size[0:2], None)
#     retR, cornersR = cv2.findChessboardCorners(grayR, board_size[0:2], None)
#     if (retL & retR) is True:
#         # opencv对真实坐标格式要求,vector<vector<Point3f>>类型
#         objPoints.append(objPoint)
#         # 角点细化
#         cornersL2 = cv2.cornerSubPix(grayL, cornersL, (11, 11), (-1, -1), criteria)
#         cornersR2 = cv2.cornerSubPix(grayR, cornersR, (11, 11), (-1, -1), criteria)
#         imgPointsL.append(cornersL2)
#         imgPointsR.append(cornersR2)
#         # findHomography 函数是计算变换矩阵
#         # 参数cv2.RANSAC是使用RANSAC算法寻找一个最佳单应性矩阵H,即返回值M
#         # 返回值:M 为变换矩阵,mask是掩模
#         M, mask = cv2.findHomography(cornersL2, cornersR2, cv2.RANSAC, 5.0)
#         # ravel方法将数据降维处理,最后并转换成列表格式
#         print(M)
#         print('------------------------------------------')
#         img_L_name = os.path.basename(filePathL[i])
#         name = img_L_name.split('.')[0]
#         np.save(file_save + name, M)

        # matchesMask = mask.ravel().tolist()
#         cv2.drawChessboardCorners(imgL, board_size[0:2], cornersL, retL)
#         cv2.drawChessboardCorners(imgR, board_size[0:2], cornersR, retR)
#         img_L_name = os.path.basename(filePathL[i])
#         img_R_name = os.path.basename(filePathR[i])
#         cv2.imwrite(findCorners_left_save+ img_L_name, imgL)    # 保存棋盘格检测的角点的可视化结果
#         cv2.imwrite(findCorners_right_save+ img_R_name, imgR)   # 保存棋盘格检测的角点的可视化结果
#
# imgPointsL = np.array(imgPointsL).reshape(-1, 1,2)
# imgPointsR = np.array(imgPointsR).reshape(-1, 1,2)
#
# M, mask = cv2.findHomography(imgPointsL, imgPointsR, cv2.RANSAC, 5.0)
# # M, mask = cv2.findHomography(imgPointsL, imgPointsR, cv2.RANSAC, 5.0, maxIters=10000)
# print('*'*20)
# print(M)
# np.save(file_save + 'totle_M', M)
# with open(file_save+ 'totle_M.txt', 'w')as f:
#     f.write(str(M))
# # 对左右相机分别进行单目相机标定(复制时格式可能有点问题,用pycharm自动格式化)
# # 标定结果 相机的内参数矩阵 畸变系数 旋转矩阵 平移向量
# retL, cameraMatrixL, distMatrixL, RL, TL = cv2.calibrateCamera(objPoints, imgPointsL, grayL.shape[::-1], None, None)
# retR, cameraMatrixR, distMatrixR, RR, TR = cv2.calibrateCamera(objPoints, imgPointsR, grayR.shape[::-1], None, None)
# M = np.array([[1.82211923e+00, -1.47284674e-01, -4.57158368e+03],
#                 [ 3.09248793e-01 , 1.67631352e+00 ,-9.68337402e+02],
#                 [ 2.08157564e-04, -1.47653122e-05 , 1.00000000e+00]])


dst_save = './Homography_test/11_10-01/dst_save/'
if not os.path.exists(dst_save):
    os.makedirs(dst_save)
files = os.listdir(file_save)
for file in files:
    M = np.load(file_save+file)
    # pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)
    pts_left = np.float32([[1120, 410],[1120, 528], [1160, 528], [1160, 410],]).reshape(-1, 1, 2)
    pts_right = np.float32([[145, 405], [145, 540], [200, 540], [200, 405]]).reshape(-1, 1, 2)
    # 计算变换后的四个顶点坐标位置
    # 使用得到的变换矩阵对原图像的四个角进行变换,获得在目标图像上对应的坐标
    dst = cv2.perspectiveTransform(pts_left, M)
    # 根据四个顶点坐标位置在query图像画出变换后的边框
    print(dst)
    print('-----------------------------')
    file_name = file.split('.')[0]
    np.save(dst_save+file_name, dst)

双目单应矩阵代码二

import cv2
import numpy as np
import os
import matplotlib.pyplot as plt
from PIL import Image

def getImageList(img_dir):
    # 获取图片文件夹位置,方便opencv读取
    # 参数:照片文件路径
    # 返回值:数组,每一个元素表示一张照片的绝对路径
    imgPath = []
    if os.path.exists(img_dir) is False:
        print('error dir')
    else:
        for parent, dirNames, fileNames in os.walk(img_dir):
            for fileName in fileNames:
                imgPath.append(os.path.join(parent, fileName))
    return imgPath

def getObjectPoints(m, n, k):
    # 计算真实坐标
    # 参数:内点行数,内点列, 标定板大小
    # 返回值:数组,(m*n行,3列),真实内点坐标
    objP = np.zeros(shape=(m * n, 3), dtype=np.float32)
    for i in range(m * n):
        objP[i][0] = i % m
        objP[i][1] = int(i / m)
    return objP * k


def make_dir(dir_path):
    if not os.path.exists(dir_path):
        os.makedirs(dir_path)

#
# # 相机标定参数设定(单目,双目)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
criteria_stereo = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 计算标定板真实坐标,标定板内点9*6,大小30mm*30mm
board_size = (11, 8, 30)
objPoint = getObjectPoints(board_size[0], board_size[1], board_size[2])

objPoints = []
imgPointsL = []
imgPointsR = []
# 相片路径,自行修改
imgPathL = './shuangmu_biaoding/11_10-01/left_1/'
imgPathR = './shuangmu_biaoding/11_10-01/right_1/'
filePathL = getImageList(imgPathL)
filePathR = getImageList(imgPathR)

findCorners_left_save = './shuangmu_biaoding/11_10-01/findCorners_left/'  # 保存左视图棋盘格检测的角点的可视化结果
make_dir(findCorners_left_save)

findCorners_right_save = './shuangmu_biaoding/11_10-01/findCorners_right/'    # 保存右视图棋盘格检测的角点的可视化结果
make_dir(findCorners_right_save)

file_save = './Homography_test/11_10-01/files_save/'     # 用于保存得到的各种参数文件
make_dir(file_save)

H_save_img = './Homography_test/11_10-01/img_save/'
make_dir(H_save_img)

result_img_save = './Homography_test/11_10-01/result_img_save/'
make_dir(result_img_save)

for i in range(len(filePathL)):
    img_L_name = os.path.basename(filePathL[i])
    if img_L_name != '16.JPG':
        continue
    # 分别读取每张图片并转化为灰度图
    imgL = cv2.imread(filePathL[i])
    imgR = cv2.imread(filePathR[i])
    grayL = cv2.cvtColor(imgL, cv2.COLOR_BGR2GRAY)
    grayR = cv2.cvtColor(imgR, cv2.COLOR_BGR2GRAY)
    # opencv寻找角点
    retL, cornersL = cv2.findChessboardCorners(grayL, board_size[0:2], None)
    retR, cornersR = cv2.findChessboardCorners(grayR, board_size[0:2], None)
    if (retL & retR) is True:
        # opencv对真实坐标格式要求,vector<vector<Point3f>>类型
        objPoints.append(objPoint)
        # 角点细化
        cornersL2 = cv2.cornerSubPix(grayL, cornersL, (11, 11), (-1, -1), criteria)
        cornersR2 = cv2.cornerSubPix(grayR, cornersR, (11, 11), (-1, -1), criteria)
        imgPointsL.append(cornersL2)
        imgPointsR.append(cornersR2)
        # findHomography 函数是计算变换矩阵
        # 参数cv2.RANSAC是使用RANSAC算法寻找一个最佳单应性矩阵H,即返回值M
        # 返回值:M 为变换矩阵,mask是掩模
        M, mask = cv2.findHomography(cornersL2, cornersR2, cv2.RANSAC, 5.0)
        # ravel方法将数据降维处理,最后并转换成列表格式
        # print(M)
        # print('------------------------------------------')
        # img_L_name = os.path.basename(filePathL[i])
        # name = img_L_name.split('.')[0]
        # np.save(file_save + name, M)
        #
        # matchesMask = mask.ravel().tolist()
        # cv2.drawChessboardCorners(imgL, board_size[0:2], cornersL, retL)
        # cv2.drawChessboardCorners(imgR, board_size[0:2], cornersR, retR)
        # img_L_name = os.path.basename(filePathL[i])
        # img_R_name = os.path.basename(filePathR[i])
        # cv2.imwrite(findCorners_left_save+ img_L_name, imgL)    # 保存棋盘格检测的角点的可视化结果
        # cv2.imwrite(findCorners_right_save+ img_R_name, imgR)   # 保存棋盘格检测的角点的可视化结果
        point_color , thickness = (0, 255, 0), 2
        res = np.hstack((imgL, imgR))
        img_L_name = os.path.basename(filePathL[i])
        pts_left =cornersL2.tolist()
        pts_right = cornersR2.tolist()
        for pt_left, pt_right in zip(pts_left, pts_right):
            pt_left = tuple([int(item) for item in pt_left[0]])
            pt_right = pt_right[0]
            pt_right[0]+=4352
            pt_right = tuple([int(item) for item in pt_right])

            # cv2.line(res, pt_left, pt_right,(0, 255, 0),  thickness=6)
            # cv2.line(res, pt_left, pt_right, point_color, thickness)
        # cv2.imwrite(H_save_img+ img_L_name, res)

        # pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)
        pts_left = np.float32([[3750, 955],[3750, 1090], [3833, 1090], [3833, 955]]).reshape(-1, 1, 2)
        # pts_left = np.float32([[1120, 410], [1120, 528], [1160, 528], [1160, 410], ]).reshape(-1, 1, 2)
        # pts_right = np.float32([[145, 405], [145, 540], [200, 540], [200, 405]]).reshape(-1, 1, 2)
        # 计算变换后的四个顶点坐标位置
        # 使用得到的变换矩阵对原图像的四个角进行变换,获得在目标图像上对应的坐标
        dst = cv2.perspectiveTransform(pts_left, M)
        print('------------------------------')
        print(dst)
        src = imgL.copy()   # 在左原图上画出要映射的框
        query = imgR.copy()   # 在右视图中画出映射的框
        query = cv2.polylines(query, [np.int32(dst)], True, (255, 0, 255), 3, cv2.LINE_AA)
        src = cv2.polylines(src, [np.int32(pts_left)], True, (255, 0, 255), 3, cv2.LINE_AA)
        cv2.imwrite(result_img_save+ 'left_'+img_L_name, src)
        cv2.imwrite(result_img_save+ 'right_'+ img_L_name, query)

MATLAB提供了相机标定工具箱,可以用于双目系统的标定及校正。通过相机标定,可以获取两个相机各自的内参矩阵和畸变系数,以及旋转矩阵和平移矩阵。这些参数对于进行双目立体矫正非常重要。 在进行双目立体矫正之前,需要先获取双目相机的内外参数信息,即stereo_cam.yml文件。该文件中包含了相机的内参矩阵、畸变系数、旋转矩阵和平移矩阵等信息。 双目立体矫正的目的是将两个相机的图像进行对齐,消除畸变,使得双目图像在同一平面上对应的像素点有相同的行列坐标。这样可以方便后续的视差计算和深度距离计算。 在MATLAB中进行双目立体矫正的具体步骤如下: 1. 使用相机标定工具箱进行相机标定,获取各个相机的内参矩阵和畸变系数。 2. 使用双目标定结果,计算相机的外参矩阵,包括旋转矩阵和平移矩阵。 3. 根据双目标定结果,使用stereoRectify函数对左右相机图像进行立体矫正,得到新的校正后的图像。 4. 根据校正后的图像,使用stereoParams对象和stereoParameters函数计算立体校正后的视差图。 通过这些步骤,可以实现MATLAB下的双目立体矫正。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python](https://blog.csdn.net/guyuealian/article/details/121301896)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [利用 Calibration Toolbox for Matlab 工具箱进行双目立体校正](https://blog.csdn.net/Di_Wong/article/details/77995222)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XTX_AI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值