张正友视觉标定算法学习笔记

本博客内容来源于网络以及其他书籍,结合自己学习的心得进行重编辑,因为看了很多文章不便一一标注引用,如图片文字等侵权,请告知删除。

传统2D计算机视觉学习笔记目录------->传送门
传统3D计算机视觉学习笔记目录------->传送门

前言

在阅读本篇文章之前,建议首先要了解知道什么是相机的针孔模型,我在之前的文章小孔相机参数学习笔记中有详细的解释,或者说不是建议,而是必须要知道,不然也不知道这篇文章在干什么。本片文章主要来讲张正友视觉标定法,在讲张正友之前,我们先说一下什么是相机标定。

所谓相机标定,就是通过实验等方法将相机模型中的未知数给解算出来。例如小孔模型我们一般需要节算出内参矩阵 fx,fy,cx,cy 以及畸变参数。

为什么要标定出这些参数呢?一个是因为每个镜头的畸变程度各不相同,通过相机标定可以校正这种镜头畸变矫正畸变,生成矫正后的图像;另一个是根据获得的图像重构三维场景,在之前文章我们有写过PNP算法,就是通过相机的内参以及畸变解算出目标在相机下的位置,以及双目3D相机也需要相机模型参数进行计算等。

有很多标定方法,比如传统相机标定法、主动视觉相机标定方法、相机自标定法等,但是现在用的最多的就是相机的自标定法,而相机的自标定法的基础就是张正友标定法的解算流程。下面我们一起来看一下张正友标定法。


张正友标定法简介

首先一个问题,张正友是谁?

张正友博士。世界著名的计算机视觉和多媒体技术的专家,ACM Fellow,IEEE Fellow。刚刚从微软研究院视觉技术组离职,加入腾讯AI Lab担任负责人。他在立体视觉、三维重建、运动分析、图像配准、摄像机标定等方面都有开创性的贡献。

那么张正友标定法又是什么?

“张正友标定法”是张正友博士在1999年发表在国际顶级会议ICCV上的论文《Flexible Camera Calibration By Viewing a Plane From Unknown Orientations》中,提出的一种利用平面棋盘格进行相机标定的实用方法。该方法既克服了摄影标定法需要的高精度三维标定物的缺点,又解决了之前自标定法鲁棒性差的难题。

标定过程仅需使用一个打印出来的棋盘格,并从不同方向拍摄几组图片即可,任何人都可以自己制作标定图案,不仅实用灵活方便,而且精度很高,鲁棒性好。因此很快被全世界广泛采用,极大的促进了三维计算机视觉从实验室走向真实世界的进程。

世界正需要这样的发明,张正友标定法也是张正友博士的成名之作。

下面我们看一下,张正友标定法的具体流程,主要是数学计算较多,要细心慢慢看。

张正友标定法计算流程

首先我们看一下张正友标定法使用OpenCV的计算流程:

  1. 准备标定图片,原理上三张就够,一般在多个角度采集20张左右。
  2. 提取标定板的关键点,并计算出标定板上关键点的实际相对位置,一般将标定板当做XY平面,Z为0,标定板第一个点为坐标原点。
  3. 相机标定,通过张正友标定法计算出内参外参以及畸变。
  4. 对标定结果进行评价,一般通过重投影的误差进行评价。
  5. 查看标定效果,利用标定结果对棋盘图进行矫正

从上边的过程可以看出,我们其实只有第三步是真正的解算过程,我们现在来看一下大致的方法。

首先用于标定的棋盘格是三维场景中的一个平面Π,其在成像平面的像是另一个平面𝜋,知道了两个平面的对应点的坐标,就可以求解得到两个平面的单应矩阵𝐻。其中,标定的棋盘格是特制的,其角点的坐标是已知的;图像中的角点,可以通过角点提取算法得到,这样就可以得到棋盘平面Π和图像平面𝜋的单应矩阵𝐻,即:

其中𝑝是像点坐标,𝑃是标定的棋盘坐标,K是相机内参,为了不失一般性,可以在相机的内参矩阵上添加一个扭曲参数𝛾。即:

这样就可以得到下面的等式:

是不是通过对应的点对解得𝐻后,则可以通过上面的等式得到相机的内参数𝐾,以及外参旋转矩阵𝑅和平移向量𝑡。

至于怎么解出来,我们接着看。

设棋盘格所在的平面为世界坐标系中𝑍=0的平面,这样棋盘格的任一角点𝑃的世界坐标为(𝑋,𝑌,0),根据小孔相机模型:

再根据单应性原则:

根据上面两式则有:

将旋转矩阵𝑅的各个列向量和平移向量𝑡使用𝐻的列向量表示:

又因为,𝑅是旋转矩阵,则其是正交矩阵,也就是其任意两个列向量的内积为0,列向量的模为1,则有:

即:

那么对于一幅棋盘标定版的图像(一个单应矩阵)可以获得两个对内参数的约束等式。

