OpenCV開發筆記(八十一):透過棋盤格使用魚眼方式標定相機內參矩陣矯正攝像頭影像

长沙红胖子Qt创微智科發表於2024-10-25

前言

  對於廣角攝像頭透過相機圖片可以識別出棋盤角點計算相機內參矩陣,透過畸變校準可以得到較好的效果,但是魚眼攝像頭透過這種方式獲得周圍四周的影像效果並不是很好。所以,魚眼攝像頭在校準上與普通攝像頭有一些區別。
  本篇透過一張圖片來識別計算得到相機內參矩陣,並魚眼矯正的方式矯正影像畸形。

Demo

  魚眼方式畸變校準效果
  在這裡插入圖片描述
  普通畸變校準效果
  在這裡插入圖片描述

校準例項

  注意:這裡demo只使用了可識別的兩張基本相似的棋盤圖作為計算,魚眼畸變矯正,若是區別大,那麼校準的時候會因為計算誤差超限而奔潰。

步驟一:初始化圖片列表

  

QStringList list;
list.append("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/34.png");
list.append("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/35.png");
int chessboardColCornerCount = 8;
int chessboardRowCornerCount = 11;

步驟二:迴圈識別圖片棋盤格並且將其世界座標和識別的角點放入列表

  
  

std::vector<std::vector<cv::Point3f>> vectorObjectPoint;
std::vector<std::vector<cv::Point2f>> vectorImagePoint;
cv::Mat grayMat;
cv::Mat srcMat;
for(int n = 0; n < list.size(); n++)
{
    QString str = list.at(n);
    std::string srcFilePath = str.toStdString();
    // 步驟一:讀取檔案
    cv::Mat mat = cv::imread(srcFilePath);
    LOG << mat.cols << mat.rows;
#if 1
    srcMat = cv::Mat(mat.rows * 2, mat.cols * 2, CV_8UC3);
    cv::Mat matRoi = srcMat(cv::Rect(mat.cols / 2, mat.rows / 2, mat.cols, mat.rows));
    cv::addWeighted(mat, 1.0f, matRoi, 0, 0, matRoi);
#else
    srcMat = mat.clone();
#endif
    // 步驟二:縮放,太大了縮放下(可省略)
    cv::resize(srcMat, srcMat, cv::Size(srcMat.cols / 2, srcMat.rows / 2));
    cv::Mat srcMat2 = srcMat.clone();
    cv::Mat srcMat3 = srcMat.clone();
    // 步驟三:灰度化
    cv::cvtColor(srcMat, grayMat, cv::COLOR_BGR2GRAY);
    cv::imshow("grayMat", grayMat);
    // 步驟四:檢測角點
    std::vector<cv::Point2f> vectorPoint2fCorners;
    bool patternWasFound = false;
    patternWasFound = cv::findChessboardCorners(grayMat,
                                             cv::Size(chessboardColCornerCount,
                                                    chessboardRowCornerCount),
                                             vectorPoint2fCorners,
                                             cv::CALIB_CB_ADAPTIVE_THRESH |
                                             cv::CALIB_CB_FAST_CHECK |
                                             cv::CALIB_CB_NORMALIZE_IMAGE);
    if(!patternWasFound)
    {
        LOG << "not find ChessboardCorners:" << chessboardColCornerCount << chessboardRowCornerCount;
        continue;
    }
    /*
    enum { CALIB_CB_ADAPTIVE_THRESH = 1,    // 使用自適應閾值將影像轉化成二值影像
          CALIB_CB_NORMALIZE_IMAGE = 2,    // 歸一化影像灰度係數(用直方圖均衡化或者自適應閾值)
          CALIB_CB_FILTER_QUADS    = 4,    // 在輪廓提取階段,使用附加條件排除錯誤的假設
          CALIB_CB_FAST_CHECK      = 8     // 快速檢測
         };
    */
    cvui::printf(srcMat, 0, 0, 1.0, 0xFF0000, "found = %s", patternWasFound ? "true" : "false");
    cvui::printf(srcMat, 0, 24, 1.0, 0xFF0000, "count = %d", vectorPoint2fCorners.size());
    // 步驟五:繪製棋盤點
    cv::drawChessboardCorners(srcMat2,
                            cv::Size(chessboardColCornerCount,
                                   chessboardRowCornerCount),
                            vectorPoint2fCorners,
                            patternWasFound);
    // 步驟六:進一步提取亞畫素角點
    cv::TermCriteria criteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER,   // 型別
                        30,                                   // 引數二: 最大次數
                                  0.001);                                // 引數三:迭代終止閾值
    /*
    #define CV_TERMCRIT_ITER    1                   // 終止條件為: 達到最大迭代次數終止
    #define CV_TERMCRIT_NUMBER  CV_TERMCRIT_ITER    //
    #define CV_TERMCRIT_EPS     2                   // 終止條件為: 迭代到閾值終止
    */
    cv::imshow("srcMat2", srcMat2);
    cv::cornerSubPix(grayMat,
                   vectorPoint2fCorners,
                   cv::Size(5, 5),
                   cv::Size(-1, -1),
                   criteria);
    // 步驟七:繪製棋盤點
    cv::drawChessboardCorners(srcMat3,
                            cv::Size(chessboardColCornerCount,
                                   chessboardRowCornerCount),
                            vectorPoint2fCorners,
                            patternWasFound);
    cv::imshow("5", srcMat3);
    // 步驟八:角點對應的三維座標(一張圖一組)

    std::vector<cv::Point3f> objectPoints;  // 三維世界座標系
    for(int i = 0; i < chessboardRowCornerCount; i++)
    {
        for(int j = 0; j < chessboardColCornerCount; j++)
        {
            objectPoints.push_back(cv::Point3f(j, i, 0));
        }
    }
    vectorObjectPoint.push_back(objectPoints);

    // 步驟九:影像識別出來的角點(一張圖一組)
    vectorImagePoint.push_back(vectorPoint2fCorners);
}

