相机标定实战之双目标定

news/2024/5/20 11:09:09/文章来源:https://blog.csdn.net/zyq880625/article/details/131192892

相机标定原理


文章目录

  • 相机标定原理
  • 前言
  • 一、采集图像
  • 二、基于Matlab单双目标定流程
    • 采集棋盘图
  • 三、基于OpenCV-Python双目标定流程
    • 检测棋盘格角点
    • 对角点进行亚像素精细化
    • 单目标定
    • 双目标定
    • 双目校正
    • 保存标定参数
    • 读取标定参数
    • 代码示例
  • 参考


前言

相机标定可以说是计算机视觉/机器视觉的基础,也是面试过程中经常出现的问题。相机标定涉及的知识面很广,成像几何、镜头畸变、单应矩阵、非线性优化等。在双目测距系统中,相机标定能消除畸变,进行立体校正,从而提高视差计算的准确性,这样才能得到精确的深度图。

首先需要准备一张棋盘,如下图所示。对于标定不同测距范围相机所用的棋盘方格宽度会有所不同。对于短焦双目相机(测距范围在20m以内),棋盘中方格的宽度达到20mm即可;对于长焦双目相机(测距范围在40m左右),棋盘中方格的宽度需要尽量大,否则会影响标定的精度,一般至少达到60mm。
在这里插入图片描述

尽量拍摄多组照片,这样可以提高标定效果,标定效果的好坏直接影响到测距的精度。对于短焦相机通常拍摄40组照片即可;长焦相机通常会需要更多组照片,标定长焦相机时拍摄了60组。

一、采集图像

如果使用的是左右图像拼接1个USB接口输出的双目摄像头,在采集图像时需要注意其图像拼接的顺序,例如我使用的双目摄像头拼接顺序从左至右为右目——左目(不知道是不是都是这么拼接的,还是厂家搞错了),这个问题处理不好可能会导致后面的处理结果都不对,所以这里特别说明一下。
在这里插入图片描述

