張正友視覺標(biāo)定算法學(xué)習(xí)筆記

本博客內(nèi)容來源于網(wǎng)絡(luò)以及其他書籍,結(jié)合自己學(xué)習(xí)的心得進(jìn)行重編輯,因?yàn)榭戳撕芏辔恼虏槐阋灰粯?biāo)注引用,如圖片文字等侵權(quán),請(qǐng)告知?jiǎng)h除。

傳統(tǒng)2D計(jì)算機(jī)視覺學(xué)習(xí)筆記目錄------->傳送門
傳統(tǒng)3D計(jì)算機(jī)視覺學(xué)習(xí)筆記目錄------->傳送門

前言

在閱讀本篇文章之前,建議首先要了解知道什么是相機(jī)的針孔模型,我在之前的文章小孔相機(jī)參數(shù)學(xué)習(xí)筆記中有詳細(xì)的解釋,或者說不是建議,而是必須要知道,不然也不知道這篇文章在干什么。本片文章主要來講張正友視覺標(biāo)定法,在講張正友之前,我們先說一下什么是相機(jī)標(biāo)定。

所謂相機(jī)標(biāo)定,就是通過實(shí)驗(yàn)等方法將相機(jī)模型中的未知數(shù)給解算出來。例如小孔模型我們一般需要節(jié)算出內(nèi)參矩陣 fx,fy,cx,cy 以及畸變參數(shù)。

為什么要標(biāo)定出這些參數(shù)呢?一個(gè)是因?yàn)槊總€(gè)鏡頭的畸變程度各不相同,通過相機(jī)標(biāo)定可以校正這種鏡頭畸變矯正畸變,生成矯正后的圖像;另一個(gè)是根據(jù)獲得的圖像重構(gòu)三維場(chǎng)景,在之前文章我們有寫過PNP算法,就是通過相機(jī)的內(nèi)參以及畸變解算出目標(biāo)在相機(jī)下的位置,以及雙目3D相機(jī)也需要相機(jī)模型參數(shù)進(jìn)行計(jì)算等。

有很多標(biāo)定方法,比如傳統(tǒng)相機(jī)標(biāo)定法、主動(dòng)視覺相機(jī)標(biāo)定方法、相機(jī)自標(biāo)定法等,但是現(xiàn)在用的最多的就是相機(jī)的自標(biāo)定法,而相機(jī)的自標(biāo)定法的基礎(chǔ)就是張正友標(biāo)定法的解算流程。下面我們一起來看一下張正友標(biāo)定法。


張正友標(biāo)定法簡(jiǎn)介

首先一個(gè)問題,張正友是誰?

張正友博士。世界著名的計(jì)算機(jī)視覺和多媒體技術(shù)的專家,ACM Fellow,IEEE Fellow。剛剛從微軟研究院視覺技術(shù)組離職,加入騰訊AI Lab擔(dān)任負(fù)責(zé)人。他在立體視覺、三維重建、運(yùn)動(dòng)分析、圖像配準(zhǔn)、攝像機(jī)標(biāo)定等方面都有開創(chuàng)性的貢獻(xiàn)。

那么張正友標(biāo)定法又是什么?

“張正友標(biāo)定法”是張正友博士在1999年發(fā)表在國際頂級(jí)會(huì)議ICCV上的論文《Flexible Camera Calibration By Viewing a Plane From Unknown Orientations》中,提出的一種利用平面棋盤格進(jìn)行相機(jī)標(biāo)定的實(shí)用方法。該方法既克服了攝影標(biāo)定法需要的高精度三維標(biāo)定物的缺點(diǎn),又解決了之前自標(biāo)定法魯棒性差的難題。

標(biāo)定過程僅需使用一個(gè)打印出來的棋盤格,并從不同方向拍攝幾組圖片即可,任何人都可以自己制作標(biāo)定圖案,不僅實(shí)用靈活方便,而且精度很高,魯棒性好。因此很快被全世界廣泛采用,極大的促進(jìn)了三維計(jì)算機(jī)視覺從實(shí)驗(yàn)室走向真實(shí)世界的進(jìn)程。

世界正需要這樣的發(fā)明,張正友標(biāo)定法也是張正友博士的成名之作。

下面我們看一下,張正友標(biāo)定法的具體流程,主要是數(shù)學(xué)計(jì)算較多,要細(xì)心慢慢看。