步驟三:計算內參和畸變係數,然後校準

  

#if 1
    // 步驟十(1):計算內參和畸變係數,未設 flags 和 無 迭代終止條件 圖象 >=1張即可,這是常規廣角標定矯正方式(非魚眼)
    cv::Mat dstMat;
    {
        cv::Mat cameraMatrix;                   // 相機矩陣(接收輸出)
        cv::Mat distCoeffs;                     // 畸變係數(接收輸出)
        cv::Mat rotate;                         // 旋轉量(接收輸出)
        cv::Mat translate;                      // 偏移量(接收輸出)
        cv::calibrateCamera(vectorObjectPoint,
                            vectorImagePoint,
                            grayMat.size(),
                            cameraMatrix,
                            distCoeffs,
                            rotate,
                            translate);
        cv::undistort(srcMat,
                      dstMat,
                      cameraMatrix,
                      distCoeffs);
        cv::imshow("dstMat", dstMat);
    }
#endif
    LOG;
 
#if 1
    // 步驟十(2):計算內參和畸變係數,設 flags 和 增加迭代終止條件 需要 圖象數>=2張,這是魚眼攝像頭常規方式, 但是使用 undistortImage
    cv::Mat dstMat2;
    {
        cv::Mat cameraMatrix2;                   // 相機矩陣(接收輸出)
        cv::Mat distCoeffs2;                     // 畸變係數(接收輸出)
        cv::Mat rotate2;                         // 旋轉量(接收輸出)
        cv::Mat translate2;                      // 偏移量(接收輸出)
        int flags = 0;
        flags |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
        flags |= cv::fisheye::CALIB_CHECK_COND;
        flags |= cv::fisheye::CALIB_FIX_SKEW;
        LOG;
        cv::fisheye::calibrate(vectorObjectPoint,
                               vectorImagePoint,
                               grayMat.size(),
                               cameraMatrix2,
                               distCoeffs2,
                               rotate2,
                               translate2,
                               flags,
                               cv::TermCriteria(3, 20, 1e-6));
        LOG;
        cv::fisheye::undistortImage(srcMat,         // 輸入的畸變影像。
                                    dstMat2,        // 輸出的矯正後的影像。
                                    cameraMatrix2,  // 相機內參矩陣
                                    distCoeffs2);   // 畸變係數
        LOG;
        cv::imshow("dstMat2", dstMat2);
    }
    LOG;
#endif

函式原型

calibrateCamera:相機標定求解函式

  OpenCV中的一個函式,用於相機標定。相機標定是估計相機內參(如焦距、主點座標等)和畸變係數的過程,這些引數對於後續的影像處理任務(如三維重建、目標跟蹤等)至關重要。