import os
import argparse
import cv2class StereoCamera(object):"""采集双目标定图片,按键盘【c】或【s】保存图片"""def __init__(self, chess_width, chess_height, detect=False):""":param chess_width: chessboard width size,即棋盘格宽方向黑白格子相交点个数,:param chess_height: chessboard height size,即棋盘格长方向黑白格子相交点个数:param detect: 是否实时检测棋盘格,方便采集数据"""self.chess_width = chess_widthself.chess_height = chess_heightself.detect = detectself.criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)def detect_chessboard(self, image):"""检测棋盘格并显示"""gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)ret, corners = cv2.findChessboardCorners(gray, (self.chess_width, self.chess_height), None)if ret:# 角点精检测corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), self.criteria)# Draw and display the cornersimage = cv2.drawChessboardCorners(image, (self.chess_width, self.chess_height), corners2, ret)return imagedef capture2(self, left_video, right_video, save_dir):"""用于采集双USB连接线的双目摄像头:param left_video:int or str,左路视频路径或者摄像头ID:param right_video:int or str,右视频路径或者摄像头ID:param save_dir: str,保存左右图片的路径:return:"""self.create_file(save_dir)capL = cv2.VideoCapture(left_video)capR = cv2.VideoCapture(right_video)widthL, heightL, numFramesL, fpsL = self.get_video_info(capL)widthR, heightR, numFramesR, fpsR = self.get_video_info(capR)print("capL:\n", widthL, heightL, numFramesL, fpsL)print("capR:\n", widthR, heightR, numFramesR, fpsR)save_videoL = self.create_file(save_dir, "video", "left_video.avi")save_videoR = self.create_file(save_dir, "video", "right_video.avi")writerL = self.get_video_writer(save_videoL, widthL, heightL, fpsL)writerR = self.get_video_writer(save_videoR, widthR, heightR, fpsR)i = 0while True:isuccessL, frameL = capL.read()isuccessR, frameR = capR.read()if not (isuccessL and isuccessR):print("No more frames")breakif self.detect:l = self.detect_chessboard(frameL.copy())r = self.detect_chessboard(frameR.copy())else:l = frameL.copy()r = frameR.copy()cv2.imshow('left', l)cv2.imshow('right', r)key = cv2.waitKey(10)if key == ord('q'):breakelif key == ord('c') or key == ord('s'):print("save image:{:0=3d}".format(i))cv2.imwrite(os.path.join(save_dir+"/left", "left_{:0=3d}.png".format(i)), frameL)cv2.imwrite(os.path.join(save_dir+"/right", "right_{:0=3d}.png".format(i)), frameR)i += 1writerL.write(frameL)writerR.write(frameR)capL.release()capR.release()cv2.destroyAllWindows()def capture1(self, video, save_dir):"""用于采集单USB连接线的双目摄像头(左右摄像头被拼接在同一个视频中显示):param video:int or str,视频路径或者摄像头ID:param save_dir: str,保存左右图片的路径"""self.create_file(save_dir)cap = cv2.VideoCapture(video)width, height, numFrames, fps = self.get_video_info(cap)print("capL:\n", width, height, numFrames, fps)save_videoL = self.create_file(save_dir, "video", "left_video.avi")save_videoR = self.create_file(save_dir, "video", "right_video.avi")writerL = self.get_video_writer(save_videoL, int(width / 2), height, fps)writerR = self.get_video_writer(save_videoR, int(width / 2), height, fps)i = 0while True:isuccess, frame = cap.read()if not isuccess:print("No more frames")break# 分离左右摄像头# 注意:需要根据摄像头画面分割左右图像frameL = frame[:, int(width / 2):, :]frameR = frame[:, :int(width / 2), :]if self.detect:l = self.detect_chessboard(frameL.copy())r = self.detect_chessboard(frameR.copy())else:l = frameL.copy()r = frameR.copy()cv2.imshow('left', l)cv2.imshow('right', r)key = cv2.waitKey(10)if key == ord('q'):breakelif key == ord('c') or key == ord('s'):print("save image:{:0=3d}".format(i))cv2.imwrite(os.path.join(save_dir+"/left", "left_{:0=3d}.png".format(i)), frameL)cv2.imwrite(os.path.join(save_dir+"/right", "right_{:0=3d}.png".format(i)), frameR)i += 1writerL.write(frameL)writerR.write(frameR)cap.release()cv2.destroyAllWindows()@staticmethoddef get_video_info(video_cap):width = int(video_cap.get(cv2.CAP_PROP_FRAME_WIDTH))height = int(video_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))numFrames = int(video_cap.get(cv2.CAP_PROP_FRAME_COUNT))fps = int(video_cap.get(cv2.CAP_PROP_FPS))return width, height, numFrames, fps@staticmethoddef get_video_writer(save_path, width, height, fps):if not os.path.exists(os.path.dirname(save_path)):os.makedirs(os.path.dirname(save_path))fourcc = cv2.VideoWriter_fourcc(*'XVID')frameSize = (int(width), int(height))video_writer = cv2.VideoWriter(save_path, fourcc, fps, frameSize)print("video:width:{},height:{},fps:{}".format(width, height, fps))return video_writer@staticmethoddef create_file(parent_dir, dir1=None, filename=None):out_path = parent_dirif dir1:out_path = os.path.join(parent_dir, dir1)if not os.path.exists(out_path):os.makedirs(out_path)if filename:out_path = os.path.join(out_path, filename)return out_pathdef get_parser():width = 12height = 8left_video = 0right_video = -1save_dir = "data/"parser = argparse.ArgumentParser(description='Camera calibration')parser.add_argument('--detect', type=bool, default=True, help='detect corner')parser.add_argument('--width', type=int, default=width, help='chessboard width size')parser.add_argument('--height', type=int, default=height, help='chessboard height size')parser.add_argument('--left_video', type=int, default=left_video, help='left video file or camera ID')parser.add_argument('--right_video', type=int, default=right_video, help='right video file or camera ID')parser.add_argument('--save_dir', type=str, default=save_dir, help='YML file to save calibrate matrices')return parserif __name__ == '__main__':args = get_parser().parse_args()stereo = StereoCamera(args.width, args.height, detect=args.detect)if args.left_video > -1 and args.right_video > -1:# 双USB连接线的双目摄像头stereo.capture2(left_video=args.left_video, right_video=args.right_video, save_dir=args.save_dir)elif args.left_video > -1:# 单USB连接线的双目摄像头(左右摄像头被拼接在同一个视频中显示)stereo.capture1(video=args.left_video, save_dir=args.save_dir)else:raise Exception("Error: Check your camera{}".format(args.left_video, args.right_video))

