双目相机三维坐标计算与视差计算,python-opencv实现

原理我就不说了,就是各种公式推导和坐标转换再加上一些计算方法。

要使用这个代码首先你得有自己采集的双目图片,或者可以用cv自带的来运行代码。

其次该代码也是自己综合了许多人的代码略微调整修改了,可以说是缝合怪。其中如何匹配左右图像的点(貌似可以极线校正后利用极线匹配来做,但我好像不知道怎样做)我是自己想的,可能很粗糙,也不知道对不对。思路就是:通过给左右图片的内角点标上序号,然后左右相机的相同序号的内角点作为一组点位代入三维坐标求解函数。

直接上代码吧!我自己做的实验不理想(拍摄的图片找不到角点,图片分辨率太大不知道缩小分辨率是否会对标定有影响),但我不知道是代码问题还是啥,就发出来让大家一起学习与指导一下。

第一个版本:matlab标定,opencv处理

import numpy as np
import cv2
import os
import  xml.dom.minidom

# 遍历图片
def getFileList(dir,Filelist, ext=None):
    """
    获取文件夹及其子文件夹中文件列表
    输入 dir:文件夹根目录
    输入 ext: 扩展名
    返回: 文件路径列表
    """
    newDir = dir
    if os.path.isfile(dir):
        if ext is None:
            Filelist.append(dir)
        else:
            if ext in dir[-3:]:
                Filelist.append(dir)
    
    elif os.path.isdir(dir):
        for s in os.listdir(dir):
            newDir=os.path.join(dir,s)
            getFileList(newDir, Filelist, ext)
 
    return Filelist
# 计算内焦点世界坐标
def calRealPoint(row,col,trueLength):
    imgpoint = []
    for rowIndex in range(0,col):
        for colIndex in range(0,row):
            imgpoint.append([colIndex*trueLength,rowIndex*trueLength,0])
    imgpoint = np.array(imgpoint,np.float32)
    return imgpoint
# 画线
def draw_line1(image1, image2):
    # 建立输出图像
    height = max(image1.shape[0], image2.shape[0])
    width = image1.shape[1] + image2.shape[1]
    
    output = np.zeros((height, width,3), dtype=np.uint8)
    output[0:image1.shape[0], 0:image1.shape[1]] = image1
    output[0:image2.shape[0], image1.shape[1]:] = image2
 
    for k in range(15):
        cv2.line(output, (0, 50 * (k + 1)), (2 * width, 50 * (k + 1)), (0, 255, 0), thickness=2, lineType=cv2.LINE_AA)  # 直线间隔:100
 
    return output
# 三维坐标计算
def uvToXYZ(lx, ly, rx, ry):
    mLeft = np.hstack([leftRotation, leftTranslation])
    mLeftM = np.dot(MLS, mLeft)
    mRight = np.hstack([R, T])
    mRightM = np.dot(MRS, mRight)
    # print(mLeft)
    # print(mLeftM)
    A = np.zeros(shape=(4, 3))
    for i in range(0, 3):
        A[0][i] = lx * mLeftM[2, i] - mLeftM[0][i]
    for i in range(0, 3):
        A[1][i] = ly * mLeftM[2][i] - mLeftM[1][i]
    for i in range(0, 3):
        A[2][i] = rx * mRightM[2][i] - mRightM[0][i]
    for i in range(0, 3):
        A[3][i] = ry * mRightM[2][i] - mRightM[1][i]
    B = np.zeros(shape=(4, 1))
    B[0][0] = mLeftM[0][3] - lx * mLeftM[2][3]
    B[1][0] = mLeftM[1][3] - ly * mLeftM[2][3]
    B[2][0] = mRightM[0][3] - rx * mRightM[2][3]
    B[3][0] = mRightM[1][3] - ry * mRightM[2][3]
    XYZ = np.zeros(shape=(3, 1))
    # 采用最小二乘法求其空间坐标
    cv2.solve(A, B, XYZ, cv2.DECOMP_SVD)
    # print('坐标为:\n',XYZ)
    return XYZ
# 获取像素坐标
def get_pixel(img,index):
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

    _,corners = cv2.findChessboardCorners(gray,(row,col))
    criteria = (cv2.TermCriteria_EPS+cv2.TermCriteria_MAX_ITER,30,0.01)
    corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
    return corners2[index].ravel()
