OpenCV的Sample分析:相机标定(2)

OpenCV的Sample分析:相机标定(2)

我们的分析是基于相机标定(1)的。在篇(1)中,了解了类Settings的作用,以及XML文件读入读出的方法,那么就往前再走一步

先看两个类外的小函数

static inline void read(const FileNode& node, Settings& x, const Settings& default_value = Settings())
{
    if(node.empty())
        x = default_value;
    else
        x.read(node);
}

static inline void write(FileStorage& fs, const String&, const Settings& s )
{
    s.write(fs);
} 

再看read()这个函数的时候,可以发现一个叫node的FileNode型变量。我对FileNode型变量不算清除,但是通过对FileNode型变量的初始化,可以看出这个类FileNode的具体意义

        node["BoardSize_Width" ] >> boardSize.width;
        node["BoardSize_Height"] >> boardSize.height;
        node["Calibrate_Pattern"] >> patternToUse;
        node["Square_Size"]  >> squareSize;

用一个不恰当的比喻:整个XML文件可以当作一棵树,那么XML文件的不同段落就是这棵树的节点(Node)

那么现在就开始分析主函数吧:

    Settings s;
    const string inputSettingsFile = argc > 1 ? argv[1] : "/Desktop/task/test_openCV/Project/in_VID5.xml";
    FileStorage fs(inputSettingsFile, FileStorage::READ); // Read the settings
    if (!fs.isOpened())
    {
        cout << "Could not open the configuration file: \"" << inputSettingsFile << "\"" << endl;
        return -1;
    }
    fs["Settings"] >> s;
    fs.release();                                         // close Settings file
    if (!s.goodInput)
    {
        cout << "Invalid input detected. Application stopping. " << endl;
        return -1;
    }

string变量inputSettingsFile储存XML文件in_VID5的位置(此处设置的是我的电脑中该文件的位置),这样的文件读取方法是只得借鉴的,今后可以用在自己的函数中。FileStorage变量fs读取XML文件in_VID5中的内容。

那么,问题来了,下述的这条语句是什么意思呢?

fs["Settings"] >> s;

如果为了理解这条语句而去了解Filestorage类的库函数的话,就有些麻烦。但是从这条语句中,可以猜到这条语句的执行过程:

1,Filestorage类变量fs准备复制给Settings类变量s

2,启用Settings类变量的read()函数进行拷贝工作

3,拷贝结束后,用Settings类的成员函数validate()判别文件内容信息是否有效(在上一篇讨论过)如果内容有效,那么bool型变量s.goodInput是True.

然后定义了一些变量。注意imagePoints的类型是vector<vector<Point2f>>指不同图片下像素点的像素坐标。

    vector<vector<Point2f> > imagePoints;
    Mat cameraMatrix, distCoeffs;
    Size imageSize;
    int mode = s.inputType == Settings::IMAGE_LIST ? CAPTURING : DETECTION;
    clock_t prevTimestamp = 0;
    const Scalar RED(0,0,255), GREEN(0,255,0);
    const char ESC_KEY = 27;

那么mode这个变量含义是什么呢?别急,首先inputType是这样定义的,

enum InputType { INVALID, CAMERA, VIDEO_FILE, IMAGE_LIST }
enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 }

可是mode这条语句后半段指什么呢?注意这条语句的优先级,首先判s.inputType == Settings::IMAGE_LIST,然后决定CAPTURING或者DETECTION赋值给mode

int mode = (s.inputType == Settings::IMAGE_LIST ? CAPTURING : DETECTION);