double calibrateCamera(InputArrayOfArrays objectPoints,  
                     InputArrayOfArrays imagePoints,  
                     Size imageSize,  
                     OutputArray cameraMatrix,  
                     OutputArray distCoeffs,  
                     OutputArray rvecs,  
                     OutputArray tvecs,  
                     int flags=0,  
                     TermCriteria criteria=TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 1e-6));

  引數說明:

  • objectPoints:世界座標系中的三維點。通常,這些點是透過在標定板上定義的一系列點來獲取的,這些點的座標是已知的。對於每個影像,它應該是一個 Nx3 的陣列(或陣列列表),其中 N 是點的數量,而 3 表示每個點的 (X, Y, Z) 座標。
  • imagePoints:影像座標系中的二維點,即對應於 objectPoints 中的三維點在影像中的投影。對於每個影像,它應該是一個 Nx2 的陣列(或陣列列表),其中 N 是點的數量,而 2 表示每個點的 (x, y) 座標。
  • imageSize:影像的大小,表示為 Size 型別的物件,包含影像的寬度和高度。
  • cameraMatrix:輸出引數,儲存 3x3 的相機內參矩陣。
  • distCoeffs:輸出引數,儲存畸變係數。通常有 5 個係數(k1, k2, p1, p2, k3)對於徑向和切向畸變,或 8 個係數(k1, k2, k3, k4, k5, k6, p1, p2)對於魚眼相機模型。
  • rvecs:輸出引數,對於每個影像,儲存旋轉向量的陣列。
  • tvecs:輸出引數,對於每個影像,儲存平移向量的陣列。
  • flags:不同標誌的組合,用於指定標定過程中使用的演算法。
      CV_CALIB_USE_INTRINSIC_GUESS:使用該引數時,將包含有效的fx,fy,cx,cy的估計值的內參矩陣cameraMatrix,作為初始值輸入,然後函式對其做進一步最佳化。如果不使用這個引數,用影像的中心點初始化光軸點座標(cx, cy),使用最小二乘估算出fx,fy(這種求法好像和張正友的論文不一樣,不知道為何要這樣處理)。注意,如果已知內部引數(內參矩陣和畸變係數),就不需要使用這個函式來估計外參,可以使用solvepnp()函式計算外引數矩陣。
      CV_CALIB_FIX_PRINCIPAL_POINT:在進行最佳化時會固定光軸點,光軸點將保持為影像的中心點。當  CV_CALIB_USE_INTRINSIC_GUESS引數被設定,保持為輸入的值。
      CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只將fy作為可變數,進行最佳化計算。當 CV_CALIB_USE_INTRINSIC_GUESS沒有被設定,fx和fy的實際輸入值將會被忽略,只有fx/fy的比值被計算和使用。
      CV_CALIB_ZERO_TANGENT_DIST:切向畸變係數(P1,P2)被設定為零並保持為零。
      CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6:對應的徑向畸變係數在最佳化中保持不變。如果設定了CV_CALIB_USE_INTRINSIC_GUESS引數,就從提供的畸變係數矩陣中得到。否則,設定為0。
      CV_CALIB_RATIONAL_MODEL(理想模型):啟用畸變k4,k5,k6三個畸變引數。使標定函式使用有理模型,返回8個係數。如果沒有設定,則只計算其它5個畸變引數。
      CALIB_THIN_PRISM_MODEL (薄稜鏡畸變模型):啟用畸變係數S1、S2、S3和S4。使標定函式使用薄稜柱模型並返回12個係數。如果不設定標誌,則函式計算並返回只有5個失真係數。
      CALIB_FIX_S1_S2_S3_S4 :最佳化過程中不改變薄稜鏡畸變係數S1、S2、S3、S4。如果cv_calib_use_intrinsic_guess設定,使用提供的畸變係數矩陣中的值。否則,設定為0。
      CALIB_TILTED_MODEL (傾斜模型):啟用畸變係數tauX and tauY。標定函式使用傾斜感測器模型並返回14個係數。如果不設定標誌,則函式計算並返回只有5個失真係數。
      CALIB_FIX_TAUX_TAUY :在最佳化過程中,傾斜感測器模型的係數不被改變。如果cv_calib_use_intrinsic_guess設定,從提供的畸變係數矩陣中得到。否則,設定為0。
  • criteria:迭代最佳化的終止條件。通常包含最大迭代次數和收斂的精度。
      這個函式返回一個雙精度浮點數,表示重投影誤差的估計值,即實際影像點與透過相機引數和畸變係數計算出的影像點之間的平均誤差。
      為了獲得準確的相機標定結果,通常需要多個檢視(即多張不同角度和姿態拍攝的標定板影像),並確保標定板在不同影像中佔據足夠的視場。此外,影像應該清晰,且標定板上的特徵點(如棋盤格的角點)應準確檢測。