# 从xml(matlab生成)读取内外参数等
def getParams(file):
    dom = xml.dom.minidom.parse(file)
    root = dom.documentElement
    datas = root.getElementsByTagName('data')
    after_datas = []
    for data in datas:
        data = data.firstChild.data
        data = data.replace('\n', '').replace('\r', '')
        data = data.split(' ')[0:-1]
        data = [float(x) for x in data]
        after_datas.append(data)
    MLS = np.array(after_datas[0]).reshape(3,3)
    MRS = np.array(after_datas[1]).reshape(3,3)
    dLS = np.array(after_datas[2])
    dRS = np.array(after_datas[3])
    R = np.array(after_datas[4]).reshape(3,3)
    T = np.array(after_datas[5]).reshape(3,1) 
    return MLS,MRS,dLS,dRS,R,T 
# 清空文件
def del_file(path):
    ls = os.listdir(path)
    for i in ls:
        c_path = os.path.join(path, i)
        if os.path.isdir(c_path):
            del_file(c_path)
        else:
            os.remove(c_path)

del_file('./left_')
del_file('./right_')
del_file('./left_re')
del_file('./right_re')
leftRotation = np.array([[1, 0, 0],
                         [0, 1, 0],
                         [0, 0, 1]])
leftTranslation = np.array([[0],
                            [0],
                            [0]])
# 棋盘格内焦点个数横纵
# 参数读取
MLS,MRS,dLS,dRS,R,T = getParams('2th.xml')
print('L内参\n',MLS)
print('L畸变\n',dLS)
print('R内参\n',MRS)
print('R畸变\n',dRS)
print('旋转\n',R)
print('平移\n',T)
row = 9
col = 6
# 格子真实尺寸
trueLength = 10
# 图片后缀
hz = 'jpg'
# 遍历图片
imglistL = getFileList('./left',[],hz)
imglistR = getFileList('./right',[],hz)
# 创建存储能被角点检测到的图片的文件夹
folderL = './left_'
folderR = './right_'
if not os.path.exists(folderL):
    os.mkdir(folderL)
if not os.path.exists(folderR):
    os.mkdir(folderR)
# 首先检测图片角点检测是否正确
for i in range(0,len(imglistL)):
    ChessImaR = cv2.imread(imglistR[i],0)
    ChessImaL = cv2.imread(imglistL[i],0)
    # 角点检测
    retR, cornersR = cv2.findChessboardCorners(ChessImaR,
                                               (row,col),None)
    retL, cornersL = cv2.findChessboardCorners(ChessImaL,
                                               (row,col),None)
    # 只要一对图片中有一张不能检测角点,这对图片便舍弃
    if (retR == True) & (retL == True):
        print('第%d对图片符合'%i)
        # 同时存入另一个文件夹
        cv2.imwrite(folderL+'/img'+str(i)+'.'+hz,ChessImaL)
        cv2.imwrite(folderR+'/img'+str(i)+'.'+hz,ChessImaR)
        # 画出检测的角点
        cv2.drawChessboardCorners(ChessImaL, (row, col), cornersL, retL)
        cv2.drawChessboardCorners(ChessImaR, (row, col), cornersR, retR)
        cv2.imshow('imgL',ChessImaL)
        cv2.imshow('imgR',ChessImaR)
        if cv2.waitKey(0) & 0xFF == ord(' '):
            continue
        if cv2.waitKey(0) & 0xFF == 27:
            break
    else:
        print('第%d对图片不符合,剔除'%i)
# 遍历筛选后图片
imglistL_ = getFileList(folderL,[],hz)
imglistR_ = getFileList(folderR,[],hz)
# 参数含义不知
rectify_scale = 0 
# Q重投影矩阵
RL, RR, PL, PR, Q, roiL, roiR = cv2.stereoRectify(MLS, dLS, MRS, dRS,
                                                 ChessImaR.shape[::-1], R, T,
                                                 rectify_scale,(0,0),alpha=-1)
# 畸变校正
Left_Stereo_Map = cv2.initUndistortRectifyMap(MLS, dLS, RL, PL,
                                             ChessImaR.shape[::-1], cv2.CV_16SC2)
Right_Stereo_Map = cv2.initUndistortRectifyMap(MRS, dRS, RR, PR,
                                              ChessImaR.shape[::-1], cv2.CV_16SC2)
