2

我想校准汽车录像机并将其用于运动结构 (SfM) 的 3D 重建。我用这台相机拍摄的照片的原始尺寸是 1920x1080。基本上,我一直在使用OpenCV 教程中的源代码进行校准。

但是有一些问题,我非常感谢任何帮助。

所以,像往常一样(至少在上面的源代码中),这里是管道:

  1. 找到棋盘角findChessboardCorners
  2. 获取其子像素值cornerSubPix
  3. 绘制它以进行可视化drawhessboardCorners
  4. 然后,我们通过调用来校准相机calibrateCamera
  5. 调用getOptimalNewCameraMatrixundistort函数使图像不失真

就我而言,由于图像太大(1920x1080),我将其调整为 640x320(在 SfM 期间,我也会使用这种尺寸的图像,所以,我认为这不会有任何问题)。而且,我使用 9x6 棋盘角进行校准。

这里,问题出现了。调用后getOptimalNewCameraMatrix,失真完全错误。甚至返回的 ROI 也是[0,0,0,0]. 以下是原始图像及其未失真版本:

原始图像 未失真的图像 您可以看到未失真图像中的图像位于左下方。

但是,如果我不调用getOptimalNewCameraMatrix并且直接调用undistort它,我会得到一个非常好的图像。 未失真的图像

所以,我有三个问题。

  1. 为什么是这样?我尝试过使用同一台相机拍摄的另一个数据集,也使用我的 iPhone 6 Plus,但结果与上述相同。

  2. 另一个问题是,does是什么getOptimalNewCameraMatrix?我已经多次阅读文档,但仍然无法理解。根据我的观察,如果我不调用getOptimalNewCameraMatrix,我的图像将保持其大小,但会被缩放和模糊。任何人都可以为我更详细地解释这个功能吗?

  3. 对于 SfM,我想调用 togetOptimalNewCameraMatrix很重要?因为如果不是,未失真的图像会被放大和模糊,使关键点检测更难(在我的情况下,我将使用光流)?

我已经用opencv示例图片测试了代码,结果很好。

下面是我的源代码:

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 中询问某人,他成功地尝试了我的代码和我的数据集。我想知道实际上出了什么问题。

4

1 回答 1

0

问题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);

问题一:

这只是我的感觉,但您也应该小心,如果您在校准后调整图像大小,那么相机矩阵也必须“缩放”,例如:

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(...). 我认为即使是扭曲的图像也可以包含很多很好的跟踪特征。

于 2016-09-16T14:11:09.003 回答