initUndistortRectifyMap:計算畸變引數

  OpenCV中用於初始化用於影像去畸變和校正的對映表的函式。這個函式的目的是生成兩個對映,一個用於x座標,另一個用於y座標,它們可以被用於 remap函式來校正影像的畸變。

void initUndistortRectifyMap(InputArray cameraMatrix, 
                         InputArray distCoeffs, 
                         InputArray R, 
                         InputArray newCameraMatrix, 
                         Size size, 
                         int m1type, 
                         OutputArray map1, 
                         OutputArray map2)

  引數說明

  • cameraMatrix:相機的內參矩陣,一個3x3的浮點數矩陣。
  • distCoeffs:畸變係數,一個1x5或1x8的向量,包含徑向和切向畸變係數。
  • R:可選的旋轉矩陣,一個3x3的浮點數矩陣,表示從原相機座標系到新的相機座標系的旋轉。如果這個引數是空的,那麼newCameraMatrix必須是cameraMatrix。
  • newCameraMatrix:新的相機內參矩陣,一個3x3的浮點數矩陣。這個矩陣可以是原始相機矩陣,或者經過getOptimalNewCameraMatrix調整後的矩陣,以考慮影像的有效視場。
  • size:輸出對映的尺寸,表示為Size型別的物件,包含影像的寬度和高度。
  • m1type:輸出對映的型別,可以是CV_32FC1或CV_16SC2。
  • map1:輸出的第一個對映,用於x座標,可以被傳遞給remap函式。
  • map2:輸出的第二個對映,用於y座標,可以被傳遞給remap函式。
      這兩個對映map1和map2可以被傳遞給remap函式,以對影像進行去畸變和校正。
      如果有一個畸變的影像distortedImage和想要得到校正後的影像undistortedImage,可以這樣使用這兩個函式:
Mat map1,map2;
initUndistortRectifyMap(cameraMatrix, distCoeffs, R, newCameraMatrix, size, CV_32FC1, map1, map2);  
remap(distortedImage, undistortedImage, map1, map2, INTER_LINEAR);

  在這個例子中,INTER_LINEAR是插值方法的型別,用於remap函式。其他的插值方法,如INTER_NEAREST、INTER_CUBIC等也可以被使用,具體取決於應用需求。

cv::fisheye::calibrate:魚眼標定函式

  cv::fisheye::calibrate函式是OpenCV中用於標定魚眼相機鏡頭的函式。