二、基于Matlab单双目标定流程

采集棋盘图

尽量让棋盘占据照片中最多的画面(不小于1\3)
尽量让棋盘格出现在图像中的各个位置(尤其图像的四个角)
拍摄的棋盘图是具有多个角度的(前倾,后倾,左倾,右倾,斜倾等)
拍摄的棋盘图要清晰可辩,最好是在最清晰的范围内拍摄
拍摄照片数量要多一点,推荐20张以上

https://blog.csdn.net/leonardohaig/article/details/81254179

三、基于OpenCV-Python双目标定流程

双目标定的目的是获取左右目相机的内参矩阵、畸变向量、旋转矩阵和平移矩阵。除了Matlab的标定工具箱之外,OpenCV同样也实现了张友正标定法,而我们只需要调用相关的函数即可对相机进行标定。
双目相机标定步骤:

检测棋盘格角点

retL, cornersL = cv2.findChessboardCorners(ChessImaL,(self.width, self.height), cv2.CALIB_CB_ADAPTIVE_THRESH | cv2.CALIB_CB_FILTER_QUADS)  # 提取左图每一张图片的角点
retR, cornersR = cv2.findChessboardCorners(ChessImaR,(self.width, self.height), cv2.CALIB_CB_ADAPTIVE_THRESH | cv2.CALIB_CB_FILTER_QUADS)  # 提取右图每一张图片的角点

对角点进行亚像素精细化

criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
cv2.cornerSubPix(ChessImaL, cornersL, (11, 11), (-1, -1), criteria)  # 亚像素精确化,对粗提取的角点进行精确化
cv2.cornerSubPix(ChessImaR, cornersR, (11, 11), (-1, -1), criteria)  # 亚像素精确化,对粗提取的角点进行精确化

单目标定

#   左侧相机单独标定
retL, K1, D1, rvecsL, tvecsL = cv2.calibrateCamera(objpoints,imgpointsL,ChessImaL.shape[::-1], None, None)
#   右侧相机单独标定
retR, K2, D2, rvecsR, tvecsR = cv2.calibrateCamera(objpoints,imgpointsR,ChessImaR.shape[::-1], None, None)

双目标定

criteria_stereo = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-5)flags = 0
flags |= cv2.CALIB_FIX_INTRINSIC# 内参、畸变系数、平移向量、旋转矩阵
retS, K1, D1, K2, D2,  R, T, E, F = cv2.stereoCalibrate(objpoints,imgpointsL,imgpointsR,K1,D1,K2,D2,ChessImaR.shape[::-1], criteria_stereo,flags)

我们要注意函数中的flags

CV_CALIB_FIX_INTRINSIC:固定K和D矩阵。这是默认标志。如果你校准好你的相机,那就只求解𝑅,𝑇,𝐸,𝐹。
CV_CALIB_USE_INTRINSIC_GUESS: K和D个矩阵将被优化。对于这个计算,你应该给出经过良好校准的矩阵,以便(可能)得到更好的结果。
CV_CALIB_FIX_PRINCIPAL_POINT: 修复K矩阵中的参考点。
CV_CALIB_FIX_FOCAL_LENGTH: 在K矩阵中固定焦距。
CV_CALIB_FIX_ASPECT_RATIO: 固定长宽比。
CV_CALIB_SAME_FOCAL_LENGTH: 校准焦距,并设置Fx和Fy相同的校准结果。
CV_CALIB_ZERO_TANGENT_DIST: 去掉畸变。
CV_CALIB_FIX_K1,, CV_CALIB_FIX_K6: 移除K1到K6的畸变。

双目校正

双目校正的目的是得到立体校正所需的映射矩阵,然后对图像进行畸变校正和立体校正(极线校正)。立体校正最常见的校正方法就是Bouguet极线校正方法。
Bouguet极线校正方法:左右相机成像平面各旋转一半,使得左右图像重投影造成的误差最小,左右视图的共同面积最大。

