鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)

这篇具有很好参考价值的文章主要介绍了鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

opencv系列



一、鱼眼镜头模型

鱼眼镜头一般是由十几个不同的透镜组合而成的,在成像的过程中,入射光线经过不同程度的折射,投影到尺寸有限的成像平面上,使得鱼眼镜头与普通镜头相比起来拥有了更大的视野范围。下图表示出了鱼眼相机的一般组成结构。最前面的两个镜头发生折射,使入射角减小,其余的镜头相当于一个成像镜头,这种多元件的构造结构使对鱼眼相机的折射关系的分析变得相当复杂。
鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)
研究表明鱼眼相机成像时遵循的模型可以近似为单位球面投影模型。可以将鱼眼相机的成像过程分解成两步:第一步,三维空间点线性地投影到一个球面上,它是一个虚拟的单位球面,它的球心与相机坐标系的原点重合;第二步,单位球面上的点投影到图像平面上,这个过程是非线性的。下图表示出了鱼眼相机的成像过程。
鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)
我们知道,普通相机成像遵循的是针孔相机模型,在成像过程中实际场景中的直线仍被投影为图像平面上的直线。但是鱼眼相机如果按照针孔相机模型成像的话,投影图像会变得非常大,当相机视场角达到180°时,图像甚至会变为无穷大。所以,鱼眼相机的投影模型为了将尽可能大的场景投影到有限的图像平面内,允许了相机畸变的存在。并且由于鱼眼相机的径向畸变非常严重,所以鱼眼相机主要的是考虑径向畸变,而忽略其余类型的畸变。

二、投影函数

为了将尽可能大的场景投影到有限的图像平面内,鱼眼相机会按照一定的投影函数来设计。根据投影函数的不同,鱼眼相机的设计模型大致能被分为四种:等距投影模型、等立体角投影模型、正交投影模型和体视投影模型。下面的四种鱼眼相机的投影模型反映出了空间中的一点P是如何投影到球面上,然后到图像平面上成像的。

等距投影模型

鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)
鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)

上述式子中,rd表示鱼眼图像中的点到畸变中心的距离,是鱼眼相机的焦距,是入射光线与鱼眼相机光轴之间的夹角,即入射角。

等立体角投影模型

鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)

鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)

正交投影模型

鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)
鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)

体视投影模型

鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)

鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)

三、OpenCV中的鱼眼相机模型

OpenCV中使用的模型是由Kannala提出的一种鱼眼相机的一般近似模型。在等距投影模型的基础上提出来的。下面来详细分析其鱼眼相机模型的提出过程。我们可以将鱼眼相机模型的形式统一以等距投影模型的形式来表示,即
鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)

对实际的鱼眼镜头来说,它们不可能精确地按照投影模型来设计,所以为了方便鱼眼相机的标定,Kannala提出了一种鱼眼相机的一般多项式近似模型。通过前面的四个模型,可以发现 θd是θ的奇函数,而且将这些式子按泰勒级数展开,发现 θd可以用θ 的奇次多项式表示,即
鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)

为了实际计算的方便,需要确定式中 θd取到的次幂数。Kannala提出取式的前五项即取到的九次方,就给出了足够的自由度来很好地近似各种投影模型。 θd的一次项系数可以为1,于是OpenCV中使用的鱼眼相机模型为:
鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)

上式表示的模型是根据四种鱼眼相机投影模型得出的一种通用鱼眼相机多项式模型。这种模型根据θ能够得到 θd ,即通过无畸变图像中的点能够计算出鱼眼图像中的畸变点。这种模型在OpenCV的鱼眼相机标定方法中是适用的,因为OpenCV借助标定板对鱼眼相机进行标定。从空间点到鱼眼图像上的点的变换过程可用式子表示为:
鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)

上面式子中, X表示空间点,Xc表示相机坐标系下对应的空间点, Rt分别是两个坐标系之间的旋转矩阵和平移向量, (u,v)T 表示投影到鱼眼图像上的对应点。OpenCV中对鱼眼相机的标定步骤能够分成四步:

(1)初始化内参数;
(2)初始化外参数;
(3)使用LM算法最小化定位的图像点和投影的图像点之间的投影误差;
(4)确定结果。

四、标定(C++)实现

使用的函数

由于鱼眼镜头和针孔镜头的模型不一样,对于鱼眼镜头的模型在之前的博客中已经做了详细介绍,这里直接使用OpenCV中的cv::fisheye::calibrate()函数进行标定。函数原型如下,需要输入目标点集,图像点集、图像尺寸。函数输出相机内参,畸变系数,旋转矩阵和平移向量,以及反投影误差。

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

采集标定图像

采集若干拍摄有标定棋盘格的图像,并使棋盘格出现在画面的各个位置,特别是边缘位置。如下图所示:
鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)