double cv::fisheye::calibrate(  
    InputArrayOfArrays objectPoints,    // 存放世界座標系中的點集,通常為三維點  
    InputArrayOfArrays imagePoints,    // 存放影像座標系中的對應點集,通常為二維點  
    const Size& image_size,           // 影像的大小  
    InputOutputArray K,               // 輸出相機的內參矩陣  
    InputOutputArray D,               // 輸出畸變係數  
    OutputArrayOfArrays rvecs,         // 輸出每幅影像的旋轉向量  
    OutputArrayOfArrays tvecs,         // 輸出每幅影像的平移向量  
    int flags = 0,                     // 標定方法的標誌位  
    TermCriteria criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 100, DBL_EPSILON)                      // 迭代演算法的終止條件  
);

  引數說明:

  • objectPoints:世界座標系中的點集,通常是一個三維的點陣列。在標定過程中,這些點通常是標定板(如棋盤格)的角點在世界座標系中的位置。
  • imagePoints:影像座標系中對應的點集,是objectPoints中的點在影像上的投影。這些點通常是透過影像處理演算法(如角點檢測)在影像中找到的。
  • image_size:影像的大小,包括寬度和高度。
  • K:輸出引數,相機的內參矩陣。內參矩陣包含了焦距(fx, fy)和光心(cx, cy)等引數。
  • D:輸出引數,畸變係數。對於魚眼鏡頭,畸變係數可能包括多個引數,用於描述鏡頭的非線性畸變。
  • rvecs:輸出引數,每幅影像的旋轉向量。旋轉向量表示了相機座標系與世界座標系之間的旋轉關係。
  • tvecs:輸出引數,每幅影像的平移向量。平移向量表示了相機座標系與世界座標系之間的平移關係。
  • flags:標定方法的標誌位,可以透過組合不同的標誌來指定標定過程的不同選項。例如,cv::fisheye::CALIB_USE_INTRINSIC_GUESS表示使用提供的內參矩陣作為初始估計。
  • criteria:迭代演算法的終止條件。通常使用迭代次數和誤差閾值的組合來定義。
      返回值,函式返回重投影誤差的平均值,該值越小表示標定結果越好。
      在使用cv::fisheye::calibrate函式之前,需要準備多幅包含標定板(如棋盤格)的影像,並提取出標定板角點在影像中的位置。
      標定過程中,需要確保標定板在影像中的位置和方向具有足夠的多樣性,以便能夠準確地估計相機的內參和畸變係數。
      標定結果的質量受到多種因素的影響,包括標定板的精度、影像的質量、標定過程中相機的穩定性等。

cv::fisheye::undistortImage:魚眼影像去畸變

  fisheye::undistortImage函式是OpenCV中用於魚眼影像去畸變的一個函式。該函式的原型通常如下所示(注意,具體引數型別和名稱可能會根據OpenCV的不同版本有所變化,但基本思想是一致的):

void cv::fisheye::undistortImage(InputArray distorted, 
                           OutputArray undistorted, 
                           InputArray K, 
                           InputArray D, 
                           InputArray Knew = cv::noArray(), 
                           Size new_size = Size(), 
                           double balance = 0.0 
);

  引數說明:

  • distorted:輸入的畸變影像。
  • undistorted:輸出的矯正後的影像。
  • K:相機內參矩陣。通常是一個3x3的浮點數矩陣,包含了焦距fx, fy(在矩陣的對角線上)和光心(cx, cy)(矩陣的前兩行的最後一列)。
  • D:畸變係數。對於魚眼相機,通常包含四個係數:k1, k2, k3, k4,這些係數描述了徑向畸變。
  • Knew(可選):新的相機內參矩陣。如果提供,則使用此矩陣進行去畸變,而不是直接使用原始內參矩陣K。這在某些情況下很有用,比如當你想調整去畸變後影像的視野或比例時。如果未提供,則預設為空(cv::noArray()),此時會使用原始內參矩陣K。
  • new_size(可選):輸出影像的尺寸。如果提供,則去畸變後的影像將被縮放到這個尺寸。如果未提供,則輸出影像的尺寸將與輸入影像的尺寸相同。
  • balance(可選):用於在影像去畸變過程中調整畸變減少和視野保留之間的平衡的引數。值越大,畸變減少得越多,但可能會損失更多的影像視野。預設值為0.0,表示不進行特別的平衡調整。
      請注意,雖然上述引數描述是通用的,但具體的實現和引數名稱可能會根據你使用的OpenCV版本有所不同。因此,建議查閱你所使用的OpenCV版本的官方文件以獲取最準確的資訊。
      此外,fisheye::undistortImage函式是專門為魚眼鏡頭設計的,它考慮了魚眼鏡頭特有的畸變模式。如果你正在處理的是非魚眼鏡頭拍攝的影像,可能需要使用cv::undistort函式或其他適用於普通鏡頭的去畸變方法。
      在這裡插入圖片描述
      經典的魚眼單目標定程式,採用:
int flags = 0;
flags |= myfisheye::CALIB_RECOMPUTE_EXTRINSIC;
flags |= myfisheye::CALIB_CHECK_COND;
flags |= myfisheye::CALIB_FIX_SKEW;

cv::fisheye::initUndistortRectifyMap:魚眼影像去畸變

  函式是OpenCV中用於魚眼相機畸變校正的函式,它計算用於畸變校正的對映表。以下是該函式的原型:

void cv::fisheye::initUndistortRectifyMap(  
    InputArray K,                  // 相機內參矩陣  
    InputArray D,                  // 畸變係數矩陣  
    InputArray R,                  // 旋轉矩陣,通常為單位矩陣  
    InputArray P,                  // 校正後的相機內參矩陣  
    const cv::Size& size,          // 矯正後的影像大小  
    int m1type,                    // 對映表型別,通常設定為cv::CV_16SC2  
    OutputArray map1,              // x座標的對映表  
    OutputArray map2               // y座標的對映表  
);

  引數說明:

  • K:相機內參矩陣,它是一個3x3的矩陣,包含了焦距(fx, fy)和光心(cx, cy)等引數。這個矩陣可以透過相機標定得到。
  • D:畸變係數矩陣,它包含了描述鏡頭畸變程度的引數。對於魚眼相機,畸變係數通常包括k1, k2, k3, k4, k5五個引數。
  • R:旋轉矩陣,用於將相機座標系轉換到校正後的相機座標系。在大多數情況下,當進行單目魚眼相機的校正時,這個矩陣可以設定為單位矩陣。
  • P:校正後的相機內參矩陣,它是透過相機標定得到的,並且可能已經考慮了畸變校正。如果不需要改變校正後影像的內參,這個矩陣可以與原相機內參相同。
  • size:矯正後的影像大小,即輸出影像的尺寸。
  • m1type:對映表型別,它指定了輸出對映表的格式。通常,這個引數被設定為cv::CV_16SC2,表示對映表是一個兩通道的16位有符號整數矩陣,其中每個元素是一個包含x和y座標的二維向量。
  • map1:輸出的x座標對映表,它是一個與矯正後影像大小相同的矩陣,用於儲存每個畫素點在原畸變影像中的x座標。
  • map2:輸出的y座標對映表,它也是一個與矯正後影像大小相同的矩陣,用於儲存每個畫素點在原畸變影像中的y座標。
      這個函式通常與cv::remap函式一起使用,以根據計算得到的對映表對畸變影像進行校正。首先,使用cv::fisheye::initUndistortRectifyMap函式計算對映表,然後使用cv::remap函式根據對映表對影像進行重對映,從而得到校正後的影像。
      注意,在使用這個函式之前,需要確保已經透過相機標定得到了準確的相機內參矩陣和畸變係數矩陣,矯正後的影像大小(size引數)應該根據實際需求進行設定,以確保輸出影像的尺寸符合後續處理或顯示的要求。對映表型別(m1type引數)的選擇對於最終矯正效果有一定影響,通常建議使用cv::CV_16SC2以獲得更好的效能和精度。

cv::remap:x和y重對映

  cv::remap 函式是 OpenCV 庫中用於影像重對映的函式,它可以將源影像中的畫素根據提供的對映關係重新定位到目標影像中。

void cv::remap(InputArray src,
             OutputArray dst,  
             InputArray map1, InputArray map2,  
             int interpolation,  
             int borderMode = BORDER_CONSTANT,  
             const Scalar& borderValue = Scalar())

  引數說明:

  • src:型別InputArray,輸入影像,即源影像。
  • dst:型別OutputArray,輸出影像,即目標影像。它的大小與 map1 相同,型別與 src 相同。
  • map1:型別InputArray,對映矩陣的第一個組成部分,可以是單獨的 x 座標對映,也可以是與 map2 一起構成 (x, y) 座標的對映。其型別可以是 CV_16SC2、CV_32FC1 或 CV_32FC2。
  • map2(可選):型別InputArray,當 map1 是單獨的 x 座標對映時,map2 存放 y 座標的對映。其型別可以是 CV_16UC1、CV_32FC1。如果 map1 是 (x, y) 座標的對映,則 map2 可以為空或不使用。
  • interpolation:型別int,插值方法。可選的插值方式包括 INTER_NEAREST(最近鄰插值)、INTER_LINEAR(雙線性插值,預設)、INTER_CUBIC(雙三樣條插值)和 INTER_LANCZOS4(Lanczos插值)。注意,INTER_AREA 插值方式在這裡是不支援的。
  • borderMode(可選):型別int,邊界模式,預設為 BORDER_CONSTANT。表示目標影像中“離群點”的畫素值不會被此函式修改。
  • borderValue(可選):型別const Scalar&,當有常數邊界時使用的值,其有預設值 Scalar(),即預設值為 0。僅在 borderMode = BORDER_CONSTANT 時需要。
      可對影像翻轉(如上下翻轉、左右翻轉),影像縮放(透過修改對映矩陣中的座標值來實現),影像旋轉(需要更復雜的對映矩陣計算)。
      注意,對映矩陣 map1 和 map2(如果使用)必須與源影像 src 有相同的尺寸。該函式目前只支援小於 32767 × 32767 尺寸的輸入圖和輸出圖。插值方法的選擇會影響重對映後的影像質量。