# 左校正变换矩阵、右校正变换矩阵、左投影矩阵、右投影矩阵、深度差异映射矩阵
R_l,R_r,P_l,P_r,Q, roi_left, roi_right = cv2.stereoRectify(K1, D1, K2, D2,(width, height),R, T,flags=cv2.CALIB_ZERO_DISPARITY, alpha=0.9)

在这个函数中,只有一个标志CALIB_ZERO_DISPARITY,它用于匹配图像之间的y轴。alpha值用于转换后的黑色部分,因为图像会旋转,而显示的图像大小不会改变,所以一些图像边缘部分会是黑色的,而原始图像会小得多:

alpha= -1: 让 OpenCV 优化黑色部分。
alpha= 0 : 旋转和裁切图像,使没有黑色的部分。这个选项在大多数情况下会严重削减图像,你不会得到一个像样的高质量的图像,但可以一试。
alpha= 1 : 进行变换,但不要裁切任何部分。
alpha= experimental: 尝试设置不同的值,在某个特定的alpha值,可能有一些黑色的区域,但图像质量整体高。
# 计算畸变矫正和立体校正的映射变换。
map_lx, map_ly = cv2.initUndistortRectifyMap(K1, D1, R_l, P_l,(width,height),cv2.CV_32FC1)
map_rx, map_ry = cv2.initUndistortRectifyMap(K2, D2, R_r, P_r, (width,height),cv2.CV_32FC1)# 得到畸变校正和立体校正后的图像
rec_img_L = cv2.remap(imgL,map_lx, map_ly,  cv2.INTER_LINEAR, cv2.BORDER_CONSTANT)  # 使用remap函数完成映射
rec_img_R = cv2.remap(imgR,map_rx, map_ry,  cv2.INTER_LINEAR, cv2.BORDER_CONSTANT)

initUndistortRectifyMap函数可以同时实现图像的畸变校正和校准。对于左相机,我们使用K1(相机矩阵)和D1(失真矩阵)进行畸变校正,使用R1(从左到右旋转)和P1(从左到右投影矩阵)进行校正。在对remap进行变换后,我们将得到修正后的图像。对于右相机,我们会用相同的步骤做一遍。至此双目校正部分就完成了。
   标定效果如下所示。可以看到原图中左右图像存在畸变,并且棋盘格位置明显未对齐;校正后左右图像畸变被消除,且极线对齐效果明显。
在这里插入图片描述
在这里插入图片描述

保存标定参数

为方便查看和读取,首先将需要保存的参数放到一个空字典中,然后保存为json文件。如下所示:

params_dict = {}
params_dict['size']        = [width, height]
params_dict['K1']          = left_K.tolist()
params_dict['D1']          = left_D.tolist()
params_dict['K2']          = right_K.tolist()
params_dict['D2']          = right_D.tolist()
params_dict['left_map_x']  = map_lx.tolist()
params_dict['left_map_y']  = map_ly.tolist()
params_dict['right_map_x'] = map_rx.tolist()
params_dict['right_map_y'] = map_ry.tolist()
params_dict['R']           = R.tolist()
params_dict['T']           = T.tolist()
params_dict['Q']           = Q.tolist()# 保存为.json文件
file_path = args.save_dir + args.file_name + ".json"with open(file_path,"w") as f:json.dump(params_dict, f, indent=1)

读取标定参数

# 读取.json文件
with open(params_file, "r") as f:dict = json.load(f)for d in dict:dict[d] = np.asarray(dict[d], "f")

代码示例

