相机标定:
教程:
https://docs.opencv.org/3.4/d9/dab/tutorial_homography.html
https://docs.opencv.org/3.4/examples.html
https://docs.opencv.org/3.4/d3/d81/tutorial_contrib_root.html
http://www.technolabsz.com/2012/07/camera-calibration-using-opencv.html
https://github.com/warp1337/opencv_cam_calibration/blob/master/src/camera_calibration.cpp
https://github.com/warp1337/opencv_cam_calibration
https://docs.opencv.org/2.4/doc/tutorials/calib3d/camera_calibration/camera_calibration.html
相机校准和3D重建
相机校准
https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_calib3d/py_calibration/py_calibration.html
姿态估计
https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_calib3d/py_pose/py_pose.html
对极几何
https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_calib3d/py_epipolar_geometry/py_epipolar_geometry.html
立体图像的深度图
https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_calib3d/py_depthmap/py_depthmap.html
彩色图对齐深度图:::
深度图像配准(Registration)原理
https://www.cnblogs.com/cv-pr/p/5769617.html
intel realsense SR300 深度图像和彩色图像对齐
https://blog.csdn.net/jay463261929/article/details/53582800
Kinect深度图与RGB摄像头的标定与配准
https://blog.csdn.net/aichipmunk/article/details/9264703
补充我写的一些:
# coding:utf-8 import cv2 import numpy as np import matplotlib.pyplot as plt import json import os #为深度图像中的每一个像素附上对应的RGB颜色: 深度图坐标系转换到RGB坐标系 #为RGB图像中的每一个像素附上对应的深度: RGB坐标系转换到深度图坐标系 ''' -0.991604 -0.126243 -0.027989 214.997275 -0.122135 0.985481 -0.117944 133.474350 0.042472 -0.113535 -0.992626 407.073850 0.000000 0.000000 0.000000 1.000000 ''' R_dep = np.array([[-0.991604, -0.126243, -0.027989], [-0.122135, 0.985481, -0.117944], [0.042472, -0.113535, -0.992626]]) t_dep = np.array([[214.997275,133.474350,407.073850]]).T ''' -0.990949 -0.125081 -0.048724 189.587175 -0.118729 0.986054 -0.116626 194.357782 0.062633 -0.109786 -0.991980 383.686418 0.000000 0.000000 0.000000 1.000000 ''' R_rgb = np.array([[-0.990949, -0.125081, -0.048724], [-0.118729, 0.986054, -0.116626], [0.062633, -0.109786, -0.991980]]) t_rgb = np.array([[189.587175,194.357782,383.686418]]).T R_dep = np.matrix(R_dep) t_dep = np.matrix(t_dep) R_rgb = np.matrix(R_rgb) t_rgb = np.matrix(t_rgb) print("R_dep\n", R_dep, "\n") print("t_dep\n", t_dep, "\n") print("R_rgb\n", R_rgb, "\n") print("t_rgb\n", t_rgb, "\n") #为深度图像中的每一个像素附上对应的RGB颜色: 深度图坐标系转换到RGB坐标系 #所有旋转矩阵都是正交阵,因此可用转置运算代替求逆运算 #R_rgb2dep = R_rgb * R_dep.I R_dep2rgb = R_rgb * R_dep.T T_dep2rgb = t_rgb - (R_dep2rgb * t_dep) print("R_rgb2dep\n", R_dep2rgb, "\n") print("T_rgb2dep\n", T_dep2rgb, "\n") #XYZ = K_rgb * R_dep2rgb * K_dep.T * Z_dep * Point_dep + K_rgb * T_dep2rgb ''' void computeC2MC1(const Mat &R1, const Mat &tvec1, const Mat &R2, const Mat &tvec2, Mat &R_1to2, Mat &tvec_1to2) { //c2Mc1 = c2Mo * oMc1 = c2Mo * c1Mo.inv() R_1to2 = R2 * R1.t(); tvec_1to2 = R2 * (-R1.t()*tvec1) + tvec2; } Mat R_1to2, t_1to2; computeC2MC1(R1, tvec1, R2, tvec2, R_1to2, t_1to2); ''' # R2 = R_rgb # R1 = R_dep # tvec2 = t_rgb # tvec1= t_dep # # R_dep2rgb = R2 * R1.T; # T_dep2rgb = R2 * (-R1.T*tvec1) + tvec2; # # print("R_rgb2dep\n", R_dep2rgb, "\n") # print("T_rgb2dep\n", T_dep2rgb, "\n") #结果 ''' [[ 0.99978415 0.00351069 0.02047798] [-0.00348663 0.99999324 -0.00122868] [-0.02048311 0.00115602 0.9997894 ]] T_rgb2dep [[-34.16833064] [ 62.1341124 ] [-19.05218977]] ''' import pcl import pcl.pcl_visualization def depth2cloud(depth, intrinsics): # https://blog.csdn.net/renyuanxingxing/article/details/89846404 cloud = pcl.PointCloud() rows = len(depth) cols = len(depth[0]) pointcloud = [] for m in range(0, rows): for n in range(0, cols): d = depth[m][n] if d == 0: pass else: z = float(d) / intrinsics[2][2] x = (n - intrinsics[0][2]) * z / intrinsics[0][0] y = (m - intrinsics[1][2]) * z / intrinsics[1][1] points = [x, y, z] pointcloud.append(points) pointcloud = np.array(pointcloud, dtype=np.float32) cloud.from_array(pointcloud) return cloud ''' '''
//相机1 Mat rvec1, tvec1; solvePnP(objectPoints, corners1, leftcameraMatrix, leftdistCoeffs, rvec1, tvec1); //相机2 Mat rvec2, tvec2; solvePnP(objectPoints, corners2, rightcameraMatrix, rightdistCoeffs, rvec2, tvec2); //利用罗德里格斯公式,将旋转向量转为旋转矩阵 Mat R1, R2; Rodrigues(rvec1, R1); Rodrigues(rvec2, R2); //同一场景下,计算两个相机之间的RT //相机1到相机2的Rt Mat R_1to2, tvec_1to2; R_1to2 = R2 * R1.t(); tvec_1to2 = R2 * (-R1.t()*tvec1) + tvec2; std::cout<<"1to2_rgb2dep"<<std::endl; cout << fixed << setprecision(0) << R_1to2 << endl; cout << fixed << setprecision(0) << t_1to2 << endl; //相反求是否也对???? //相机2到相机1的Rt Mat R_2to1, tvec_2to1; R_2to1 = R1 * R2.t(); tvec_2to1 = R1 * (-R2.t()*tvec2) + tvec1; std::cout<<"2to1_dep2rgb"<<std::endl; cout << fixed << setprecision(0) << R_2to1 << endl; cout << fixed << setprecision(0) << t_2to1 << endl;
Kinect深度图与摄像头RGB的标定与配准(转载文章)有很多新东西
https://blog.csdn.net/aidem_brown/article/details/83713961
kinect 2.0 SDK学习笔记(四)--深度图与彩色图对齐
https://blog.csdn.net/jiaojialulu/article/details/53154045
kinect深度图与彩图匹配
https://blog.csdn.net/cocoaqin/article/details/77428100?fps=1&locationNum=3
深度摄像头与彩色摄像头的对齐
https://www.cnblogs.com/rogerjin/p/7845866.html
请教关于Kinect深度和彩色图融合的问题?
https://www.zhihu.com/question/29631310
https://devblogs.microsoft.com/cppblog/kinect-for-windows-c-samples/
- AudioBasics-D2D C++ Sample captures audio data and displays the direction of the audio source on screen.
- Background Removal Basics-D2D C++ Sample demonstrates how to use the KinectBackgroundRemoval API to separate people in the foreground from the background. This effect is similar to using a green screen technique to separate people from a green screen background.
- Coordinate Mapping Basics-D2D C++ Sample shows how to use the ColorImageStream to separate people in the foreground from the background. This effect is similar to using a green screen technique to separate people from a green screen background.
- Depth with Color-D3D C++ Sample demonstrates a 3D visualization (point cloud) using the ColorImageStream and DepthImageStream in a Direct3D sample.
- Face Tracking Visualization C++ Sample uses the Face Tracking SDK to track and visualize a single face or multiple faces.
- Skeletal Viewer C++ Sample Demonstrates Kinect NUI processing such as capturing depth stream, color video stream and skeletal tracking frames and displaying them on the screen.
国外的标定:
http://rgbdemo.org/index.php/Documentation/Calibration
http://burrus.name/index.php/Research/KinectCalibration#tocLink5
Kinect彩色图深度图配准(分辨率不一样时的处理方式):
http://blog.csdn.net/shihz_fy/article/details/43602393
ROS下的驱动与图像序列保存及opencv显示深度坐标:
http://blog.csdn.net/sunbibei/article/details/51594824
SDK获取出厂内参数代码,MATLAB 标定Kinect v2等
https://blog.csdn.net/h532600610/article/details/51800488
Corners中的角点坐标顺序排列规律不一定是以行从左上到右下。
使用坐标计算映射关系时应提高警惕,对坐标进行重新排列
标定板横纵方向点数一奇一偶,检测到的点顺序就是固定的。板不标准,无法判断起始坐标点。
总结:该函数的功能就是判断图像内是否包含完整的棋盘图,如果能够检测完全,就把他们的角点坐标按顺序(逐行,从左到右)记录下来,并返回非0数,否则返回0。这里对size参数要求非常严格,函数必须检测到相同的size才会返回非0,否则返回0,这里一定要注意。角点检测不完全时,可能画不出图像,或者画出红色角点;正确的图像后面有参考。
该函数检测的角点的坐标是不精确的,要想精确结果,需要使用cornerSubPix()函数,进行亚像素精度的调整。
recoverPose求Rt
https://python.hotexamples.com/examples/cv2/-/recoverPose/python-recoverpose-function-examples.html
https://github.com/CarlosHVMoraes/py3DRec
c++版本:https://blog.csdn.net/AIchipmunk/article/details/48157369
# coding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
import json
import os
def mkdir(path):
import os
isExists = os.path.exists(path)
if not isExists:
os.makedirs(path)
def searchCheckerboardDirFile(rootDir, garyimglist, rgbimglist, namelist, bboxflag):
for dir_or_file in os.listdir(rootDir):
filePath = os.path.join(rootDir, dir_or_file)
# 判断是否为文件
if os.path.isfile(filePath):
# 如果是文件再判断是否以.jpg结尾,不是则跳过本次循环
if bboxflag in filePath:
if os.path.basename(filePath).endswith('.png'):
if os.path.exists(filePath.replace("_IMG_Texture_8Bit.png", "_IMG_DepthMap.tif")):
nameTemp = filePath.split('/')[-1]
tempPath = filePath.split(nameTemp)[0]
tempName = tempPath.split('/')[-2]
garyimglist.append(os.path.join(tempPath, "OtherSampleFrame_IMG_Texture_8Bit.png"))
rgbimglist.append(os.path.join(tempPath, "OtherSampleFrame_IMG_Rgb.jpg"))
namelist.append(tempName)
else:
continue
# 如果是个dir,则再次调用此函数,传入当前目录,递归处理。
elif os.path.isdir(filePath):
searchCheckerboardDirFile(filePath, garyimglist, rgbimglist, namelist, bboxflag)
else:
print('not file and dir '+os.path.basename(filePath))
return garyimglist,rgbimglist, namelist
R1path = "./R2"
R1undistort = "./R2_undistort"
R1draw = "./R2_draw"
flagTemp = "-R2-"
mkdir(R1undistort)
mkdir(R1draw)
R1garyimglist = []
R1rgbimglist = []
R1namelist = []
R1garyimglist, rgbimglist, R1namelist = searchCheckerboardDirFile(R1path, R1garyimglist, R1rgbimglist, R1namelist, flagTemp)
patternSize = (9, 9)
patternH = 9
patternW = 9
# 世界坐标系中的棋盘格点,例如(0,0,0), (1,0,0), (2,0,0) ....,(8,5,0),去掉Z坐标,记为二维矩阵,认为在棋盘格这个平面上Z=0
objp = np.zeros((patternW*patternH,3), np.float32) #构造0矩阵,81行3列,用于存放角点的世界坐标
objp[:,:2] = np.mgrid[0:patternW,0:patternH].T.reshape(-1,2)# 三维网格坐标划
#棋盘格在真实世界的大小 20mm
patternDistance = 20
objp = objp * patternDistance
# 储存棋盘格角点的世界坐标和图像坐标对
objpoints = [] # 在世界坐标系中的三维点
imgpoints = [] # 在图像平面的二维点
cornersSubPix1 = []
cornersSubPix2 = []
for exR1 in range(len(R1garyimglist)):
print(exR1, "/", len(R1garyimglist), "\n")
grayTemp = cv2.imread(R1garyimglist[exR1], -1)
GRAYH, GRAYW = grayTemp.shape[:2]
rgb = cv2.imread(rgbimglist[exR1], -1)
fliprgb = np.flip(rgb).copy()
# flip 会把读入的bgr图像,转换乘rgb图像,那么等会儿的cvtColor,其实是rgb去做这个函数了,为了统一,我们把他弄回BGR
fliprgb = cv2.cvtColor(fliprgb, cv2.COLOR_RGB2BGR)
LeftImgNewgray = cv2.cvtColor(fliprgb, cv2.COLOR_BGR2GRAY)
RightImggray = cv2.cvtColor(grayTemp, cv2.COLOR_BGR2GRAY)
patternSize = (9, 9)
cornersLeft = None
cornersRight = None
test = 0
if test == 0:
retLeft, cornersLeft = cv2.findChessboardCorners(LeftImgNewgray, patternSize, cornersLeft,
cv2.CALIB_CB_ADAPTIVE_THRESH)
retRight, cornersRight = cv2.findChessboardCorners(RightImggray, patternSize, cornersRight,
cv2.CALIB_CB_ADAPTIVE_THRESH)
else:
retLeft, cornersLeft = cv2.findChessboardCorners(LeftImgNewgray, patternSize, cornersLeft,
cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FILTER_QUADS)
retRight, cornersRight = cv2.findChessboardCorners(RightImggray, patternSize, cornersRight,
cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FILTER_QUADS)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
if (retLeft and retRight):
SubPix = 1
if SubPix:
img1 = cv2.drawChessboardCorners(LeftImgNewgray.copy(), patternSize, cornersLeft, retLeft)
#cv2.imwrite("./R1/"+R1namelist[exR1]+"LeftResult.png", img1)
cornersSubPix1 = cv2.cornerSubPix(LeftImgNewgray, cornersLeft, (11, 11), (-1, -1), criteria)
img1_ = cv2.drawChessboardCorners(LeftImgNewgray.copy(), patternSize, cornersSubPix1, retLeft)
cv2.imwrite(R1draw + "/"+R1namelist[exR1]+"LeftSubPixResult.png", img1_)
# img2 = cv2.drawChessboardCorners(RightImggray.copy(), patternSize, cornersRight, retLeft)
# #cv2.imwrite("./R1/"+R1namelist[exR1]+"RightResult.png", img2)
# cornersSubPix2 = cv2.cornerSubPix(RightImggray, cornersRight, (11, 11), (-1, -1), criteria)
# img2_ = cv2.drawChessboardCorners(RightImggray.copy(), patternSize, cornersSubPix2, retRight)
# #cv2.imwrite(R1draw + "/"+R1namelist[exR1]+"RightSubPixResult.png", img2_)
#求彩色相机内参畸变
objpoints.append(objp)
imgpoints.append(cornersSubPix1)
else:
cornersSubPix1.extend(cornersLeft)
cornersSubPix2.extend(cornersRight)
else:
print("棋盘格检测有问题")
print(R1namelist[exR1])
continue
rgbTemp = cv2.imread(rgbimglist[0], -1)
fliprgbTemp = np.flip(rgbTemp).copy()
fliprgbTemp = cv2.cvtColor(fliprgbTemp, cv2.COLOR_RGB2BGR)
gary = cv2.cvtColor(fliprgbTemp, cv2.COLOR_BGR2GRAY)
# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gary.shape[::-1], None, None)
print("mtx\n", mtx, "\n")
print("dist\n", dist, "\n")
for exR1 in range(len(rgbimglist)):
print("去畸变:",exR1, "/", len(rgbimglist), "\n")
rgb = cv2.imread(rgbimglist[exR1], -1)
fliprgb = np.flip(rgb).copy()
fliprgb = cv2.cvtColor(fliprgb, cv2.COLOR_RGB2BGR)
h, w = LeftImgNewgray.shape[:2]
newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),0,(w,h)) # 自由比例参数
dst = cv2.undistort(fliprgb, mtx, dist, None, newcameramtx)
cv2.imwrite(R1undistort + '/'+R1namelist[exR1] + ".jpg", dst)
# 反投影误差
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))