Demo原始碼

void OpenCVManager::testFisheyeChessboard()
{
    QStringList list;
    list.append("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/34.png");
    list.append("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/35.png");
    int chessboardColCornerCount = 8;
    int chessboardRowCornerCount = 11;

//    list.append("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/36.png");
//    int chessboardColCornerCount = 4;
//    int chessboardRowCornerCount = 6;

    std::vector<std::vector<cv::Point3f>> vectorObjectPoint;
    std::vector<std::vector<cv::Point2f>> vectorImagePoint;
    cv::Mat grayMat;
    cv::Mat srcMat;

    for(int n = 0; n < list.size(); n++)
    {
        QString str = list.at(n);
        std::string srcFilePath = str.toStdString();
        // 步驟一:讀取檔案
        cv::Mat mat = cv::imread(srcFilePath);
        LOG << mat.cols << mat.rows;
        srcMat = mat.clone();
        // 步驟二:縮放,太大了縮放下(可省略)
        cv::resize(srcMat, srcMat, cv::Size(srcMat.cols / 2, srcMat.rows / 2));
        cv::Mat srcMat2 = srcMat.clone();
        cv::Mat srcMat3 = srcMat.clone();
        // 步驟三:灰度化
        cv::cvtColor(srcMat, grayMat, cv::COLOR_BGR2GRAY);
        cv::imshow("grayMat", grayMat);
        // 步驟四:檢測角點
        std::vector<cv::Point2f> vectorPoint2fCorners;
        bool patternWasFound = false;
        patternWasFound = cv::findChessboardCorners(grayMat,
                                                    cv::Size(chessboardColCornerCount, chessboardRowCornerCount),
                                                    vectorPoint2fCorners,
                                                    cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_FAST_CHECK | cv::CALIB_CB_NORMALIZE_IMAGE);
        if(!patternWasFound)
        {
            LOG << "not find ChessboardCorners:" << chessboardColCornerCount << chessboardRowCornerCount;
            continue;
        }
        /*
        enum { CALIB_CB_ADAPTIVE_THRESH = 1,    // 使用自適應閾值將影像轉化成二值影像
               CALIB_CB_NORMALIZE_IMAGE = 2,    // 歸一化影像灰度係數(用直方圖均衡化或者自適應閾值)
               CALIB_CB_FILTER_QUADS    = 4,    // 在輪廓提取階段,使用附加條件排除錯誤的假設
               CALIB_CB_FAST_CHECK      = 8     // 快速檢測
             };
        */
        cvui::printf(srcMat, 0, 0, 1.0, 0xFF0000, "found = %s", patternWasFound ? "true" : "false");
        cvui::printf(srcMat, 0, 24, 1.0, 0xFF0000, "count = %d", vectorPoint2fCorners.size());
        // 步驟五:繪製棋盤點
        cv::drawChessboardCorners(srcMat2,
                                  cv::Size(chessboardColCornerCount, chessboardRowCornerCount),
                                  vectorPoint2fCorners,
                                  patternWasFound);
        // 步驟六:進一步提取亞畫素角點
        cv::TermCriteria criteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER,   // 型別
                                  30,                                   // 引數二: 最大次數
                                  0.001);                                // 引數三:迭代終止閾值
        /*
        #define CV_TERMCRIT_ITER    1                   // 終止條件為: 達到最大迭代次數終止
        #define CV_TERMCRIT_NUMBER  CV_TERMCRIT_ITER    //
        #define CV_TERMCRIT_EPS     2                   // 終止條件為: 迭代到閾值終止
        */
        cv::imshow("srcMat2", srcMat2);
        cv::cornerSubPix(grayMat,
                         vectorPoint2fCorners,
                         cv::Size(5, 5),
                         cv::Size(-1, -1),
                         criteria);
        // 步驟七:繪製棋盤點
        cv::drawChessboardCorners(srcMat3,
                                  cv::Size(chessboardColCornerCount, chessboardRowCornerCount),
                                  vectorPoint2fCorners,
                                  patternWasFound);
        cv::imshow("5", srcMat3);
        // 步驟八:角點對應的三維座標(一張圖一組)

        std::vector<cv::Point3f> objectPoints;  // 三維世界座標系
        for(int i = 0; i < chessboardRowCornerCount; i++)
        {
            for(int j = 0; j < chessboardColCornerCount; j++)
            {
                objectPoints.push_back(cv::Point3f(j, i, 0));
            }
        }
        vectorObjectPoint.push_back(objectPoints);

        // 步驟九:影像識別出來的角點(一張圖一組)
        vectorImagePoint.push_back(vectorPoint2fCorners);
    }
