全自动泊车系统研究之鱼眼睛相机的标定矫正

一 、全自动泊车研究之鱼眼摄像头标定

整体介绍

全自动泊车项目代码:
链接: link.

本部分鱼眼相机的标定代码****/AutoParking/Script/calibration.cpp****

最近在整理毕业论文,正好回忆下关于全自动泊车的项目,这是关于基于视觉的全自动泊车的demo,采用4个鱼眼摄像头采集图像。
整个软件设计架构包括:鱼眼相机标定矫正,透视变换,360度环视图,车位线的检测以及障碍物(地锁,限位杆等)等其他模块。整个项目分为每个部分来记录;

相机模型(针孔相机模型)

  • 介绍:

    相机将三维世界中的坐标点(真实距离)映射到二维图像平面(像素)的过程,这一过程可以建成我们熟悉的小孔成像模型;由于相机的镜头的存在,使得光线投影到图像平面的过程中会发生畸变,这就需要我们前期进行标定求出相机的内参(有的相机买来可以询问商家获取内部参数);

  • 坐标系
    首先,世界坐标系、相机坐标系、图像坐标系、像素坐标系以及这四个坐标系的转换关系:

    世界坐标系:是客观三维世界的绝对坐标系,也称客观坐标系。因为数码相机安放在三维空间中,我们需要世界坐标系这个基准坐标系来描述数码相机的位置,并且用它来描述安放在此三维环境中的其它任何物体的位置,用(Xw, Yw, Zw)表示其坐标值。

    相机坐标系(光心坐标系):以相机的光心为坐标原点,X 轴和Y 轴分别平行于图像坐标系的 X 轴和Y 轴,相机的光轴为Z 轴,用(Xc, Yc, Zc)表示其坐标值。

    图像坐标系:以图像平面的中心为坐标原点,X轴和Y 轴分别平行于图像平面的两条垂直边,用( x , y )表示其坐标值。图像坐标系是用物理单位(例如毫米)表示像素在图像中的位置。

    像素坐标系:以图像平面的左上角顶点为原点,X 轴和Y 轴分别平行于图像坐标系的 X 轴和Y 轴,用(u , v )表示其坐标值。数码相机采集的图像首先是形成标准电信号的形式,然后再通过模数转换变换为数字图像。每幅图像的存储形式是M × N的数组,M 行 N 列的图像中的每一个元素的数值代表的是图像点的灰度。这样的每个元素叫像素,像素坐标系就是以像素为单位的图像坐标系。

接下来进入正题,针孔相机模型如下:

在这里插入图片描述
在左图中设O-X-Y-Z为相机坐标,O为摄像机的光心,f为焦距(透镜到成像平面的距离),整个图像为物理成像平面,O’为图像中心,像素坐标原点通常在左上角。

将左图的模型简化为相似三角形,P为真实空间的一点,P’为P点投影到图像上的一点,根据三角形相似的性质得到: Z / f = X / X ′ = Y / Y ′ Z/ f = X / X' = Y/Y' Z/f=X/X=Y/Y

简单的手推:

在这里插入图片描述

最终的图像坐标到相机坐标的转化关系为:
在这里插入图片描述其中fx, fy分别是图像水平轴和垂直轴的尺度因子。K的参数中只包含焦距、主点坐标等只由相机的内部结构决定,因此称 K 为内部参数矩阵,fx, fy , cx, cy叫做内部参数。本项目中只考虑内参,外参的标定后面特殊处理。

畸变模型(进行矫正)

  • 介绍:
    相机的前方通常会有一个透镜,会对成像产生影响,称为畸变

    由透镜形状引起的畸变称为径向畸变,包括桶形畸变和枕形畸变。桶形畸变是由于图像放大率随着与光轴之间的距离增加而减小;枕形畸变相反。

在这里插入图片描述
径向畸变的程度会随着与中心距离的增加而增加,可以通过下面公式进行矫正。
在这里插入图片描述
透镜在装机过程中不和成像平面严格平行会引入切向畸变,切向畸变通过下面公式矫正。

在这里插入图片描述
最终的x,y方向的畸变系数为:
在这里插入图片描述

/AutoParking/Script/calibration.cpp

下面展示一些 内联代码片

// An highlighted block
Mat imageSrc = imread(imageFileName);
        imshow(imageFileName, imageSrc);
        Mat image;//
        //copyMakeBorder(imageSrc, image, (int)(y_expand / 2), (int)(y_expand / 2), (int)(x_expand / 2), (int)(x_expand / 2), BORDER_CONSTANT);
        Mat imageGray;

        cvtColor(imageSrc, imageGray, CV_RGB2GRAY);//
        bool patternfound = findChessboardCorners(imageSrc, board_size, corners, CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE +
                                                                                 CALIB_CB_FAST_CHECK);
        if (!patternfound)
        {
            cout << "img" << i + 1  << endl;
            conner_flag = false;
            break;
        }
        else
        {
            /*  SubPix */
            cornerSubPix(imageGray, corners, Size(11, 11), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
            Mat imageTemp = imageSrc.clone();//
            for (int j = 0; j < corners.size(); j++)
            {
                circle(imageTemp, corners[j], 10, Scalar(0, 0, 255), 2, 8, 0);//
            }
            string imageFileName;
            std::stringstream StrStm;
            StrStm << i + 1;
            StrStm >> imageFileName;
            imageFileName += "_corner.jpg";
            imageFileName = root+imageFileName;
            imwrite(imageFileName, imageTemp);
            cout << "img" << i + 1 << endl;

            successImageNum = successImageNum + 1;
            corners_Seq.push_back(corners);
        }
        image_Seq.push_back(imageSrc);
    }
    if (!conner_flag)
        return 0;
    cout << "......" << endl;
    Size square_size = Size(20, 20);
    vector<vector<Point3f>>  object_Points;

    vector<int>  point_counts;
    for (int t = 0; t < successImageNum; t++)
    {
        vector<Point3f> tempPointSet;
        for (int i = 0; i < board_size.height; i++)
        {
            for (int j = 0; j < board_size.width; j++)
            {
                Point3f tempPoint;
                tempPoint.x = i * square_size.width;
                tempPoint.y = j * square_size.height;
                tempPoint.z = 0;
                tempPointSet.push_back(tempPoint);
            }
        }
        object_Points.push_back(tempPointSet);
    }
    for (int i = 0; i < successImageNum; i++)
    {
        point_counts.push_back(board_size.width*board_size.height);
    }
    Size image_size = image_Seq[0].size();
    cv::Matx33d intrinsic_matrix;
    cv::Vec4d distortion_coeffs;
    std::vector<cv::Vec3d> rotation_vectors;
    std::vector<cv::Vec3d> translation_vectors;
    int flags = 0;
    flags |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
    flags |= cv::fisheye::CALIB_CHECK_COND;
    flags |= cv::fisheye::CALIB_FIX_SKEW;
    fisheye::calibrate(object_Points, corners_Seq, image_size, intrinsic_matrix, distortion_coeffs, rotation_vectors, translation_vectors, flags, cv::TermCriteria(3, 20, 1e-6));

    cout << "\nStoring Intrinsics.xml and Distortions.xml files\n\n";
    //FileStorage fs("intrinsics.xml", FileStorage::WRITE);
    //fs << "image_width" << image_size.width << "image_height" << image_size.height
    //	<< "camera_matrix" << intrinsic_matrix << "distortion_coefficients"
    //	<< distortion_coeffs;
    //fs.release();
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值