#-*- coding:utf-8 -*-
import os
import numpy as np
import cv2
import glob
import argparseimport json
import pickleclass Stereo_Camera_Calibration(object):def __init__(self, width, height, lattice):self.width       = width         # 棋盘格宽方向黑白格子相交点个数self.height      = height       # 棋盘格长方向黑白格子相交点个数self.lattice     = lattice# 设置迭代终止条件self.criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)self.criteria_stereo = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-5)# =========================== 双目标定 =============================== #def stereo_calibration(self, file_L, file_R):# 设置 object points, 形式为 (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)objp = np.zeros((self.width * self.height, 3), np.float32)  #我用的是6×7的棋盘格,可根据自己棋盘格自行修改相关参数objp[:, :2] = np.mgrid[0:self.width, 0:self.height].T.reshape(-1, 2)objp       *= self.lattice # 用arrays存储所有图片的object points 和 image pointsobjpoints = []  # 3d points in real world spaceimgpointsR = []  # 2d points in image planeimgpointsL = []for i in range(len(file_L)):  ChessImaL = cv2.imread(file_L[i],0)  # 左视图ChessImaR = cv2.imread(file_R[i],0)  # 右视图retL, cornersL = cv2.findChessboardCorners(ChessImaL,(self.width, self.height), cv2.CALIB_CB_ADAPTIVE_THRESH | cv2.CALIB_CB_FILTER_QUADS)  # 提取左图每一张图片的角点retR, cornersR = cv2.findChessboardCorners(ChessImaR,(self.width, self.height), cv2.CALIB_CB_ADAPTIVE_THRESH | cv2.CALIB_CB_FILTER_QUADS)  # 提取右图每一张图片的角点if (True == retR) & (True == retL):objpoints.append(objp)cv2.cornerSubPix(ChessImaL, cornersL, (11, 11), (-1, -1), self.criteria)  # 亚像素精确化,对粗提取的角点进行精确化cv2.cornerSubPix(ChessImaR, cornersR, (11, 11), (-1, -1), self.criteria)  # 亚像素精确化,对粗提取的角点进行精确化imgpointsL.append(cornersL)imgpointsR.append(cornersR)# ret_l = cv2.drawChessboardCorners(ChessImaL, (self.width, self.height), cornersL, retL)# cv2.imshow(file_L[i], ChessImaL)# cv2.waitKey()# ret_r = cv2.drawChessboardCorners(ChessImaR, (self.width, self.height), cornersR, retR)# cv2.imshow(file_R[i], ChessImaR)# cv2.waitKey(500)# 相机的单双目标定、及校正#   左侧相机单独标定retL, K1, D1, rvecsL, tvecsL = cv2.calibrateCamera(objpoints,imgpointsL,ChessImaL.shape[::-1], None, None)#   右侧相机单独标定retR, K2, D2, rvecsR, tvecsR = cv2.calibrateCamera(objpoints,imgpointsR,ChessImaR.shape[::-1], None, None)# --------- 双目相机的标定 ----------#flags = 0flags |= cv2.CALIB_FIX_INTRINSIC         # K和D个矩阵是固定的。这是默认标志。如果你校准好你的相机,只求解𝑅,𝑇,𝐸,𝐹。#flags |= cv2.CALIB_FIX_PRINCIPAL_POINT  # 修复K矩阵中的参考点。# flags |= cv2.CALIB_USE_INTRINSIC_GUESS    # K和D个矩阵将被优化。对于这个计算,你应该给出经过良好校准的矩阵,以便(可能)得到更好的结果。#flags |= cv2.CALIB_FIX_FOCAL_LENGTH      # 在K矩阵中固定焦距。# flags |= cv2.CALIB_FIX_ASPECT_RATIO     # 固定长宽比。#flags |= cv2.CALIB_ZERO_TANGENT_DIST     # 去掉畸变。# 内参、畸变系数、平移向量、旋转矩阵retS, K1, D1, K2, D2,  R, T, E, F = cv2.stereoCalibrate(objpoints,imgpointsL,imgpointsR,K1,D1,K2,D2,ChessImaR.shape[::-1], self.criteria_stereo,flags)# 左内参矩阵、左畸变向量、右内参矩阵、右畸变向量、旋转矩阵、平移矩阵return K1, D1, K2, D2, R, T# ==================================================================== ## =========================== 双目校正 =============================== ## 获取畸变校正、立体校正、重投影矩阵def getRectifyTransform(self, width,height,K1 ,D1 ,K2 ,D2 , R, T):#得出进行立体矫正所需要的映射矩阵 # 左校正变换矩阵、右校正变换矩阵、左投影矩阵、右投影矩阵、深度差异映射矩阵R_l,R_r,P_l,P_r,Q, roi_left, roi_right = cv2.stereoRectify(K1, D1, K2, D2,(width, height),R, T,flags=cv2.CALIB_ZERO_DISPARITY, alpha=0)# # 标志CALIB_ZERO_DISPARITY,它用于匹配图像之间的y轴# 计算畸变矫正和立体校正的映射变换。map_lx, map_ly = cv2.initUndistortRectifyMap(K1, D1, R_l, P_l, (width,height),cv2.CV_32FC1)map_rx, map_ry = cv2.initUndistortRectifyMap(K2, D2, R_r, P_r, (width,height),cv2.CV_32FC1)return map_lx, map_ly,map_rx, map_ry, Q# 得到畸变校正和立体校正后的图像def get_rectify_img(self, imgL, imgR,map_lx, map_ly,map_rx, map_ry):rec_img_L = cv2.remap(imgL,map_lx, map_ly,  cv2.INTER_LINEAR, cv2.BORDER_CONSTANT)  # 使用remap函数完成映射rec_img_R = cv2.remap(imgR,map_rx, map_ry,  cv2.INTER_LINEAR, cv2.BORDER_CONSTANT)return rec_img_L, rec_img_R# 立体校正检验——极线对齐def draw_line(self, rec_img_L,rec_img_R):#建立输出图像width  = max(rec_img_L.shape[1],rec_img_R.shape[1])height = max(rec_img_L.shape[0],rec_img_R.shape[0])output = np.zeros((height,width*2,3),dtype=np.uint8)output[0:rec_img_L.shape[0],0:rec_img_L.shape[1]] = rec_img_Loutput[0:rec_img_R.shape[0],rec_img_L.shape[1]:]  = rec_img_R# 绘制等间距平行线line_interval = 50  # 直线间隔:50for k in range(height // line_interval):cv2.line(output, (0, line_interval * (k + 1)), (2 * width, line_interval * (k + 1)), (0, 255, 0), thickness=2, lineType=cv2.LINE_AA)return output # 可显示的图像 # ===================================================================== #def get_parser():parser = argparse.ArgumentParser(description='Camera calibration')parser.add_argument('--width', type=int, default=12, help='chessboard width size')parser.add_argument('--height', type=int, default=8, help='chessboard height size')parser.add_argument('--lattice', type=float, default=12.5, help='lattice length')parser.add_argument('--image_dir', type=str, default="data/", help='images path')parser.add_argument('--save_dir', type=str, default="config/", help='path to save file')parser.add_argument('--file_name', type=str, default="camera_params", help='camera params save file')return parserdef get_file(path):          #获取文件路径img_path = []for root, dirs, files in os.walk(path):for file in files:img_path.append(os.path.join(root,file))return img_pathif __name__ == "__main__":args = get_parser().parse_args()params_dict = {}file_L = get_file(args.image_dir + 'left')file_R = get_file(args.image_dir + 'right')imgL = cv2.imread(file_L[2])imgR = cv2.imread(file_R[2])height, width = imgL.shape[0:2]calibration = Stereo_Camera_Calibration(args.width, args.height, args.lattice)left_K,left_D, right_K, right_D, R, T = calibration.stereo_calibration(file_L, file_R)map_lx, map_ly,map_rx, map_ry, Q = calibration.getRectifyTransform(width,height,left_K,left_D,right_K, right_D, R, T)# 查看校正效果img_ = calibration.draw_line(imgL,imgR)cv2.imshow("img",img_)rec_img_L, rec_img_R = calibration.get_rectify_img(imgL,imgR,map_lx, map_ly,map_rx, map_ry)img_show = calibration.draw_line(rec_img_L,rec_img_R)cv2.imshow("output",img_show)cv2.waitKey(0)params_dict['size']        = [width, height]params_dict['K1']          = left_K.tolist()params_dict['D1']          = left_D.tolist()params_dict['K2']          = right_K.tolist()params_dict['D2']          = right_D.tolist()params_dict['map_lx']      = map_lx.tolist()params_dict['map_ly']      = map_ly.tolist()params_dict['map_rx']      = map_rx.tolist()params_dict['map_ry']      = map_ry.tolist()params_dict['R']           = R.tolist()params_dict['T']           = T.tolist()params_dict['Q']           = Q.tolist()# =========== 保存相机参数 =========== # # 保存为.json文件file_path = args.save_dir + args.file_name + ".json"with open(file_path,"w") as f:json.dump(params_dict, f, indent=1)print("ALL Make Done!")

