OpenCV開發筆記(七十六):相機標定(一):識別棋盤並繪製角點

前言

  知道圖像畸變矯映射的原理之後,那麼如何得到相機的內參是矯正的第一步,內參決定了內參矩陣(中心點、焦距等),用內參矩陣才能計算出投影矩陣,從而將原本畸變的圖像矯正爲平面投影圖像。
  本篇描述了相機成形的原理,並繪製出識別的角點。

 

Demo

  請添加圖片描述

  請添加圖片描述

  請添加圖片描述

 

相機成形的原理

小孔成像原理

  在這裏插入圖片描述

  得到矩陣計算原理:
  在這裏插入圖片描述

  得到計算過程:
  在這裏插入圖片描述

 

相機的畸變

  相機的畸變是指相機鏡頭對物體所成的像相對於物體本身而言的失真程度,它是光學透鏡的固有特性。畸變產生的原因主要是透鏡的邊緣部分和中心部分的放大倍率不一樣。
畸變分爲以下幾類:

  • 徑向畸變
  • 切向畸變
  • 薄棱鏡畸變
      通常情況下,徑向畸變的影響要遠遠大於其他畸變。畸變是不可消除的,但在實際的應用中,可以通過一些軟件來進行畸變的補償,如OpenCV、MATLAB等。

徑向畸變

  主要由透鏡不同部位放大倍率不同造成,它又分爲枕形畸變和桶形畸變兩種。枕形畸變,也稱爲鞍形形變,視野中邊緣區域的放大率遠大於光軸中心區域的放大率,常用在遠攝鏡頭中。桶形畸變則與枕形畸變相反,視野中光軸中心區域的放大率遠大於邊緣區域的放大率,常出現在廣角鏡頭和魚眼鏡頭中。
  在這裏插入圖片描述

切向畸變

  主要由透鏡安裝與成像平面不平行造成,類似於透視原理,如近大遠小、圓變橢圓等。
  在這裏插入圖片描述

薄棱鏡畸變

  由透鏡設計缺陷和加工安裝誤差造成,又稱爲線性畸變。其影響較小,一般忽略不計。

 

棋牌識別步驟

步驟一:標定採集的數據圖像

  採集一張棋盤圖片,要確認他是可以被識別的。
  在這裏插入圖片描述

  讀取圖像,這裏由於圖片較大,我們重設大小爲原來寬高的1/2:

    // 使用圖片
    std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/chessboard.png";
//    std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/24.jpg";
    cv::Mat srcMat = cv::imread(srcFilePath);
    int chessboardColCornerCount = 6;
    int chessboardRowCornerCount = 9;
    // 步驟一:讀取文件
//    cv::imshow("1", srcMat);
//    cv::waitKey(0);
    // 步驟二:縮放,太大了縮放下(可省略)
    cv::resize(srcMat, srcMat, cv::Size(srcMat.cols / 2, srcMat.rows / 2));
    cv::Mat srcMat2 = srcMat.clone();
    cv::Mat srcMat3 = srcMat.clone();
//    cv::imshow("2", srcMat);
//    cv::waitKey(0);

步驟二:圖像處理,提取角點,並繪製出來

  先灰度化,然後輸入預製的縱向橫向角數量,使用棋盤角點函數提取角點

    // 步驟三:灰度化
    cv::Mat grayMat;
    cv::cvtColor(srcMat, grayMat, cv::COLOR_BGR2GRAY);
    cv::imshow("3", grayMat);
//    cv::waitKey(0);
    // 步驟四:檢測角點
    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);
    /*
    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());
    qDebug() << __FILE__ << __LINE__ << 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                   // 終止條件爲: 迭代到閾值終止
    */
    qDebug() << __FILE__ << __LINE__ << vectorPoint2fCorners.size();
    cv::cornerSubPix(grayMat,
                     vectorPoint2fCorners,
                     cv::Size(11, 11),
                     cv::Size(-1, -1),
                     criteria);
 

函數原型

findChessboardCorners:識別預製棋盤角點數量的棋盤

  OpenCV 中用於檢測圖像中棋盤角點的函數。

bool cv::findChessboardCorners(InputArray image,
                           Size patternSize,
                           OutputArray corners,
                           int flags=CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE)

  參數解釋:

  • image:輸入的圖像,通常是一個灰度圖像,因爲角點檢測在灰度空間中進行更爲準確。
  • patternSize:棋盤的內角點數量,例如一個 8x6 的棋盤會有 48 個內角點,所以 patternSize 會是 Size(8, 6)。
  • corners:檢測到的角點輸出數組。
  • flags:不同的標誌,用於指定角點檢測的不同方法。可以是以下的一個或多個標誌的組合:
    CALIB_CB_ADAPTIVE_THRESH:使用自適應閾值將圖像轉換爲二值圖像,而不是使用固定的全局閾值。
    CALIB_CB_NORMALIZE_IMAGE:在尋找角點之前,先對圖像進行歸一化,以提高魯棒性。
    CALIB_CB_FAST_CHECK:僅檢查角點候選者中的少量點,用於快速檢測,但可能不如標準方法準確。

  函數返回值是一個布爾值,如果找到足夠的角點以形成一個棋盤模式,則返回 true;否則返回 false。
  findChessboardCorners 函數通常用於相機標定,通過檢測棋盤角點來確定圖像與真實世界之間的對應關係。一旦角點被檢測到,就可以使用這些點來估計相機的內參(如焦距、主點)和外參(如旋轉和平移矩陣)。