# 参数设置参考官方例程
window_size = 3
min_disp = 16
num_disp = 112-min_disp
stereo = cv2.StereoSGBM_create(minDisparity = min_disp,
    numDisparities = num_disp,
    blockSize = 16,
    P1 = 8*3*window_size**2,
    P2 = 32*3*window_size**2,
    disp12MaxDiff = 1,
    uniquenessRatio = 10,
    speckleWindowSize = 100,
    speckleRange = 32
)
# 使用滤波器降低噪声
stereoR=cv2.ximgproc.createRightMatcher(stereo)
# 过滤器参数设置
lmbda = 80000
sigma = 1.8
visual_multiplier = 1.0 
wls_filter = cv2.ximgproc.createDisparityWLSFilter(matcher_left=stereo)
wls_filter.setLambda(lmbda)
wls_filter.setSigmaColor(sigma)
# 创建保存极线校正的图片
folderL_ = './left_re'
folderR_ = './right_re'
if not os.path.exists(folderL_):
    os.mkdir(folderL_)
if not os.path.exists(folderR_):
    os.mkdir(folderR_)
# 标定参数使用
for i in range(0,len(imglistL_)):
    frameR = cv2.imread(imglistR_[i])
    frameL = cv2.imread(imglistL_[i])
    # Rectify图片,极线校正
    Left_nice = cv2.remap(frameL,Left_Stereo_Map[0],Left_Stereo_Map[1], cv2.INTER_AREA)
    Right_nice = cv2.remap(frameR,Right_Stereo_Map[0],Right_Stereo_Map[1], cv2.INTER_AREA)
    # 保存极线校正后的图片
    cv2.imwrite(folderL_+'/img'+str(i)+'.'+hz,Left_nice)
    cv2.imwrite(folderR_+'/img'+str(i)+'.'+hz,Right_nice)
    # 显示极线校正的图片
    res1 = draw_line1(Left_nice,Right_nice)
    cv2.imshow('ln',Left_nice)
    cv2.imshow('rn',Right_nice)
    cv2.imshow('res1',res1)
    # 计算距离三维坐标
    u11,v11 = get_pixel(Left_nice,0)
    u21,v21 = get_pixel(Right_nice,0)
    u12,v12 = get_pixel(Left_nice,1)
    u22,v22 = get_pixel(Right_nice,1)
    p1 = uvToXYZ(u11,v11,u21,v21)
    p2 = uvToXYZ(u12,v12,u22,v22)
    print('p1坐标\n',p1)
    print('p2坐标\n',p2)
    print('距离\n',np.linalg.norm(p1-p2))
    ################################################
    grayR= cv2.cvtColor(Right_nice,cv2.COLOR_BGR2GRAY)
    grayL= cv2.cvtColor(Left_nice,cv2.COLOR_BGR2GRAY)
    # 计算深度,参考官方例程
    dispL= stereo.compute(grayL,grayR).astype(np.float32)/ 16.0 
    dispR= stereo.compute(grayR,grayL).astype(np.float32)/ 16.0
    # 使用滤波器降噪,filteredimg为过滤后的深度图
    filteredImg= wls_filter.filter(dispL,grayL,None,dispR)
    filteredImg = cv2.normalize(src=filteredImg, dst=filteredImg, beta=0, alpha=255, 
                                norm_type=cv2.NORM_MINMAX)
    filteredImg = np.uint8(filteredImg)
    
    points = cv2.reprojectImageTo3D(dispL,Q)

    cv2.imshow('filteredImg',filteredImg)
    cv2.imshow('disparity', (dispL-min_disp)/num_disp)
    
    if cv2.waitKey(0) & 0xFF == ord(' '):
        continue
    if cv2.waitKey(0) & 0xFF == 27:
        break

cv2.waitKey(0)
cv2.destroyAllWindows()

matlab标定参数变xml

function writeXML(stereoParams,file)

docNode = com.mathworks.xml.XMLUtils.createDocument('opencv_storage'); %创建xml文件对象
docRootNode = docNode.getDocumentElement; %获取根节点

IntrinsicMatrixl = (stereoParams.CameraParameters1.IntrinsicMatrix)'; %相机内参矩阵
RadialDistortionl = stereoParams.CameraParameters1.RadialDistortion; %相机径向畸变参数向量1*3
TangentialDistortionl =stereoParams.CameraParameters1.TangentialDistortion; %相机切向畸变向量1*2
Distortionl = [RadialDistortionl(1:2),TangentialDistortionl,RadialDistortionl(3)]; %构成opencv中的畸变系数向量[k1,k2,p1,p2,k3]

IntrinsicMatrixr = (stereoParams.CameraParameters2.IntrinsicMatrix)'; %相机内参矩阵
RadialDistortionr = stereoParams.CameraParameters2.RadialDistortion; %相机径向畸变参数向量1*3
TangentialDistortionr =stereoParams.CameraParameters2.TangentialDistortion; %相机切向畸变向量1*2
Distortionr = [RadialDistortionr(1:2),TangentialDistortionr,RadialDistortionr(3)]; %构成opencv中的畸变系数向量[k1,k2,p1,p2,k3]