标定代码

#include "stdio.h"
#include <iostream>
#include <fstream>
#include <io.h>

#include "opencv2/opencv.hpp"
#include <opencv2/core/core.hpp>
#include "opencv2/calib3d/calib3d.hpp"
#include <opencv2/highgui/highgui.hpp>

using namespace std;
using namespace cv;

void getFiles(string path, vector<string>& files)
{
    //文件句柄
    intptr_t hFile = 0;
    //文件信息
    struct _finddata_t fileinfo;
    string p;
    if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
    {
        do
        {
            //如果是目录,迭代之
            //如果不是,加入列表
            if ((fileinfo.attrib &  _A_SUBDIR))
            {
                if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
                    getFiles(p.assign(path).append("\\").append(fileinfo.name), files);
            }
            else
            {
                files.push_back(p.assign(path).append("\\").append(fileinfo.name));
            }
        } while (_findnext(hFile, &fileinfo) == 0);
        _findclose(hFile);
    }
}

int main(int argc, char** argv)
{   
    string filePath = ".\\720PPcalib\\front";
    vector<string> files;

    获取该路径下的所有文件
    getFiles(filePath, files);

    const int board_w = 6;
    const int board_h = 4;
    const int NPoints = board_w * board_h;//棋盘格内角点总数
    const int boardSize = 30; //mm
    Mat image,grayimage;
    Size ChessBoardSize = cv::Size(board_w, board_h);
    vector<Point2f> tempcorners;

    int flag = 0;
    flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
    //flag |= cv::fisheye::CALIB_CHECK_COND;
    flag |= cv::fisheye::CALIB_FIX_SKEW;
    //flag |= cv::fisheye::CALIB_USE_INTRINSIC_GUESS;

    vector<Point3f> object;
    for (int j = 0; j < NPoints; j++)
    {
        object.push_back(Point3f((j % board_w) * boardSize, (j / board_w) * boardSize, 0)); 
    }

    cv::Matx33d intrinsics;//z:相机内参
    cv::Vec4d distortion_coeff;//z:相机畸变系数

    vector<vector<Point3f> > objectv;
    vector<vector<Point2f> > imagev;

    Size corrected_size(1280, 720);
    Mat mapx, mapy;
    Mat corrected;

    ofstream intrinsicfile("intrinsics_front1103.txt");
    ofstream disfile("dis_coeff_front1103.txt");
    int num = 0;
    bool bCalib = false;
    while (num < files.size())
    {
        image = imread(files[num]);

        if (image.empty())
            break;
        imshow("corner_image", image);
        waitKey(10);
        cvtColor(image, grayimage, CV_BGR2GRAY);
        IplImage tempgray = grayimage;
        bool findchessboard = cvCheckChessboard(&tempgray, ChessBoardSize);

        if (findchessboard)
        {
            bool find_corners_result = findChessboardCorners(grayimage, ChessBoardSize, tempcorners, 3);
            if (find_corners_result)
            {
                cornerSubPix(grayimage, tempcorners, cvSize(5, 5), cvSize(-1, -1), cvTermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
                drawChessboardCorners(image, ChessBoardSize, tempcorners, find_corners_result);
                imshow("corner_image", image);
                cvWaitKey(100);

                objectv.push_back(object);
                imagev.push_back(tempcorners);
                cout << "capture " << num << " pictures" << endl;
            }
        }
        tempcorners.clear();
        num++;
    }

    cv::fisheye::calibrate(objectv, imagev, cv::Size(image.cols,image.rows), intrinsics, distortion_coeff, cv::noArray(), cv::noArray(), flag, cv::TermCriteria(3, 20, 1e-6));  
    fisheye::initUndistortRectifyMap(intrinsics, distortion_coeff, cv::Matx33d::eye(), intrinsics, corrected_size, CV_16SC2, mapx, mapy);

    for(int i=0; i<3; ++i)
    {
        for(int j=0; j<3; ++j)
        {
            intrinsicfile<<intrinsics(i,j)<<"\t";
        }
        intrinsicfile<<endl;
    }
    for(int i=0; i<4; ++i)
    {
        disfile<<distortion_coeff(i)<<"\t";
    }
    intrinsicfile.close();
    disfile.close();

    num = 0;
    while (num < files.size())
    {
        image = imread(files[num++]);

        if (image.empty())
            break;
        remap(image, corrected, mapx, mapy, INTER_LINEAR, BORDER_TRANSPARENT);

        imshow("corner_image", image);
        imshow("corrected", corrected);
        cvWaitKey(200); 
    }

    cv::destroyWindow("corner_image");
    cv::destroyWindow("corrected");

    image.release();
    grayimage.release();
    corrected.release();
    mapx.release();
    mapy.release();

    return 0;
}


标定结果

使用标定的结果进行畸变校正后的结果如下所示,可以看到,原本弯曲的曲线已经变直。

鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)文章来源地址https://www.toymoban.com/news/detail-493205.html

