OpenCV标定问题

8

我正在开发一个OpenCV项目,目前进行的是标定。我相信我已经正确实现了代码;但是我得到了不同的相机矩阵值,有时差异很大。在重复展示标定图案10次的情况下,经过6次操作,我得到了以下结果(为了清晰起见,小数点后位数被截断):

[573, 0,  386;
  0, 573, 312;
  0,  0,   1]

[642, 0,  404;
  0, 644, 288;
  0,  0,   1]

[664, 0,  395;
  0, 665, 272;
  0,  0,   1]

[629, 0,  403;
  0, 630, 288;
  0,  0,   1]

[484, 0,  377;
  0, 486, 307;
  0,  0,   1]

[644, 0,  393;
  0, 643, 289;
  0,  0,   1]

这些值之间的差异是不可接受的。我需要以相当准确的程度了解给定参数是什么。造成这些大误差的通常原因是什么,我如何评估给定矩阵的正确性?似乎取决于我展示图案的距离和方向的各种变化,但我无法理解这个模式。

代码:

using namespace cv;
using namespace std;

int main(int, char**)
{
    VideoCapture cap(1);
    if(!cap.isOpened())
        return -1;

    cap.set(CV_CAP_PROP_FRAME_WIDTH,800);
    cap.set(CV_CAP_PROP_FRAME_HEIGHT,600);
    Mat edges;
    Size size(9,17);

    int counter = 10;

    vector<Point2f> corners;
    bool found;

    vector<Point3f> chess = fr::ChessGen::getBoard(size,1,true);

    vector<vector<Point3f> > objectPoints;
    vector<vector<Point2f> > imagePoints;

    Mat camera = Mat::eye(3,3,CV_64F);
    Mat distortion = Mat::zeros(8, 1, CV_64F);
    vector<Mat > rvecs;
    vector<Mat > tvecs;

    namedWindow("edges",1);
    for(;;)
    {
        Mat frame;
        cap >> frame;
        cvtColor(frame, edges, CV_BGR2GRAY);

        found = findCirclesGrid(edges,size,corners
                                ,CALIB_CB_ASYMMETRIC_GRID
                                );
        if(found) frame.convertTo(edges,-1,0.2);

        drawChessboardCorners(edges,size,corners,found);

        imshow("edges", edges);
        if(found){
            if(waitKey(200)>=0){
                objectPoints.push_back(chess);
                imagePoints.push_back(corners);
                if(--counter<= 0)
                    break;
            }
        }
        else waitKey(30);
    }

    calibrateCamera(objectPoints,imagePoints,Size(800,600),camera,distortion,rvecs,tvecs,0);

    if(found) imwrite("/home/ryan/snapshot.png",edges);

    cout << camera << endl;

    return 0;
}

在每种情况下,您得到了什么重新投影误差? - Sassa
这里有一个带有文档的校准示例代码:http://docs.opencv.org/doc/tutorials/calib3d/camera_calibration/camera_calibration.html,同时请注意Martin Beckett所说的内容。 - chipmunk
你能发布一些你使用过的图像集吗?只是为了确保我们有正确的图片... - tjltjl
这些图像是从实时摄像头流中获取的。在每个帧中,程序会搜索棋盘。如果找到了,计数器会减少并将图像添加到校准图像列表中。如果计数器达到零,则进行校准。因此,这是一个缺点。如果我移动棋盘的速度足够慢,以至于它在位置之间不会模糊,那么所有10个位置可能非常接近。 - Ryan Kennedy
这行代码是做什么的?vector<Point3f> chess = fr::ChessGen::getBoard(size,1,true); 另外,fr 这个命名空间是什么?我在任何地方都找不到它。 - Pedro Batista
这是一个实用函数,用于生成棋盘坐标向量:fr::ChessGen::getBoard(2, 1, true) => [(0,0) (0,1) (0,2) (1,0) (1,1) (1,2) (2,0) (2,1) (2,2)]。我的错;我以为在发布之前已经完全泛化了这段代码。 - Ryan Kennedy
4个回答

5
根据相机/镜头和所需的准确性而定,但您可能需要超过10个位置,并且需要覆盖更广泛的视角范围。 我假设根据800x600,这是一个带有大量扭曲的简单广角镜头的网络摄像头。我建议您在每个3-4个不同的角度中对目标进行6-8个位置/旋转。您还需要确保目标和相机固定,并且在图像期间不会移动。再次假设相机具有简单的自动增益,因此您应确保目标非常光亮,以便使用快门速度快且增益低。 openCV使用的技术之一存在问题,即需要看到目标上所有角落/点以识别并在解决方案中使用帧 - 因此很难获得靠近图像角落的点。您应该检查用于校准的实际图像数量的数据 - 可能仅在10个图像中找到所有点并基于该子集构建解决方案。

3
重要的是不仅要采用垂直于相机的图案,还要进行旋转。为了提高结果质量,您还可以密切检查检测到的角落位置,删除其中某些角落未正确检测的图片,并再次运行算法。
我不知道您使用的是哪种相机,但若相机存在严重畸变或不够清晰,则角落可能难以正确检测。在这种情况下,可以使用圆形图案进行OpenCV校准,可获得更好的结果。

2
你写道:“仔细检查检测到的角点的位置,删除那些未正确检测到某些角点的图片,并重新运行算法”。这是必须的,OpenCV的算法最小化了平方重投影误差的总和,对异常值不具有鲁棒性,因此图像中错误检测的点会导致结果出现误差(尤其是在图像数量较少的情况下)。 - Milo

1

根据我的经验,您应该使用OpenCV提供的undistort()函数,使用无畸变的图像进行校准。

这意味着您需要运行两次校准,第一次确定镜头系数。然后在第二次运行中对每个棋盘格帧进行去畸变处理。使用无畸变的校准帧可以使焦距fx和fy更加精确。


0

根据Zhengyou Zhang的论文,适当的角度应该低于45度,并且必须有超过6张图片,最好拍摄20张。此外,您需要注意光线平衡和强度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接