張正友標(biāo)定法計(jì)算流程

首先我們看一下張正友標(biāo)定法使用OpenCV的計(jì)算流程:

  1. 準(zhǔn)備標(biāo)定圖片,原理上三張就夠,一般在多個(gè)角度采集20張左右。
  2. 提取標(biāo)定板的關(guān)鍵點(diǎn),并計(jì)算出標(biāo)定板上關(guān)鍵點(diǎn)的實(shí)際相對(duì)位置,一般將標(biāo)定板當(dāng)做XY平面,Z為0,標(biāo)定板第一個(gè)點(diǎn)為坐標(biāo)原點(diǎn)。
  3. 相機(jī)標(biāo)定,通過張正友標(biāo)定法計(jì)算出內(nèi)參外參以及畸變。
  4. 對(duì)標(biāo)定結(jié)果進(jìn)行評(píng)價(jià),一般通過重投影的誤差進(jìn)行評(píng)價(jià)。
  5. 查看標(biāo)定效果,利用標(biāo)定結(jié)果對(duì)棋盤圖進(jìn)行矯正

從上邊的過程可以看出,我們其實(shí)只有第三步是真正的解算過程,我們現(xiàn)在來看一下大致的方法。

首先用于標(biāo)定的棋盤格是三維場(chǎng)景中的一個(gè)平面Π,其在成像平面的像是另一個(gè)平面??,知道了兩個(gè)平面的對(duì)應(yīng)點(diǎn)的坐標(biāo),就可以求解得到兩個(gè)平面的單應(yīng)矩陣??。其中,標(biāo)定的棋盤格是特制的,其角點(diǎn)的坐標(biāo)是已知的;圖像中的角點(diǎn),可以通過角點(diǎn)提取算法得到,這樣就可以得到棋盤平面Π和圖像平面??的單應(yīng)矩陣??,即:

其中??是像點(diǎn)坐標(biāo),??是標(biāo)定的棋盤坐標(biāo),K是相機(jī)內(nèi)參,為了不失一般性,可以在相機(jī)的內(nèi)參矩陣上添加一個(gè)扭曲參數(shù)??。即:

這樣就可以得到下面的等式:

是不是通過對(duì)應(yīng)的點(diǎn)對(duì)解得??后,則可以通過上面的等式得到相機(jī)的內(nèi)參數(shù)??,以及外參旋轉(zhuǎn)矩陣??和平移向量??。

至于怎么解出來,我們接著看。

設(shè)棋盤格所在的平面為世界坐標(biāo)系中??=0的平面,這樣棋盤格的任一角點(diǎn)??的世界坐標(biāo)為(??,??,0),根據(jù)小孔相機(jī)模型:

再根據(jù)單應(yīng)性原則:

根據(jù)上面兩式則有:

將旋轉(zhuǎn)矩陣??的各個(gè)列向量和平移向量??使用??的列向量表示:

又因?yàn)椋??是旋轉(zhuǎn)矩陣,則其是正交矩陣,也就是其任意兩個(gè)列向量的內(nèi)積為0,列向量的模為1,則有:

即:

那么對(duì)于一幅棋盤標(biāo)定版的圖像(一個(gè)單應(yīng)矩陣)可以獲得兩個(gè)對(duì)內(nèi)參數(shù)的約束等式。

我們令:

矩陣??是一個(gè)對(duì)稱矩陣,其未知量只有6個(gè),將6個(gè)未知量寫為向量的形式:

則有:

其中:

則約束等式有:

寫成矩陣的形式有:

假如有??幅圖像,則:

其中,??是一個(gè)2??×6的矩陣,??是一個(gè)6維向量,所以

  • 當(dāng)??≥3,可以得到??的唯一解;
  • 當(dāng)??=2,則可以假設(shè)扭曲參數(shù)??=0作為額外的約束條件
  • 當(dāng)??=1,則值能計(jì)算兩個(gè)相機(jī)的內(nèi)參數(shù)

對(duì)于方程????=0可以使用SVD求得其最小二乘解。對(duì)??????進(jìn)行SVD分解,其最小特征值對(duì)應(yīng)的特征向量就是????=0的最小二乘解,從而求得矩陣??。由于這里得到的??的估計(jì)值是在相差一個(gè)常量因子下得到的,所以有:

所以則有:

其中fx = ??(1/??),fy = ??(1/??) 。

為了進(jìn)一步增加標(biāo)定結(jié)果的可靠性,可以使用最大似然估計(jì)來優(yōu)化上面估計(jì)得到的結(jié)果。

假設(shè)同一相機(jī)從??個(gè)不同的角度的得到了??幅標(biāo)定板的圖像,每幅圖像上有??個(gè)像點(diǎn)。??????表示第??幅圖像上第??個(gè)像點(diǎn)對(duì)應(yīng)的標(biāo)定板上的三維點(diǎn),則:

??? (??,????,????,??????) 表示??????的像點(diǎn)。其中,????,????表示第??幅圖像對(duì)應(yīng)相機(jī)的旋轉(zhuǎn)矩陣和平移向量,??是相機(jī)的內(nèi)參數(shù)。則像點(diǎn)??????的概率密度函數(shù)是:

構(gòu)造似然函數(shù):

為了能夠讓??取得最大值,需要最小化下面的值

問題變成了一個(gè)非線性優(yōu)化問題,利用上面得到的解作為初始值,迭代得到最優(yōu)解。這個(gè)過程就是在減少重投影誤差的過程。

至此,通過張正友標(biāo)定法,我們獲得了相機(jī)的內(nèi)參以及外參,但是畸變沒有獲得。張正友標(biāo)定法只關(guān)注了影響較大的徑向畸變。畸變的解算有點(diǎn)類似內(nèi)參解算,暫時(shí)先不列舉,腦袋有點(diǎn)炸了。

然而,我沒有辮子

OpenCV 張正友標(biāo)定流程展示[代碼]

#include <iostream>
#include <opencv2/opencv.hpp>
#include  <boost/filesystem.hpp>

std::vector<std::string> get_all_image_file(std::string image_folder_path){
    boost::filesystem::path dirpath = image_folder_path;
    boost::filesystem::directory_iterator end;
    std::vector<std::string> files;
    for (boost::filesystem::directory_iterator iter(dirpath); iter != end; iter++)
    {
        boost::filesystem::path p = *iter;
        files.push_back(dirpath.string()+ "/"+ p.leaf().string());
    }
     std::sort(files.begin(),files.end());
    return files;
}

std::vector<cv::Mat> get_all_iamge(std::string image_folder_path)
{
    std::vector<cv::Mat> images;
    std::vector<std::string> image_files_path = get_all_image_file(image_folder_path);
    for(int i=0; i<  image_files_path.size() ;i++){
        cv::Mat image;
        image = cv::imread(image_files_path[i]);
        images.push_back(image);
    }
    return images;
}

int find_chessboard(cv::Mat image, std::vector<cv::Point2f> &image_points, cv::Size board_size)
{
    if (0 == findChessboardCorners(image,board_size,image_points))
    {
        std::cout<<"can not find chessboard corners!\n";
        return 0;
    }
    else
    {
        cv::Mat view_gray;
        cv::cvtColor(image,view_gray,cv::COLOR_RGB2GRAY);
        cv::find4QuadCornerSubpix(view_gray,image_points,cv::Size(11,11)); //對(duì)粗提取的角點(diǎn)進(jìn)行亞像素精確化
//        int nChessBoardFlags = cv::CALIB_CB_EXHAUSTIVE | cv::CALIB_CB_ACCURACY;
//        bool bFindResult = findChessboardCornersSB( view_gray,board_size,image_points,nChessBoardFlags );   
//        Opencv4 識(shí)別棋盤格方法,比opencv3有較大提升
    }
    return 1;
}

int init_chessboard_3dpoints(cv::Size board_size, std::vector<cv::Point3f> &points, float point_size)
{
    cv::Size2f square_size = cv::Size2f(point_size,point_size);
    for (int i=0;i<board_size.height;i++){
        for (int j=0;j<board_size.width;j++){
            cv::Point3f realPoint;
            realPoint.x = j*square_size.width;
            realPoint.y = i*square_size.height;
            realPoint.z = 0;
            points.push_back(realPoint);
        }
    }
    return 0;
}


