如何使用openCV使用广角镜头正确校准我的相机?

问题描述:

我想用鱼眼镜头校准相机。我为此使用鱼眼镜头模块,但无论我修复了哪些失真参数,都会得到奇怪的结果。 这是我使用的输入图像:https://i.imgur.com/apBuAwF.png如何使用openCV使用广角镜头正确校准我的相机?

其中红色圆圈表示我用来校准相机的角。

这是我能得到的最好,输出:https://imgur.com/a/XeXk5

我目前不通过心脏知道相机传感器尺寸是什么,但基于像素的焦距在我nitrinsic矩阵进行计算,我推断我的传感器尺寸约为3.3毫米(假设我的物理焦距为1.8毫米),这对我来说似乎很现实。然而,当我的输入图像不失真时,我就会变得无稽之谈。有人能告诉我我可能做错了吗?

矩阵和有效值由校准是输出:

K:[263.7291703200009, 0, 395.1618975493187; 
0, 144.3800397321767, 188.9308218101271; 
0, 0, 1] 

D:[0, 0, 0, 0] 

rms: 9.27628 

我的代码:

#include <opencv2/opencv.hpp> 
#include "opencv2/core.hpp" 
#include "opencv2/imgcodecs.hpp" 
#include "opencv2/imgproc.hpp" 
#include "opencv2/highgui.hpp" 
#include "opencv2/ccalib/omnidir.hpp" 

using namespace std; 
using namespace cv; 

vector<vector<Point2d> > points2D; 
vector<vector<Point3d> > objectPoints; 

Mat src; 

//so that I don't have to select them manually every time 
void initializePoints2D() 
{ 
    points2D[0].push_back(Point2d(234, 128)); 
    points2D[0].push_back(Point2d(300, 124)); 
    points2D[0].push_back(Point2d(381, 126)); 
    points2D[0].push_back(Point2d(460, 127)); 
    points2D[0].push_back(Point2d(529, 137)); 
    points2D[0].push_back(Point2d(207, 147)); 
    points2D[0].push_back(Point2d(280, 147)); 
    points2D[0].push_back(Point2d(379, 146)); 
    points2D[0].push_back(Point2d(478, 153)); 
    points2D[0].push_back(Point2d(551, 165)); 
    points2D[0].push_back(Point2d(175, 180)); 
    points2D[0].push_back(Point2d(254, 182)); 
    points2D[0].push_back(Point2d(377, 185)); 
    points2D[0].push_back(Point2d(502, 191)); 
    points2D[0].push_back(Point2d(586, 191)); 
    points2D[0].push_back(Point2d(136, 223)); 
    points2D[0].push_back(Point2d(216, 239)); 
    points2D[0].push_back(Point2d(373, 253)); 
    points2D[0].push_back(Point2d(534, 248)); 
    points2D[0].push_back(Point2d(624, 239)); 
    points2D[0].push_back(Point2d(97, 281)); 
    points2D[0].push_back(Point2d(175, 322)); 
    points2D[0].push_back(Point2d(370, 371)); 
    points2D[0].push_back(Point2d(578, 339)); 
    points2D[0].push_back(Point2d(662, 298)); 


    for(int j=0; j<25;j++) 
    { 
     circle(src, points2D[0].at(j), 5, Scalar(0, 0, 255), 1, 8, 0); 
    } 

    imshow("src with circles", src); 
    waitKey(0); 
} 