到了这里,关于鱼眼相机成像模型以及基于OpenCV标定鱼眼镜头(C++)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【机器视觉】线阵相机模型说明以及使用HALCON标定助手对线阵相机进行标定

    线阵相机矫正所需参数共17个,其中11个参数为内参,6个参数为外参。 线阵相机内参 CamParam 数量共11个,模型如下: CamParam:= [Focus, Kappa, Sx, Sy, Cx, Cy, ImageWidth, ImageHeight, Vx, Vy, Vz]  Focus : 镜头焦距;  Kappa : 镜头畸变系数;正:枕形畸变 负:桶状畸变  Sx : 传感器像元宽度

    2024年02月16日
    浏览(45)
  • 基于opencv的相机标定C++代码

    事先需要把标定图片放在images目录下:  calibdata.txt的内容是标定图片的路径+图片文件名称: 希望对大家有帮助!!!(目前我使用的VS是2019版本,opencv4_1_2)。 拍摄图像示例  标定结果保存在一个txt文件中:     输入:灰度图像image_gray;角点个数大小corner_size,如Size(9,6

    2024年02月05日
    浏览(35)
  • 基于OpenCV的单目相机标定与三维定位

           相机是产生图像数据的硬件,广泛应用于消费电子、汽车、安防等领域。围绕着相机衍生出一系列的研究与应用领域,包括传统的图像处理和基于深度学习的智能应用等。目前大火的自动驾驶中相机也是重要的硬件组成,如环视用鱼眼相机,adas用周视相机。    

    2024年02月09日
    浏览(37)
  • 基于环视鱼眼相机的全景拼接

    本文主要记录基于环视鱼眼相机的全景拼接过程中遇到的问题及其解决思路 代码来源:https://github.com/Leooncode/surround-view-system-introduction/blob/master/doc/doc.md 1、针对多个鱼眼相机连接问题 鱼眼相机为USB摄像头,与网络摄像头采集方式不同,具体修改为: 对于USB摄像头无需使用

    2024年01月17日
    浏览(41)
  • 基于鱼眼相机的机械臂抓取流程

    相机标定使用ROS中camera_calibration工具进行标定,该工具也可以标定鱼眼相机。标定板黑白格大小为12x8,单个方块大小20mm 标定后即可得到相机内参。后续使用时需要通过参数矫正鱼眼相机的图片 工具标定根据机械臂厂商提供的工具进行 参考手眼标定。 注意事项:image_callba

    2024年02月13日
    浏览(38)
  • 相机成像模型(一)

            如上图所示相机模组由多个元件组成,其中比较重要的元件包括镜头、感光芯片、驱动芯片。镜头的作用是聚集光线,确保良好的成像环境;感光芯片将光信号转换为电信号;驱动芯片则负责信号处理(去噪、白平衡等)与格式转换。         相机的成像过程

    2024年02月08日
    浏览(39)
  • 相机成像模型

    坐标系变换 针孔相机模型存在四个坐标系:世界坐标系、摄像机坐标系、图像物理坐标系和图像像素坐标系 世界坐标系 :绝对坐标系,物体在真实世界中的坐标系(3D) 摄像机坐标系 :以相机的 光心 为坐标系的原点,以平行于图像的x和y方向为x轴和y轴, z轴和光轴平行,

    2024年02月21日
    浏览(41)
  • 相机标定-机器视觉基础(理论推导、Halcon和OpenCV相机标定)

             相机标定是获得目标工件精准坐标信息的基础。首先,必须进行相机内参标定,构建一个模型消除图像畸变;其次,需要对相机和机器人的映射关系进行手眼标定,构建一个模型将图像坐标系上的点映射到世界坐标系。主要分为背景知识、相机内外参模型推导、

    2023年04月21日
    浏览(48)
  • 相机小孔成像模型(逐步推导详解)

    先搞清楚为什么可以简化成小孔成像模型 原则:先简单后复杂,先理想后实际 一、明确四个坐标系:这个是推导的前提! 说明: 1、图像坐标系的坐标原点是成像平面的中心,相机坐标系原点设在光心处,空间中任意一点P可以用相机坐标系和世界坐标系表示。 2、相机坐标

    2024年02月06日
    浏览(35)
  • 机器视觉【1】相机的成像(畸变)模型

    很久没写文章,简单唠一唠。 不知道巧合还是蜀道同归,部门领导设定了些研究课题,用于公司部门员工的超前发展,该课题是“2D to 3D的三维重建”,这一块刚好是我个人看中的一个大方向,所以就有了这一系列的文章。其实我还发现不少同学是没搞清楚 什么是 机器视觉

    2024年02月03日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包