R = stereoParams.RotationOfCamera2';
T = stereoParams.TranslationOfCamera2;

camera_matrix = docNode.createElement('lcamera-matrix'); %创建mat节点
data = docNode.createElement('data');
for i=1:3
    for j=1:3
        data.appendChild(docNode.createTextNode(sprintf('%.16f ',IntrinsicMatrixl(i,j))));
    end
    data.appendChild(docNode.createTextNode(sprintf('\n')));
end
camera_matrix.appendChild(data);
docRootNode.appendChild(camera_matrix);

camera_matrix = docNode.createElement('rcamera-matrix'); %创建mat节点
data = docNode.createElement('data');
for i=1:3
    for j=1:3
        data.appendChild(docNode.createTextNode(sprintf('%.16f ',IntrinsicMatrixr(i,j))));
    end
    data.appendChild(docNode.createTextNode(sprintf('\n')));
end
camera_matrix.appendChild(data);
docRootNode.appendChild(camera_matrix);

distortion = docNode.createElement('ldistortion');
data = docNode.createElement('data');
for i=1:5
      data.appendChild(docNode.createTextNode(sprintf('%.16f ',Distortionl(i))));
end
distortion.appendChild(data);
docRootNode.appendChild(distortion);

distortion = docNode.createElement('rdistortion');
data = docNode.createElement('data');
for i=1:5
      data.appendChild(docNode.createTextNode(sprintf('%.16f ',Distortionr(i))));
end
distortion.appendChild(data);
docRootNode.appendChild(distortion);

rM = docNode.createElement('R');
data = docNode.createElement('data');
for i=1:3
    for j=1:3
        data.appendChild(docNode.createTextNode(sprintf('%.16f ',R(i,j))));
    end
    data.appendChild(docNode.createTextNode(sprintf('\n')));
end
rM.appendChild(data);
docRootNode.appendChild(rM);

tM = docNode.createElement('T');
data = docNode.createElement('data');
for i = 1:3
    data.appendChild(docNode.createTextNode(sprintf('%.16f ',T(i))));
end
tM.appendChild(data);
docRootNode.appendChild(tM);

xmlFileName = file;
xmlwrite(xmlFileName,docNode);
end

第二个版本,标定与立体校正匹配等全用cv

 

import numpy as np
import cv2
import os

# 遍历图片
def getFileList(dir,Filelist, ext=None):
    """
    获取文件夹及其子文件夹中文件列表
    输入 dir:文件夹根目录
    输入 ext: 扩展名
    返回: 文件路径列表
    """
    newDir = dir
    if os.path.isfile(dir):
        if ext is None:
            Filelist.append(dir)
        else:
            if ext in dir[-3:]:
                Filelist.append(dir)
    
    elif os.path.isdir(dir):
        for s in os.listdir(dir):
            newDir=os.path.join(dir,s)
            getFileList(newDir, Filelist, ext)
 
    return Filelist
# 计算内焦点世界坐标
def calRealPoint(row,col,trueLength):
    imgpoint = []
    for rowIndex in range(0,col):
        for colIndex in range(0,row):
            imgpoint.append([colIndex*trueLength,rowIndex*trueLength,0])
    imgpoint = np.array(imgpoint,np.float32)
    return imgpoint
# 画线
def draw_line1(image1, image2):
    # 建立输出图像
    height = max(image1.shape[0], image2.shape[0])
    width = image1.shape[1] + image2.shape[1]
    
    output = np.zeros((height, width,3), dtype=np.uint8)
    output[0:image1.shape[0], 0:image1.shape[1]] = image1
    output[0:image2.shape[0], image1.shape[1]:] = image2
 
    for k in range(15):
        cv2.line(output, (0, 50 * (k + 1)), (2 * width, 50 * (k + 1)), (0, 255, 0), thickness=2, lineType=cv2.LINE_AA)  # 直线间隔:100
 
    return output