int main(int argc, char** argv) 
{ 
    Mat srcSaved; 

    src = imread("images/frontCar.png"); 
    resize(src, src, Size(), 0.5, 0.5); 
    src.copyTo(srcSaved); 

    vector<Point3d> objectPointsRow; 
    vector<Point2d> points2DRow; 
    objectPoints.push_back(objectPointsRow); 
    points2D.push_back(points2DRow); 

    for(int i=0; i<5;i++) 
    { 

     for(int j=0; j<5;j++) 
     { 
      objectPoints[0].push_back(Point3d(5*j,5*i,1));   
     } 
    } 

    initializePoints2D(); 
    cv::Matx33d K; 
    cv::Vec4d D; 
    std::vector<cv::Vec3d> rvec; 
    std::vector<cv::Vec3d> tvec; 


    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_FIX_K1; 
    flag |= cv::fisheye::CALIB_FIX_K2; 
    flag |= cv::fisheye::CALIB_FIX_K3; 
    flag |= cv::fisheye::CALIB_FIX_K4; 


    double rms =cv::fisheye::calibrate(
objectPoints, points2D, src.size(), 
K, D, rvec, tvec, flag, cv::TermCriteria(3, 20, 1e-6)  
); 

    Mat output; 
    cerr<<"K:"<<K<<endl; 
    cerr<<"D:"<<D<<endl; 
    cv::fisheye::undistortImage(srcSaved, output, K, D); 
    cerr<<"rms: "<<rms<<endl; 
    imshow("output", output); 
    waitKey(0); 

    cerr<<"image .size: "<<srcSaved.size()<<endl; 

} 

如果任何人有一个想法,随意或者分享一些Python代码或者在C++。无论漂浮你的船。

编辑:

正如你可能已经从构成我的方块地毯通知我不使用黑色和白色棋盘的校准,但角落。在一天结束时,我认为目标是获得表示来自失真半径的样本的角坐标。地毯在某种程度上与棋盘相同,唯一的区别 - 我再次想到 - 事实上,您在地毯上的角落处比在黑白棋盘上的高频边缘更少。

我知道图片数量非常有限,也就是只有1.我希望图像在一定程度上不失真,但我也希望不失真能做得很好。但在这种情况下,图像输出看起来像是完全废话。

我结束了使用这个图像与棋盘:https://imgur.com/a/WlLBR 本网站提供:https://sites.google.com/site/scarabotix/ocamcalib-toolbox/ocamcalib-toolbox-download-page 但效果仍然很差:对角线像我张贴的其他输出图像。

谢谢

+0

难道你不能只是用图像打印A4纸吗?这就是它的校准。 – karlphillip

+0

@karlphillip我目前没有物理访问摄像头。也基于我在网上找到的。使用棋盘不是强制性的。 – privetDruzia

+0

是的,我的意思是这是更简单的方法。那么祝你好运吧! – karlphillip

你的第一个问题是,你只使用一个图像。即使你有一个没有失真的理想*,你也无法从共面点的单个图像估计内在特性。一个共面点的图像根本不会给你足够的约束来解决内在问题。

您需要至少两张不同3D方向的图像,或者一个三维校准装置,其中的点不是共面的。当然,在实践中,您至少需要20张图像才能进行精确校准。

你的第二个问题是你使用地毯作为棋盘格。您需要能够以亚像素精度检测图像中的点。小的定位误差会导致估计的摄像机参数产生很大的误差。我非常怀疑你可以用任何合理的准确度来检测你的地毯的方格角落。事实上,你甚至无法准确测量地毯上的实际点位置,因为它很模糊。

祝你好运!

+0

非常感谢您的回答,我会研究一下!你想介绍一下亚像素精度吗? AFAIK地毯或棋盘的角落通过例如哈里斯角落检测器在图像空间中被检测到,Whcih意味着由该函数返回的角落的像素位置是整数值。 – privetDruzia

+0

我最终使用这个图像与棋盘:https://imgur.com/a/WlLBR 我在这个网站上找到:https://sites.google.com/site/scarabotix/ocamcalib-toolbox/ocamcalib - 工具箱下载页 但结果仍然很差:对角线像我张贴的其他输出图像。 – privetDruzia

+0

@privetDruzia,是的,哈里斯算法本身给你在整数坐标的角点。然后,将像素邻域集中在拐角处,并将二维二次函数拟合到该邻域中的哈里斯函数。然后计算抛物面的最小值,并将其用作拐角的精确位置。大多数进行兴趣点检测的功能都会为您做到这一点。 – Dima