参考

https://ww2.mathworks.cn/help/vision/ug/stereo-camera-calibrator-app.html
https://blog.csdn.net/baidu_39231810/article/details/128628651

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_694639.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【链表复习】C++ 链表复习及题目解析 (2)

目录 牛客 CM11 链表分割 牛客 OR36 之链表的回文结构 Leetcode 160. 相交链表 LeetCode 141. 环形链表 LeetCode 138. 复制带随机指针的链表 本文继续延续前文,为大家带来几道经典的链表中等难度的题目。 牛客 CM11 链表分割 现有一链表的头指针 ListNode* p…

7--Gradle进阶 - settings.gradle的文件说明

7--Gradle进阶 - settings.gradle的文件说明 前言 介绍 settings.gradle 文件之前,先来说明一下,settings.gradle 主要是用来多模块工程使用的。 所以我们先来创建一个多模块的工程。 多模块工程创建 1. 创建 root 工程 1.1 配置本地 Gradle 1.2 配置依赖…

怎么把图片放大不改变清晰度,给大家介绍两个方法

时代的发展和进步,我们在使用手机、电脑等设备时,常常需要对图片进行放大操作。从功能上来说,图片放大可以让我们更好地观看和理解图片内容,同时也可以提高图像分辨率和清晰度,以满足不同的需求和场景首先,…