void calib_monocular(std::vector<cv::Mat> images){
    cv::Size image_szie;
    cv::Size board_size(4,11);
    std::vector<cv::Mat> images_tvecs_mat;
    std::vector<cv::Mat> images_rvecs_mat;
    image_szie.width = images[0].cols;
    image_szie.height = images[0].rows;
    std::vector<std::vector<cv::Point2f> > images_points;
  // 識(shí)別所有圖片的棋盤格
    for(int i=0;i<images.size();i++){
        std::vector<cv::Point2f> image_points;
        if(find_chessboard(images[i],image_points,board_size)>0){
             images_points.push_back(image_points);
        }
    }

    std::vector<cv::Point3f> image_points_in3d;
// 計(jì)算棋盤格角點(diǎn)在棋盤格坐標(biāo)系中的位置
    init_chessboard_3dpoints(board_size,image_points_in3d,0.045);  // 0.045為棋盤格一個(gè)格子的大小
    std::vector<std::vector<cv::Point3f> > images_points_in3d;
// 生成所有識(shí)別出的標(biāo)定板對(duì)應(yīng)在各自棋盤格坐標(biāo)系中的位置
    for(int i=0;i<images_points.size();i++){
        images_points_in3d.push_back(image_points_in3d);
    }
    cv::Mat intrinsic,distortion;
// 使用張正友標(biāo)定法計(jì)算內(nèi)參畸變以及外參
    cv::calibrateCamera(images_points_in3d,images_points,image_szie,
                        intrinsic,distortion,
                        images_rvecs_mat,images_tvecs_mat);
}

int main(int argc, char *argv[])
{
    std::string image_file_path = argv[1];
    std::vector<cv::Mat> images = get_all_iamge(image_file_path);
    calib_monocular(images);
    return 0;
}
標(biāo)定板示例

總結(jié)

張正友標(biāo)定法的思路并不是很難,主要是解算的數(shù)學(xué)原理較復(fù)雜,需要有比較打的耐心看下去,我現(xiàn)在也只能看懂,讓自己完全推導(dǎo)一遍還是挺難的。張正友標(biāo)定法更重要的是將標(biāo)定這項(xiàng)工作簡(jiǎn)潔化,不在需要精密高額的設(shè)備,而只需要通過打印標(biāo)定板就可以獲得比較好的效果。

在實(shí)際的標(biāo)定項(xiàng)目中,還是需要注意很多的事情,以下是我在標(biāo)定時(shí)用的一些小trick或者一些注意點(diǎn):

  • 比如某個(gè)點(diǎn)識(shí)別錯(cuò)了,要通過重投影誤差將其剔除,然后重新計(jì)算標(biāo)定結(jié)果。
  • 增加圖片的數(shù)目,標(biāo)定板在圖片中的各個(gè)角落都要有著各個(gè)角度的分布。
  • 對(duì)于畸變不大的圖片,opencv 中圓形標(biāo)定板的效果要比棋盤格的效果要好,opencv4 棋盤格識(shí)別精度有較大提升,但還是建議用圓形。

重要的事情說三遍:

如果我的文章對(duì)您有所幫助,那就點(diǎn)贊加個(gè)關(guān)注唄 ( * ^ __ ^ * )

如果我的文章對(duì)您有所幫助,那就點(diǎn)贊加個(gè)關(guān)注唄 ( * ^ __ ^ * )

如果我的文章對(duì)您有所幫助,那就點(diǎn)贊加個(gè)關(guān)注唄 ( * ^ __ ^ * )

傳統(tǒng)2D計(jì)算機(jī)視覺學(xué)習(xí)筆記目錄------->傳送門
傳統(tǒng)3D計(jì)算機(jī)視覺學(xué)習(xí)筆記目錄------->傳送門

任何人或團(tuán)體、機(jī)構(gòu)全部轉(zhuǎn)載或者部分轉(zhuǎn)載、摘錄,請(qǐng)保留本博客鏈接或標(biāo)注來源。博客地址:開飛機(jī)的喬巴

作者簡(jiǎn)介:開飛機(jī)的喬巴(WeChat:zhangzheng-thu),現(xiàn)主要從事機(jī)器人抓取視覺系統(tǒng)以及三維重建等3D視覺相關(guān)方面,另外對(duì)slam以及深度學(xué)習(xí)技術(shù)也頗感興趣,歡迎加我微信或留言交流相關(guān)工作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容