#if 0
    // 步驟十(1):計算內參和畸變係數,未設 flags 和 無 迭代終止條件 圖象 >=1張即可,這是常規廣角標定矯正方式(非魚眼)
    cv::Mat dstMat;
    {
        cv::Mat cameraMatrix;                   // 相機矩陣(接收輸出)
        cv::Mat distCoeffs;                     // 畸變係數(接收輸出)
        cv::Mat rotate;                         // 旋轉量(接收輸出)
        cv::Mat translate;                      // 偏移量(接收輸出)
        cv::calibrateCamera(vectorObjectPoint,
                            vectorImagePoint,
                            grayMat.size(),
                            cameraMatrix,
                            distCoeffs,
                            rotate,
                            translate);
        cv::undistort(srcMat,
                      dstMat,
                      cameraMatrix,
                      distCoeffs);
        cv::imshow("dstMat", dstMat);
    }
#endif
    LOG;
#if 0
    // 步驟十(2):計算內參和畸變係數,設 flags 和 增加迭代終止條件 需要 圖象數>=2張,這是魚眼攝像頭常規方式, 但是使用 undistortImage
    cv::Mat dstMat2;
    {
        cv::Mat cameraMatrix2;                   // 相機矩陣(接收輸出)
        cv::Mat distCoeffs2;                     // 畸變係數(接收輸出)
        cv::Mat rotate2;                         // 旋轉量(接收輸出)
        cv::Mat translate2;                      // 偏移量(接收輸出)
        int flags = 0;
        flags |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
        flags |= cv::fisheye::CALIB_CHECK_COND;
        flags |= cv::fisheye::CALIB_FIX_SKEW;
        LOG;
        cv::fisheye::calibrate(vectorObjectPoint,
                               vectorImagePoint,
                               grayMat.size(),
                               cameraMatrix2,
                               distCoeffs2,
                               rotate2,
                               translate2,
                               flags,
                               cv::TermCriteria(3, 20, 1e-6));
        LOG;
        cv::fisheye::undistortImage(srcMat,         // 輸入的畸變影像。
                                    dstMat2,        // 輸出的矯正後的影像。
                                    cameraMatrix2,  // 相機內參矩陣
                                    distCoeffs2);   // 畸變係數
        LOG;
        cv::imshow("dstMat2", dstMat2);
    }
    LOG;
#endif
#endif
    LOG;
    cv::waitKey(0);
}

工程模板v1.71.0

  在這裡插入圖片描述

入坑

入坑一:魚眼標定校準後輸出全黑

問題

  在這裡插入圖片描述

  在這裡插入圖片描述

嘗試

  應該是引數不對:
  在這裡插入圖片描述

  十張同樣圖
  在這裡插入圖片描述

  三十張同樣圖
  在這裡插入圖片描述

  那麼不是影像多少的問題,下一步看是不是其他問題:
  在這裡插入圖片描述

  在這裡插入圖片描述

  發現是矩陣錯了.

解決

  在這裡插入圖片描述

入坑二:執行魚眼標定崩潰

問題

  執行奔潰
  在這裡插入圖片描述

原因

  校準的函式失敗,經過研究是多張圖可能誤差較大,導致越過上限從而崩潰,

解決

  只輸入相似的圖,稍微移動一下,保持基本大致不變進行採集:
  在這裡插入圖片描述

相關文章