drawChessboardCorners:繪製棋盤角點

  OpenCV中的一個函數,用於在檢測到的棋盤角點周圍繪製方框。這對於相機標定、圖像對齊等應用非常有用。

void cv::drawChessboardCorners(InputOutputArray image,
                            Size patternSize,
                            InputArray corners,
                            bool patternWasFound)

  參數解釋:

  • image:輸入的圖像,通常是一個彩色圖像,函數會在這個圖像上繪製角點。
  • patternSize:棋盤的內角點數量,例如一個 8x6 的棋盤會有 48 個內角點,所以 patternSize 會是 Size(8, 6)。
  • corners:檢測到的角點,通常是通過 findChessboardCorners 函數得到的。
  • patternWasFound:一個布爾值,表示是否找到了足夠的角點來形成一個棋盤模式。如果爲 true,則函數會在角點周圍繪製彩色的方框;如果爲 false,則只會繪製白色的方框。
    這個函數通常與 findChessboardCorners 結合使用,以檢測圖像中的棋盤角點,並在檢測到的角點周圍繪製方框。這對於視覺校準和相機標定等任務非常有用。

TermCriteria:迭代終止模板類

  TermCriteria是OpenCV中用於指定迭代算法終止條件的模板類。它取代了之前的CvTermCriteria,並且在許多OpenCV算法中作爲迭代求解的結構被使用。

struct TermCriteria {  
    enum { COUNT=1, MAX_ITER=COUNT, EPS=2 };  
    TermCriteria();  
    TermCriteria(int type, int maxCount, double epsilon);  
    TermCriteria(const CvTermCriteria& criteria);  
};

  構造時需要三個參數:

  • 類型(type):它決定了迭代終止的條件。類型可以是CV_TERMCRIT_ITER、CV_TERMCRIT_EPS或CV_TERMCRIT_ITER+CV_TERMCRIT_EPS。在C++中,這些宏對應的版本分別爲TermCriteria::COUNT、TermCriteria::EPS。
    CV_TERMCRIT_ITER或TermCriteria::COUNT:表示迭代終止條件爲達到最大迭代次數;
    CV_TERMCRIT_EPS或TermCriteria::EPS:表示迭代到特定的閾值就終止;
    CV_TERMCRIT_ITER+CV_TERMCRIT_EPS:則表示兩者都作爲迭代終止條件。
  • 迭代的最大次數(maxCount):這是算法可以執行的最大迭代次數。
  • 特定的閾值(epsilon):當滿足這個精確度時,迭代算法會停止。

cornerSubPix:亞像素角點提取

  OpenCV中用於精確化角點位置,其函數原型如下:

void cv::cornerSubPix(InputArray image,
                   InputOutputArray corners,
                   Size winSize,
                   Size zeroZone,
                   TermCriteria criteria);

  參數解釋:

  • image:輸入圖像的像素矩陣,最好是8位灰度圖像,這樣檢測效率會更高。
  • corners:初始的角點座標向量,同時作爲亞像素座標位置的輸出,因此需要是浮點型數據。
  • winSize:搜索窗口的大小,它表示的是搜索窗口的一半尺寸。
  • zeroZone:死區的一半尺寸,死區是搜索窗口內不對中央位置做求和運算的區域。這是爲了避免自相關矩陣出現某些可能的奇異性。
  • criteria:角點搜索的停止條件,通常包括迭代次數、角點位置變化量或角點誤差變化量等。

  cornerSubPix函數用於在初步提取的角點信息上進一步提取亞像素信息,從而提高相機標定的精度。在相機標定、目標跟蹤和三維重建等應用中,精確的角點位置是非常重要的,因此cornerSubPix函數在這些領域有廣泛的應用。

 

Demo源碼

void OpenCVManager::testFindChessboardCorners()
{
#define FindChessboardCornersUseCamera 1
#if !FindChessboardCornersUseCamera
    // 使用圖片
    std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/chessboard.png";
//    std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/24.jpg";
    cv::Mat srcMat = cv::imread(srcFilePath);
#else
    // 使用攝像頭
    cv::VideoCapture capture;
    // 插入USB攝像頭默認爲0
    if(!capture.open(0))
    {
        qDebug() << __FILE__ << __LINE__  << "Failed to open camera: 0";
    }else{
        qDebug() << __FILE__ << __LINE__  << "Succeed to open camera: 0";
    }
    while(true)
    {
        cv::Mat srcMat;
        capture >> srcMat;
#endif
    int chessboardColCornerCount = 6;
    int chessboardRowCornerCount = 9;
    // 步驟一:讀取文件
//    cv::imshow("1", srcMat);
//    cv::waitKey(0);
    // 步驟二:縮放,太大了縮放下(可省略)
    cv::resize(srcMat, srcMat, cv::Size(srcMat.cols / 2, srcMat.rows / 2));
    cv::Mat srcMat2 = srcMat.clone();
    cv::Mat srcMat3 = srcMat.clone();
//    cv::imshow("2", srcMat);
//    cv::waitKey(0);
    // 步驟三:灰度化
    cv::Mat grayMat;
    cv::cvtColor(srcMat, grayMat, cv::COLOR_BGR2GRAY);
    cv::imshow("3", grayMat);
//    cv::waitKey(0);
    // 步驟四:檢測角點
    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);
    /*
    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());
    qDebug() << __FILE__ << __LINE__ << vectorPoint2fCorners.size();
    // 步驟五:繪製棋盤點
    cv::drawChessboardCorners(srcMat2,
                              cv::Size(chessboardColCornerCount, chessboardRowCornerCount),
                              vectorPoint2fCorners
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章