基于OpenCV(Python)的运动结构摄像机标定
我想校准一个汽车录像机,并用它进行三维重建结构运动(SfM)。我用这台相机拍的照片的原始尺寸是1920x1080。基本上,我一直在使用来自的源代码进行校准 但是有一些问题,我真的很感激任何帮助 因此,与往常一样(至少在上述源代码中),以下是管道:基于OpenCV(Python)的运动结构摄像机标定,python,opencv,camera-calibration,3d-reconstruction,structure-from-motion,Python,Opencv,Camera Calibration,3d Reconstruction,Structure From Motion,我想校准一个汽车录像机,并用它进行三维重建结构运动(SfM)。我用这台相机拍的照片的原始尺寸是1920x1080。基本上,我一直在使用来自的源代码进行校准 但是有一些问题,我真的很感激任何帮助 因此,与往常一样(至少在上述源代码中),以下是管道: 使用findChessboardCorners 使用cornerSubPix 使用drawhessboardCorners 然后,我们通过调用calibrateCamera 调用getOptimalNewCameraMatrix和undistort函数
findChessboardCorners
cornerSubPix
drawhessboardCorners
calibrateCamera
getOptimalNewCameraMatrix
和undistort
函数以取消图像的扭曲getOptimalNewCameraMatrix
后,失真完全错误。甚至返回的ROI也是[0,0,0,0]
。以下是原始图像及其未失真版本:
您可以看到未失真图像中的图像位于左下角
但是,如果我没有调用getOptimalNewCameraMatrix
直接undistort
它,我会得到一个非常好的图像。
所以,我有三个问题
getOptimalNewCameraMatrix
的作用是什么?我已经读了好几遍了,但还是不明白。从我观察到的情况来看,如果我不调用getOptimalNewCameraMatrix
,我的图像将保持其大小,但会被缩放和模糊。有人能给我更详细地解释一下这个函数吗getOptimalNewCameraMatrix
很重要吧?因为如果没有,未失真的图像将被放大和模糊,使得关键点检测更加困难(在我的情况下,我将使用光流)from sys import argv
import numpy as np
import imutils # To use the imutils.resize function.
# Resizing while preserving the image's ratio.
# In this case, resizing 1920x1080 into 640x360.
import cv2
import glob
# termination criteria
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((9*6,3), np.float32)
objp[:,:2] = np.mgrid[0:9,0:6].T.reshape(-1,2)
# Arrays to store object points and image points from all the images.
objpoints = [] # 3d point in real world space
imgpoints = [] # 2d points in image plane.
images = glob.glob(argv[1] + '*.jpg')
width = 640
for fname in images:
img = cv2.imread(fname)
if width:
img = imutils.resize(img, width=width)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# Find the chess board corners
ret, corners = cv2.findChessboardCorners(gray, (9,6),None)
# If found, add object points, image points (after refining them)
if ret == True:
objpoints.append(objp)
corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
imgpoints.append(corners2)
# Draw and display the corners
img = cv2.drawChessboardCorners(img, (9,6), corners2,ret)
cv2.imshow('img',img)
cv2.waitKey(500)
cv2.destroyAllWindows()
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None)
for fname in images:
img = cv2.imread(fname)
if width:
img = imutils.resize(img, width=width)
h, w = img.shape[:2]
newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))
# undistort
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
# crop the image
x,y,w,h = roi
dst = dst[y:y+h, x:x+w]
cv2.imshow("undistorted", dst)
cv2.waitKey(500)
mean_error = 0
for i in xrange(len(objpoints)):
imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
error = cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2)
mean_error += error
print "total error: ", mean_error/len(objpoints)
已经在answers.opencv.org上询问了某人,他成功地尝试了我的代码和数据集。我想知道到底是什么错了。问题2:
使用cv::getOptimalNewCameraMatrix(…)
可以根据自由缩放参数alpha
计算新的摄影机矩阵
如果将alpha
设置为1
,则所有源图像像素将保留在未失真图像中,即您将看到沿未失真图像的黑色和弯曲边界(如枕形)。这种情况对于一些计算机视觉算法来说是不吉利的,因为新的边缘会出现在未失真的图像上
默认情况下,cv::undistort(…)
调节源图像的子集,该子集将在校正后的图像中可见,这就是为什么该子集上只显示敏感像素-校正后的图像周围没有枕状突起,但数据丢失
无论如何,您可以控制源图像的子集,该子集将在校正后的图像中可见:
cv::Mat image, cameraMatrix, distCoeffs;
// ...
cv::Mat newCameraMatrix = cv::getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, image.size(), 1.0);
cv::Mat correctedImage;
cv::undistort(image, correctedImage, cameraMatrix, distCoeffs, newCameraMatrix);
问题1:
这只是我的感觉,但您也应该注意,如果您在校准后调整图像大小,那么相机矩阵也必须“缩放”,例如:
cv::Mat cameraMatrix;
cv::Size calibSize; // Image during the calibration, e.g. 1920x1080
cv::Size imageSize; // Your current image size, e.g. 640x320
// ...
cv::Matx31d t(0.0, 0.0, 1.0);
t(0) = (double)imageSize.width / (double)calibSize.width;
t(1) = (double)imageSize.height / (double)calibSize.height;
cameraMatrixScaled = cv::Mat::diag(cv::Mat(t)) * cameraMatrix;
这必须仅对相机矩阵执行,因为失真系数不取决于分辨率
问题#3:
无论我怎么想,
cv::getOptimalNewCameraMatrix(…)
在您的案例中并不重要,未失真的图像可以缩放和模糊,因为您消除了非线性变换的影响。如果我是你,我会尝试光流而不调用cv::undistort(…)
。我认为,即使是扭曲的图像也可以包含许多良好的跟踪功能。在校准期间,我已经将图像大小调整为640x320。所以,我不需要重新缩放我的内在参数,不是吗?至于cv::Underist,我确实需要它,因为对于SfM,我需要图像的未失真版本,这样您就不必重新缩放内部函数,只需在cv::Underist(…)
中使用与cv::CalibleCamera(…)
中相同大小的图像即可。而且实现您的方法而不使用cv::getOptimalNewCameraMatrix(…)
也更好。但这让我感到奇怪。有什么问题?漏洞?因为直接调用cv2.undistort
可以解决这个问题,所以我可以假设校准成功了?cv::calibrateCamera(…)
返回最终的重新投影错误,如果这是在0.1和1.0(像素)之间,那么可以认为您的校准已经足够好了。RMS误差小于1.0 px意味着亚像素精度。您能分享一下您如何调用getOptimalNewCameraMatrix
的代码吗?