WDM波分复用技术:TFF(薄膜滤波) AWG(阵列波导光栅)介绍

WDM (Wavelength Division Multiplexing)技术是通过在光纤中传输多个不同波长的光信号来扩大光纤传输带宽并提高网络传输能力的一种技术,而TFF(薄膜滤波)和AWG(阵列波导光栅)则是两种常用的WDM技术。 TFF技术 TFF &a…

object类clone、finalize

2 什么是API API(Application Programming Interface,应用程序接口)是一些预先定义的函数。目的是提供应用程序与开发人员基于某软件可以访问的一些功能集,但又无需访问源码或理解内部工作机制的细节. API是一种通用功能集,有时公…

自动驾驶专题介绍 ———— 激光雷达标定

文章目录 介绍激光雷达与激光雷达之间的外参标定激光雷达与摄像头的标定 介绍 激光雷达在感知、定位方面发挥着重要作用。跟摄像头一样,激光雷达也是需要进行内外参数标定的。内参标定是指内部激光发射器坐标系与雷达自身坐标系的转换关系,在出厂之前就已…

【道友避坑】CUB数据集转yolov5格式

写在前面:最近我拿到一个CUB_200_2011鸟类训练模型,但是我想将他转为yolov的格式进行应用。看了些其他博主博客后,发现跳跃性有些强。再此记录转换过程,希望各位道友修得此法后,能有所收获! 一、获取数据集…

为什么年龄越大工作失误越多水平越低能力越差-个人案例

此为内容创作模板,在发布之前请将不必要的内容删除 在日复一日的工作中,我们免不了会产生一些失误,会因此感到沮丧和失望。但如何正确地对待和处理这些失误才是最重要的,它直接影响到我们的工作表现和个人成长。一起来谈谈作为职…

信贷产品的贷前获客营销策略搭建

在竞争激烈的信贷市场中,有效的贷前获客营销策略对于吸引潜在借款人、提高转化率以及保持客户忠诚度至关重要。本文将分享一些关于信贷产品贷前获客营销策略搭建的基本框架和经验分享,希望能对大家有所启发。 1、市场调研和目标客户定义 在制定贷前获客…

使用Unity开发一个游戏类型的区块链 [独立区块链]

ArouseBlockchain [Unity独立区块链] 这是一个学习性质的项目,使用了Unity进行独立区块链游戏的开发。 徽章维护者如何贡献使用许可 项目说明 关于本项目的使用说明 背景安装使用说明 生成器 区块链简述 区块链的基础知识简述 背景 未来趋势 区块链未来趋势的…

