2

我想校準汽車視頻錄像機,並將其用於運動結構(SfM)的三維重建。我用這臺相機拍攝的照片的原始尺寸爲1920x1080。基本上,我一直在使用OpenCV tutorial的源代碼進行校準。使用OpenCV進行運動結構的攝像機標定(Python)

但也有一些問題,我會很感激任何幫助。

所以,像往常一樣(至少在上面的源代碼),這裏是管道:

  1. 查找與findChessboardCorners
  2. 棋盤角落cornerSubPix
  3. 畫出它獲取它的像素值可視化與drawhessboardCorners
  4. 然後,我們校準相機的電話calibrateCamera
  5. 致電getOptimalNewCameraMatrixundistort功能undistort圖像

在我的情況下,由於圖片太大(1920×1080),我把它調整爲640X320(SFM過程中,我也將使用這個尺寸的圖像,因此,我不認爲這會有什麼問題)。而且,我還使用了9x6棋盤角進行校準。

在這裏,問題出現了。在致電getOptimalNewCameraMatrix後,失真出現完全錯誤。即使是返回的投資回報率是[0,0,0,0]。以下是原始圖像和無失真的版本:

Original image ​​ 你可以看到不失真的圖像中的圖像左下角。

但是,如果我沒有撥打getOptimalNewCameraMatrix而只是直接撥打undistort,那我就有了一個相當不錯的形象。 Undistorted image

所以,我有三個問題。

  1. 這是爲什麼?我嘗試過使用同一臺相機拍攝的另一個數據集,以及我的iPhone 6 Plus,但結果與上述相同。

  2. 另一個問題是,什麼是getOptimalNewCameraMatrix呢?我已經多次閱讀文檔,但仍然無法理解它。從我觀察到的情況來看,如果我沒有撥打getOptimalNewCameraMatrix,我的圖片將保留其大小,但會縮放和模糊。任何人都可以更詳細地解釋這個函數嗎?

  3. 對於SfM,我估計撥打getOptimalNewCameraMatrix很重要?因爲如果不是這樣,未失真的圖像將變得更加放大和模糊,從而使關鍵點檢測變得更加困難(在我的情況下,我將使用光流)?

我用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,他想我的代碼和我成功的數據集。我不知道什麼是錯的。

回答

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

問題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(...)。我認爲,即使是扭曲的圖像也可以包含許多用於跟蹤的優秀特徵。

+0

在校準過程中,我已將圖像大小調整爲640x320。所以,我不需要重新調整我的內在參數是不是?而且,至於cv :: undistort,我確實需要它,因爲對於SfM,我將需要圖像的未失真版本 – Hilman

+1

然後,您不必重新調整內部函數,只需在'cv: :如'cv :: calibrateCamera(...)'中的unistort(...)'。不使用'cv :: getOptimalNewCameraMatrix(...)'來實現你的方法也更好。 – Kornel

+0

但這只是讓我想知道。問題是什麼?錯誤?由於直接調用'cv2.undistort'解決這個問題,我可以假設校準是成功的? – Hilman