# 三维坐标计算
def uvToXYZ(lx, ly, rx, ry):
    mLeft = np.hstack([leftRotation, leftTranslation])
    mLeftM = np.dot(MLS, mLeft)
    mRight = np.hstack([R, T])
    mRightM = np.dot(MRS, mRight)
    # print(mLeft)
    # print(mLeftM)
    A = np.zeros(shape=(4, 3))
    for i in range(0, 3):
        A[0][i] = lx * mLeftM[2, i] - mLeftM[0][i]
    for i in range(0, 3):
        A[1][i] = ly * mLeftM[2][i] - mLeftM[1][i]
    for i in range(0, 3):
        A[2][i] = rx * mRightM[2][i] - mRightM[0][i]
    for i in range(0, 3):
        A[3][i] = ry * mRightM[2][i] - mRightM[1][i]
    B = np.zeros(shape=(4, 1))
    B[0][0] = mLeftM[0][3] - lx * mLeftM[2][3]
    B[1][0] = mLeftM[1][3] - ly * mLeftM[2][3]
    B[2][0] = mRightM[0][3] - rx * mRightM[2][3]
    B[3][0] = mRightM[1][3] - ry * mRightM[2][3]
    XYZ = np.zeros(shape=(3, 1))
    # 采用最小二乘法求其空间坐标
    cv2.solve(A, B, XYZ, cv2.DECOMP_SVD)
    # print('坐标为:\n',XYZ)
    return XYZ
# 获取像素坐标
def get_pixel(img,index):
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

    _,corners = cv2.findChessboardCorners(gray,(row,col))
    criteria = (cv2.TermCriteria_EPS+cv2.TermCriteria_MAX_ITER,30,0.01)
    corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
    return corners2[index].ravel()
# 清空文件夹文件
def del_file(path):
    ls = os.listdir(path)
    for i in ls:
        c_path = os.path.join(path, i)
        if os.path.isdir(c_path):
            del_file(c_path)
        else:
            os.remove(c_path)

# 初始化
del_file('./left_')
del_file('./right_')
del_file('./left_re')
del_file('./right_re')
leftRotation = np.array([[1, 0, 0],
                         [0, 1, 0],
                         [0, 0, 1]])
leftTranslation = np.array([[0],
                            [0],
                            [0]])
# 棋盘格内焦点个数横纵
row = 9
col = 6
# 格子真实尺寸
trueLength = 10
# 图片后缀
hz = 'jpg'
# 遍历图片
imglistL = getFileList('./left',[],hz)
imglistR = getFileList('./right',[],hz)
# 创建存储能被角点检测到的图片的文件夹
folderL = './left_'
folderR = './right_'
if not os.path.exists(folderL):
    os.mkdir(folderL)
if not os.path.exists(folderR):
    os.mkdir(folderR)
# 首先检测图片角点检测是否正确
for i in range(0,len(imglistL)):
    ChessImaR = cv2.imread(imglistR[i],0)
    ChessImaL = cv2.imread(imglistL[i],0)
    # 角点检测
    retR, cornersR = cv2.findChessboardCorners(ChessImaR,
                                               (row,col),None)
    retL, cornersL = cv2.findChessboardCorners(ChessImaL,
                                               (row,col),None)
    # 只要一对图片中有一张不能检测角点,这对图片便舍弃
    if (retR == True) & (retL == True):
        print('第%d对图片符合'%i)
        # 同时存入另一个文件夹
        cv2.imwrite(folderL+'/img'+str(i)+'.'+hz,ChessImaL)
        cv2.imwrite(folderR+'/img'+str(i)+'.'+hz,ChessImaR)
        # 画出检测的角点
        cv2.drawChessboardCorners(ChessImaL, (row, col), cornersL, retL)
        cv2.drawChessboardCorners(ChessImaR, (row, col), cornersR, retR)
        cv2.imshow('imgL',ChessImaL)
        cv2.imshow('imgR',ChessImaR)
        if cv2.waitKey(0) & 0xFF == ord(' '):
            continue
        if cv2.waitKey(0) & 0xFF == 27:
            break
    else:
        print('第%d对图片不符合,剔除'%i)
#############################################################
#############################################################
# 对筛选后的图像进行标定,校正,匹配,求视差,最小二乘求解空间坐标
# 亚像素的参数和立体标定的参数
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)
# 遍历筛选后图片
imglistL_ = getFileList(folderL,[],hz)
imglistR_ = getFileList(folderR,[],hz)
# 存储每对图片的世界坐标,z为0,其它根据格子实际长度计算
objpoints = []
# 分别存储左右图片的角点亚像素坐标   
imgpointsR = []
imgpointsL = []
# 计算世界坐标下内角的坐标
objp = calRealPoint(row,col,trueLength)