【什么是iMessage推送,im群发】苹果推iMessage是苹果公司为其设备用户提供的即时通讯服务

iMessage是苹果公司为其设备用户提供的即时通讯服务,拥有一系列强大的功能和特点。然而,至今为止,苹果并未提供官方的群发部署功能。iMessage主要被设计为点对点的通信工具,即用户可以与一个或多个人进行私密的聊天对话。以下是关…

VMware Workstation 17 的安装

一、简介 VMware Workstation 17.0是一款功能非常强大的虚拟机,可以帮助用户在Windows系统上同时开启多个系统,不仅能在虚拟机上安装上不同的操作系统,比如Mac、Linux以及Windows10/11等,还能与云技术和容器技术(如 D…

SpringCloud Eureka注册服务提供者(七)

这里我们在原来的服务提供者项目 microservice-student-provider-1001 上面直接修改&#xff1a; 首先pom.xml修改&#xff0c;加上eureka客户端依赖&#xff1a; <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>…

1.7C++流插入运算符重载

C流插入运算符重载 在 C 中&#xff0c;流插入运算符&#xff08;<<&#xff09;用于输出数据到流中的运算符&#xff0c;流插入运算符可以被重载&#xff0c;使得程序员可以自定义输出对象的方式。 重载流插入运算符的一般形式如下&#xff1a; 其中&#xff0c;T 是…

运维(SRE)成长之路-第1天 搭建虚拟机(图示)

1.Linux安装前准备 虚拟机&#xff1a;用软件&#xff08;如&#xff1a;vmware,virtualbox等&#xff09;模拟硬件,方便实验的灵活配置 虚拟化软件&#xff0c;建议使用 Vmware Workstation 虚拟硬件配置 CPU&#xff1a;2核或更多 内存&#xff1a;1G以上&#xff0c;推荐2…

天线设计中的磁介质材料 探索可重构潜力

​from&#xff1a;IEEE Antennas & Propagation Magazine (Vol. 61 / No. 1 / Feb. 2019, pp:29-40) -- 文 前 -- 这篇文章针对铁氧体在外置磁场下磁导率发生变化这个特点&#xff0c;探讨铁氧体在可重构天线中的应用。文中对铁氧体材料的选择&#xff0c;磁导率数学模型…

Linux系统的tty架构及UART驱动详解

​一、模块硬件学习 1.1. Uart介绍 通用异步收发传输器&#xff08;Universal Asynchronous Receiver/Transmitter)&#xff0c;通常称为UART&#xff0c;是一种异步收发传输器&#xff0c;是电脑硬件的一部分。它将要传输的资料在串行通信与并行通信之间加以转换。 作为把并…

基于Hexo和Butterfly创建个人技术博客,(5) 使用Hexo的Tags Plugin插件增强博客文章内容和视觉表现力

Hexo官司网查看 这里 注意&#xff1a; Tags语法是Hexo插件提供的&#xff0c;是非标准语言&#xff0c;写文章时要注意以下几点&#xff1a; 用于在文章中快速插入特定的内容&#xff0c;作用等同于其它语言&#xff0c;可理解为一种增强版本的markdown&#xff1b;可混合Mark…

嵌入式软件开发岗位----求职过程记录(基础知识和面经总结)

1、本栏用来记录社招找工作过程中的内容&#xff0c;包括基础知识以及面试问题等&#xff0c;以便于后续个人回顾学习&#xff1b; 暂时只有2023年3月份&#xff0c;第一次社招找工作的过程&#xff1b; 2、个人经历&#xff1a; 研究生期间课题是SLAM在无人机上的应用&#xf…

Elastic 8.8 版引入了全新的 Learned Sparse Encoder 模型,并宣布正式推出合成监测

作者&#xff1a;Brian Bergholm 2023年5月25日 今天&#xff0c;我们非常高兴地宣布 Elastic 8.8 版正式发布。 新增功能 Elastic 企业搜索可帮助开发人员利用 Elasticsearch 实现强大的现代搜索和发现体验。 请在 “Elastic 企业搜索亮点” 博文或 8.8 版发行说明中&#…