我们令:

矩阵𝐵是一个对称矩阵,其未知量只有6个,将6个未知量写为向量的形式:

则有:

其中:

则约束等式有:

写成矩阵的形式有:

假如有𝑛幅图像,则:

其中,𝑉是一个2𝑛×6的矩阵,𝑏是一个6维向量,所以

  • 当𝑛≥3,可以得到𝑏的唯一解;
  • 当𝑛=2,则可以假设扭曲参数𝛾=0作为额外的约束条件
  • 当𝑛=1,则值能计算两个相机的内参数

对于方程𝑉𝑏=0可以使用SVD求得其最小二乘解。对𝑉𝑇𝑉进行SVD分解,其最小特征值对应的特征向量就是𝑉𝑏=0的最小二乘解,从而求得矩阵𝐵。由于这里得到的𝐵的估计值是在相差一个常量因子下得到的,所以有:

所以则有:

其中fx = 𝛼(1/𝛾),fy = 𝛽(1/𝛾) 。

为了进一步增加标定结果的可靠性,可以使用最大似然估计来优化上面估计得到的结果。

假设同一相机从𝑛个不同的角度的得到了𝑛幅标定板的图像,每幅图像上有𝑚个像点。𝑀𝑖𝑗表示第𝑖幅图像上第𝑗个像点对应的标定板上的三维点,则:

𝑚̂ (𝐾,𝑅𝑖,𝑡𝑖,𝑀𝑖𝑗) 表示𝑀𝑖𝑗的像点。其中,𝑅𝑖,𝑡𝑖表示第𝑖幅图像对应相机的旋转矩阵和平移向量,𝐾是相机的内参数。则像点𝑚𝑖𝑗的概率密度函数是:

构造似然函数:

为了能够让𝐿取得最大值,需要最小化下面的值

问题变成了一个非线性优化问题,利用上面得到的解作为初始值,迭代得到最优解。这个过程就是在减少重投影误差的过程。

至此,通过张正友标定法,我们获得了相机的内参以及外参,但是畸变没有获得。张正友标定法只关注了影响较大的径向畸变。畸变的解算有点类似内参解算,暂时先不列举,脑袋有点炸了。

然而,我没有辫子

OpenCV 张正友标定流程展示[代码]

#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)); //对粗提取的角点进行亚像素精确化
//        int nChessBoardFlags = cv::CALIB_CB_EXHAUSTIVE | cv::CALIB_CB_ACCURACY;
//        bool bFindResult = findChessboardCornersSB( view_gray,board_size,image_points,nChessBoardFlags );   
//        Opencv4 识别棋盘格方法,比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;
  // 识别所有图片的棋盘格
    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;
// 计算棋盘格角点在棋盘格坐标系中的位置
    init_chessboard_3dpoints(board_size,image_points_in3d,0.045);  // 0.045为棋盘格一个格子的大小
    std::vector<std::vector<cv::Point3f> > images_points_in3d;
// 生成所有识别出的标定板对应在各自棋盘格坐标系中的位置
    for(int i=0;i<images_points.size();i++){
        images_points_in3d.push_back(image_points_in3d);
    }
    cv::Mat intrinsic,distortion;
// 使用张正友标定法计算内参畸变以及外参
    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;
}
标定板示例

总结

张正友标定法的思路并不是很难,主要是解算的数学原理较复杂,需要有比较打的耐心看下去,我现在也只能看懂,让自己完全推导一遍还是挺难的。张正友标定法更重要的是将标定这项工作简洁化,不在需要精密高额的设备,而只需要通过打印标定板就可以获得比较好的效果。

在实际的标定项目中,还是需要注意很多的事情,以下是我在标定时用的一些小trick或者一些注意点:

  • 比如某个点识别错了,要通过重投影误差将其剔除,然后重新计算标定结果。
  • 增加图片的数目,标定板在图片中的各个角落都要有着各个角度的分布。
  • 对于畸变不大的图片,opencv 中圆形标定板的效果要比棋盘格的效果要好,opencv4 棋盘格识别精度有较大提升,但还是建议用圆形。

重要的事情说三遍:

如果我的文章对您有所帮助,那就点赞加个关注呗 ( * ^ __ ^ * )

如果我的文章对您有所帮助,那就点赞加个关注呗 ( * ^ __ ^ * )

如果我的文章对您有所帮助,那就点赞加个关注呗 ( * ^ __ ^ * )

传统2D计算机视觉学习笔记目录------->传送门
传统3D计算机视觉学习笔记目录------->传送门

任何人或团体、机构全部转载或者部分转载、摘录,请保留本博客链接或标注来源。博客地址:开飞机的乔巴

作者简介:开飞机的乔巴(WeChat:zhangzheng-thu),现主要从事机器人抓取视觉系统以及三维重建等3D视觉相关方面,另外对slam以及深度学习技术也颇感兴趣,欢迎加我微信或留言交流相关工作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容