print('Starting calibration for the 2 cameras... ')
for i in range(0,len(imglistL_)):   
    ChessImaL_ = cv2.imread(imglistL_[i],0) 
    ChessImaR_ = cv2.imread(imglistR_[i],0)   
    retR_, cornersR_ = cv2.findChessboardCorners(ChessImaR_,
                                               (row,col),None) 
    retL_, cornersL_ = cv2.findChessboardCorners(ChessImaL_,
                                               (row,col),None)
    if (True == retR_) & (True == retL_):
        # 亚像素
        cornersR2_ = cv2.cornerSubPix(ChessImaR_,cornersR_,(11,11),(-1,-1),criteria)
        cornersL2_ = cv2.cornerSubPix(ChessImaL_,cornersL_,(11,11),(-1,-1),criteria)
        imgpointsR.append(cornersR2_)
        imgpointsL.append(cornersL2_)
        objpoints.append(objp)
        # 画角点
        cv2.drawChessboardCorners(ChessImaL_, (row, col), cornersL2_, retL_)
        cv2.drawChessboardCorners(ChessImaR_, (row, col), cornersR2_, retR_)
        print('第%d对图片'%i)
        cv2.imshow('imgL',ChessImaL_)
        cv2.imshow('imgR',ChessImaR_)
        if cv2.waitKey(0) & 0xFF == ord(' '):
            continue
        if cv2.waitKey(0) & 0xFF == 27:
            break
# 类似先进行单目标定
retR, mtxR, distR, rvecsR, tvecsR = cv2.calibrateCamera(objpoints,
                                                        imgpointsR,
                                                        ChessImaR_.shape[::-1],None,None)
# 重投影误差计算
for i in range(0,len(objpoints)):
    err = 0
    ChessImaR_1 = cv2.imread(imglistR_[i])
    # 反向计算像素坐标,与亚像素角点坐标比较 ,计算的temp有两部分
    temp = cv2.projectPoints(objpoints[i],rvecsR[i],tvecsR[i],mtxR,distR)
    for j in range(0,temp[0].shape[0]):
        err = err+np.linalg.norm(temp[0][j]-imgpointsR[i][j])
    err = err/temp[0].shape[0]
    print('右边第%d张重新投影与原角点亚像素平均误差为:%f\n'%(i,err))
    cv2.drawChessboardCorners(ChessImaR_1, (row, col), temp[0],patternWasFound=True)
    cv2.imshow('the_right',ChessImaR_1)
    if cv2.waitKey(0) & 0xFF == ord(' '):
        continue
    if cv2.waitKey(0) & 0xFF == 27:
        break
###############################################################################
retL, mtxL, distL, rvecsL, tvecsL = cv2.calibrateCamera(objpoints,
                                                        imgpointsL,
                                                        ChessImaL_.shape[::-1],None,None)
# 重投影误差计算
for i in range(0,len(objpoints)):
    err = 0
    ChessImaL_1 = cv2.imread(imglistL_[i])
    # 反向计算像素坐标,与亚像素角点坐标比较 ,计算的temp有两部分
    temp = cv2.projectPoints(objpoints[i],rvecsL[i],tvecsL[i],mtxL,distL)
    for j in range(0,temp[0].shape[0]):
        err = err+np.linalg.norm(temp[0][j]-imgpointsL[i][j])
    err = err/temp[0].shape[0]
    print('左边第%d张重新投影与原角点亚像素平均误差为:%f\n'%(i,err))
    cv2.drawChessboardCorners(ChessImaL_1, (row, col), temp[0],patternWasFound=True)
    cv2.imshow('the_left',ChessImaL_1)
    if cv2.waitKey(0) & 0xFF == ord(' '):
        continue
    if cv2.waitKey(0) & 0xFF == 27:
        break
print('Cameras Ready to use')
#############################################################################
# 立体标定及可选参数,具体查阅
flags = 0
flags |= cv2.CALIB_FIX_INTRINSIC
#flags |= cv2.CALIB_FIX_PRINCIPAL_POINT
# flags |= cv2.CALIB_USE_INTRINSIC_GUESS
#flags |= cv2.CALIB_FIX_FOCAL_LENGTH
#flags |= cv2.CALIB_FIX_ASPECT_RATIO
#flags |= cv2.CALIB_ZERO_TANGENT_DIST
#flags |= cv2.CALIB_RATIONAL_MODEL
#flags |= cv2.CALIB_SAME_FOCAL_LENGTH
#flags |= cv2.CALIB_FIX_K3
#flags |= cv2.CALIB_FIX_K4
#flags |= cv2.CALIB_FIX_K5
retS, MLS, dLS, MRS, dRS, R, T, E, F = cv2.stereoCalibrate(objpoints,
                                                          imgpointsL,
                                                          imgpointsR,
                                                          mtxL,
                                                          distL,
                                                          mtxR,
                                                          distR,
                                                          ChessImaR.shape[::-1],
                                                          criteria_stereo,
                                                          flags)