然而inputType又是怎样定义的,在下面这一段代码中找到答案(位于类Setting的成员函数validate()中)。在这段代码中可以发现,inputType的赋值跟String类型input这个变量密切相关。inputTYpe分为四类,即INVALID, CAMERA, VIDEO_FILE, IMAGE_LIST。input的值取决于设定文件(in_VID5.xml)的设定,在我们这里input存放的是图片的地址,于是inputType指IMAGE_LIST,变量nrFrames指图像的帧数。

        if (input.empty())      // Check for valid input
                inputType = INVALID;
        else
        {
            if (input[0] >= '0' && input[0] <= '9')
            {
                stringstream ss(input);
                ss >> cameraID;
                inputType = CAMERA;
            }
            else
            {
                if (readStringList(input, imageList))
                {
                    inputType = IMAGE_LIST;
                    nrFrames = (nrFrames < (int)imageList.size()) ? nrFrames : (int)imageList.size();
                }
                else
                    inputType = VIDEO_FILE;
            }
            if (inputType == CAMERA)
                inputCapture.open(cameraID);
            if (inputType == VIDEO_FILE)
                inputCapture.open(input);
            if (inputType != IMAGE_LIST && !inputCapture.isOpened())
                    inputType = INVALID;
        }

因此,mode等于CAPUTRING,或者说mode等于1(根据枚举数组的定义。。)。

然后有,有一个大的for循环,for(; ;),现在分析这个循环的第一个部分:
        Mat view;
        bool blinkOutput = false;

        view = s.nextImage();

        //-----  If no more image, or got enough, then stop calibration and show result -------------
        if( mode == CAPTURING && imagePoints.size() >= (size_t)s.nrFrames )
        {
          if( runCalibrationAndSave(s, imageSize,  cameraMatrix, distCoeffs, imagePoints))
              mode = CALIBRATED;
          else
              mode = DETECTION;
        }
        if(view.empty())          // If there are no more images stop the loop
        {
            // if calibration threshold was not reached yet, calibrate now
            if( mode != CALIBRATED && !imagePoints.empty() )
                runCalibrationAndSave(s, imageSize,  cameraMatrix, distCoeffs, imagePoints);
            break;
        }

view是一个Mat型变量,在第一次循环中保存第一幅图像(这幅图像可以来源自一个图片集,或者是一段视频)。在第一次循环过程中,尽管mode == CAPTURING,由于多维数组变量imagePoints刚刚才初始化,不能满足条件imagePoints.size() >= (size_t)s.nrFrames,所以不执行第一个if语句。另外,在第一次循环中,view非空,程序也不执行第二个if语句。注释信息说明第二个if就是循环结束的标志。

好,接着向下看,这个循环的第二部分,

        imageSize = view.size();  // Format input image.
        if( s.flipVertical )    flip( view, view, 0 );

        //! [find_pattern]
        vector<Point2f> pointBuf;

        bool found;

        int chessBoardFlags = CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE;

        if(!s.useFisheye) {
            // fast check erroneously fails with high distortions like fisheye
            chessBoardFlags |= CALIB_CB_FAST_CHECK;
        }

        switch( s.calibrationPattern ) // Find feature points on the input format
        {
        case Settings::CHESSBOARD:
            found = findChessboardCorners( view, s.boardSize, pointBuf, chessBoardFlags);
            break;
        case Settings::CIRCLES_GRID:
            found = findCirclesGrid( view, s.boardSize, pointBuf );
            break;
        case Settings::ASYMMETRIC_CIRCLES_GRID:
            found = findCirclesGrid( view, s.boardSize, pointBuf, CALIB_CB_ASYMMETRIC_GRID );
            break;
        default:
            found = false;
            break;
        }

变量imageSize的意义就不解释了。如果图像是横着的,就用flip函数把图像变成竖着的。vector变量pointBuf记录这一帧图像的像素点。此次相机标定采用棋盘格法,不执行鱼眼这个判断。

int chessBoardFlags = CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE;

这条语句指明图像应该先使用直方图均衡化做预处理,然后用自适应阈值分割找到标定板。针对不同的标定板,程序首先会寻找它们,如果找到了bool型变量found置为TRUE,否则就是FALSE。

找到了,才能标定嘛。至于寻找标定板的过程,这里暂且不分析,哈哈。





猜你喜欢

转载自blog.csdn.net/qq_39732684/article/details/80336391