print('L内参\n',MLS)
print('L畸变\n',dLS)
print('R内参\n',MRS)
print('R畸变\n',dRS)
print('旋转\n',R)
print('平移\n',T)
# 参数含义不知
rectify_scale = 0 
# Q重投影矩阵
RL, RR, PL, PR, Q, roiL, roiR = cv2.stereoRectify(MLS, dLS, MRS, dRS,
                                                 ChessImaR.shape[::-1], R, T,
                                                 rectify_scale,(0,0),alpha=-1)
# 畸变校正
Left_Stereo_Map = cv2.initUndistortRectifyMap(MLS, dLS, RL, PL,
                                             ChessImaR.shape[::-1], cv2.CV_16SC2)
Right_Stereo_Map = cv2.initUndistortRectifyMap(MRS, dRS, RR, PR,
                                              ChessImaR.shape[::-1], cv2.CV_16SC2)
# 参数设置参考官方例程
window_size = 3
min_disp = 16
num_disp = 112-min_disp
stereo = cv2.StereoSGBM_create(minDisparity = min_disp,
    numDisparities = num_disp,
    blockSize = 16,
    P1 = 8*3*window_size**2,
    P2 = 32*3*window_size**2,
    disp12MaxDiff = 1,
    uniquenessRatio = 10,
    speckleWindowSize = 100,
    speckleRange = 32
)
# 使用滤波器降低噪声
stereoR=cv2.ximgproc.createRightMatcher(stereo)
# 过滤器参数设置
lmbda = 80000
sigma = 1.8
visual_multiplier = 1.0 
wls_filter = cv2.ximgproc.createDisparityWLSFilter(matcher_left=stereo)
wls_filter.setLambda(lmbda)
wls_filter.setSigmaColor(sigma)
# 创建保存极线校正的图片
folderL_ = './left_re'
folderR_ = './right_re'
if not os.path.exists(folderL_):
    os.mkdir(folderL_)
if not os.path.exists(folderR_):
    os.mkdir(folderR_)
# 标定参数使用
for i in range(0,len(imglistL_)):
    frameR = cv2.imread(imglistR_[i])
    frameL = cv2.imread(imglistL_[i])
    # Rectify图片,极线校正
    Left_nice = cv2.remap(frameL,Left_Stereo_Map[0],Left_Stereo_Map[1], cv2.INTER_AREA)
    Right_nice = cv2.remap(frameR,Right_Stereo_Map[0],Right_Stereo_Map[1], cv2.INTER_AREA)
    # 保存极线校正后的图片
    cv2.imwrite(folderL_+'/img'+str(i)+'.'+hz,Left_nice)
    cv2.imwrite(folderR_+'/img'+str(i)+'.'+hz,Right_nice)
    # 显示极线校正的图片
    res1 = draw_line1(Left_nice,Right_nice)
    cv2.imshow('ln',Left_nice)
    cv2.imshow('rn',Right_nice)
    cv2.imshow('res1',res1)
    # 计算距离三维坐标
    u11,v11 = get_pixel(Left_nice,0)
    u21,v21 = get_pixel(Right_nice,0)
    u12,v12 = get_pixel(Left_nice,1)
    u22,v22 = get_pixel(Right_nice,1)
    p1 = uvToXYZ(u11,v11,u21,v21)
    p2 = uvToXYZ(u12,v12,u22,v22)
    print('p1坐标\n',p1)
    print('p2坐标\n',p2)
    print('距离\n',np.linalg.norm(p1-p2))
    ################################################
    grayR= cv2.cvtColor(Right_nice,cv2.COLOR_BGR2GRAY)
    grayL= cv2.cvtColor(Left_nice,cv2.COLOR_BGR2GRAY)
    # 计算深度,参考官方例程
    dispL= stereo.compute(grayL,grayR).astype(np.float32)/ 16.0 
    dispR= stereo.compute(grayR,grayL).astype(np.float32)/ 16.0
    # 使用滤波器降噪,filteredimg为过滤后的深度图
    filteredImg= wls_filter.filter(dispL,grayL,None,dispR)
    filteredImg = cv2.normalize(src=filteredImg, dst=filteredImg, beta=0, alpha=255, 
                                norm_type=cv2.NORM_MINMAX)
    filteredImg = np.uint8(filteredImg)

    points = cv2.reprojectImageTo3D(dispL,Q)

    cv2.imshow('filteredImg',filteredImg)
    cv2.imshow('disparity', (dispL-min_disp)/num_disp)
    
    if cv2.waitKey(0) & 0xFF == ord(' '):
        continue
    if cv2.waitKey(0) & 0xFF == 27:
        break

cv2.waitKey(0)
cv2.destroyAllWindows()

希望能帮到大家,最好有大佬能帮我解决一个问题,以上代码都是网上搜集自己整理,我也不知道是谁的了,就感谢大家所有人!

  • 12
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
<项目介绍> 该资源内项目源码是个人的毕设,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到94.5分,放心下载使用! 该资源适合计算机相关专业(如人工智能、通信工程、自动化、软件工程等)的在校学生、老师或者企业员工下载,适合小白学习或者实际项目借鉴参考! 当然也可作为毕业设计、课程设计、课程作业、项目初期立项演示等。如果基础还行,可以在此代码基础之上做改动以实现更多功能。 双目测距理论及其python运用 一、双目测距基本流程 Stereo Vision, 也叫双目立体视觉,它的研究可以帮助我们更好的理解人类的双眼是如何进行深度感知的。双目视觉在许多领域得到了应用,例如城市三维重建、3D模型构建(如kinect fusion)、视角合成、3D跟踪、机器人导航(自动驾驶)、人类运动捕捉(Microsoft Kinect)等等。双目测距也属于双目立体视觉的一个应用领域,双目测距的基本原理主要是三角测量原理,即通过视差来判定物体的远近。 那么总结起来,双目测距的大致流程就是: **双目标定 --> 立体校正(含消除畸变) --> 立体匹配 --> 视差计算 --> 深度计算(3D坐标)计算** linux下安装opencv-python: ```python pip install opencv-python ``` 二、相机畸变 光线经过相机的光学系统往往不能按照理想的情况投射到传感器上,也就是会产生所谓的畸变。畸变有两种情况:一种是由透镜形状引起的畸变称之为径向畸变。在针孔模型中,一条直线投影到像素平面上还是一条直线。可是,在实际拍摄的照片中,摄像机的透镜往往使得真实环境中的一条直线在图片中变成了曲线。越靠近图像的边缘,这种现象越明显。由于实际加工制作的透镜往往是中心对称的,这使得不规则的畸变通常径向对称。它们主要分为两大类,桶形畸变 和 枕形畸变(摘自《SLAM十四讲》)如图所示: <div align=center><img src="https://img-blog.csdnimg.cn/20190907184815326.PNG" width="324" height="100" /></div> 桶形畸变是由于图像放大率随着离光轴的距离增加而减小,而枕形畸变却恰好相反。 在这两种畸变中,穿过图像中心和光轴有交点的直线还能保持形状不变。
双目相机的标定可以使用Python中的OpenCV库来实现。具体步骤如下: 1.采集双目图像,保证左右相机的视野重叠。 2.对采集到的图像进行角点检测,可以使用OpenCV中的findChessboardCorners函数。 3.对检测到的角点进行亚像素级别的精确化处理,可以使用cornerSubPix函数。 4.利用检测到的角点进行双目相机的标定,可以使用OpenCV中的stereoCalibrate函数。 5.对标定结果进行评估,可以使用reprojectionError函数。 下面是一个简单的Python代码示例: ``` import cv2 # 读取左右相机的图像 left_img = cv2.imread('left.jpg') right_img = cv2.imread('right.jpg') # 设置棋盘格的大小 board_size = (9, 6) # 检测角点 left_ret, left_corners = cv2.findChessboardCorners(left_img, board_size, None) right_ret, right_corners = cv2.findChessboardCorners(right_img, board_size, None) # 精确化角点位置 criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) cv2.cornerSubPix(left_img, left_corners, (11, 11), (-1, -1), criteria) cv2.cornerSubPix(right_img, right_corners, (11, 11), (-1, -1), criteria) # 进行双目相机标定 criteria_stereo = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) flags = cv2.CALIB_FIX_INTRINSIC ret, mtx_left, dist_left, mtx_right, dist_right, R, T, E, F = cv2.stereoCalibrate([left_corners], [right_corners], board_size, None, None, None, None, criteria_stereo, flags) # 评估标定结果 mean_error = cv2.reprojectionError([left_corners], [right_corners], R, T, mtx_left, dist_left, mtx_right, dist_right, F) print("双目相机标定完成,平均重投影误差为:", mean_error) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值