OpenCV标定演示,及如何生成标定板图片

标定的程序在官方的源码里有,

opencv-4.5.5\samples\cpp\tutorial_code\calib3d\camera_calibration

很多小白不知道怎么跑起来,这个也怪OpenCV官方,工作没做完善,其实的default.xml是要自己手动改的,输入的图片也要自己去拍摄,还有那个VID5.xml也要改成可以直接找到图片的路径;

我这里拍了5张图,故意做了鱼眼效果后,用于标定校正。程序已经改好了,直接visual studio就可以跑了,到这里去下载吧,

https://github.com/SpaceView/OpenCV455_cameraCalibrationDemo

关于标定板:

OpenCV官方曾经提供的标定板是CHESSBOARD 9x6和7x7的,实际应用根据场景的需要,可能需要不同的标定板。

标定程序实际支持三种标定板,包括chessboard,grid circle, asymmetric grid circle格子,这里我们手动生成标定板(包括这里提到的三种标定板)的图片, 程序如下,

class CalibTools {

public:

    cv::Mat GenerateChessboard(int xBlockNum, int yBlockNum, int BLOCKWIDTH, std::string saveFileName) {
        //const int BLOCKWIDTH = 150;
        //const int xBlockNum = 7;
        //const int YBlockNum = 7;
        cv::Size sz = { xBlockNum * BLOCKWIDTH, yBlockNum * BLOCKWIDTH };
        cv::Mat  mat(sz, CV_8UC3);
        for (int r = 0; r < yBlockNum; r++) {
            for (int c = 0; c < xBlockNum; c++) {
                int IX = c * BLOCKWIDTH, IY = r * BLOCKWIDTH;
                int EX = IX + BLOCKWIDTH, EY = IY + BLOCKWIDTH;
                cv::Vec3b value;
                if (0 == (c + r) % 2) {
                    value = { 0xFF,0xFF, 0xFF };
                }
                else {
                    value = 0x000000;
                }
                for (int y = IY; y < EY; y++) {
                    cv::Vec3b* p = mat.ptr(y);
                    for (int x = IX; x < EX; x++) {
                        p[x] = value;
                    }
                }
            }
        }
        if (!saveFileName.empty()) {
            saveFileName += ".png";
            cv::imwrite(saveFileName, mat);
        }

        return mat;
    }

    /*
    *     *   *   *   *   *   *   *   --> xDotNum1  = 7   _|_ 1
    *     *   *   *   *   *   *   *                       _|_ 2
    *     *   *   *   *   *   *   *                       _|_ 3
    *     *   *   *   *   *   *   *                       _|_ 4
    *     *   *   *   *   *   *   *                       _|_ 5
    *     *   *   *   *   *   *   *                       _|_ 6
    *     *   *   *   *   *   *   *                       _|_ 7 --> In total yDotNum = 7
    */
    cv::Mat GenerateGridCircle(int xDotNum, int yDotNum, int dotRadius, int BLOCKWIDTH, std::string saveFileName) {
        cv::Size sz = { (xDotNum +1) * BLOCKWIDTH, (yDotNum+1) * BLOCKWIDTH };
        cv::Mat  mat(sz, CV_8UC3);
        mat.setTo(cv::Scalar(255,255,255));
        for (int r = 1; r <= yDotNum; r++) {
            int y = r * BLOCKWIDTH;
            for (int c = 1; c <= xDotNum; c++) {
                int x = c * BLOCKWIDTH;
                cv::circle(mat, cv::Point(x, y), dotRadius, cv::Scalar(0, 0, 0), cv::FILLED);
            }
        }
        if (!saveFileName.empty()) {
            saveFileName += ".png";
            cv::imwrite(saveFileName, mat);
        }

        return mat;
    }


    /*
    *     *   *   *   *   *   *   *   --> xDotNum1  = 7    |
    *       *   *   *   *   *   *     --> xDotNum2  = 6   _|_  1, --> 2 rows make 1 unit in  yDotNum
    *     *   *   *   *   *   *   *                        |
    *       *   *   *   *   *   *                         _|_  2
    *     *   *   *   *   *   *   *                        |
    *       *   *   *   *   *   *                         _|_  3
    *     *   *   *   *   *   *   *                        |
    *       *   *   *   *   *   *                         _|_  4, --> In total yDotNum = 4
    * 
    *     *<--->*    ---> DOTDIST
    *        *
    *     *     *
    */
    cv::Mat GenerateAsymmetricGridCircle(int xDotNum1, int xDotNum2, int yDotNum, int dotRadius, int DOTDIST, std::string saveFileName) {
        int totXDotNum = xDotNum1 + xDotNum2 + 2;
        int totYDotNum = 2 * yDotNum + 2;
        int DIST = DOTDIST / 2;
        int HDIST = DIST / 2;

        cv::Size sz = { totXDotNum * DIST, totYDotNum * DIST };
        cv::Mat  mat(sz, CV_8UC3);
        mat.setTo(cv::Scalar(255, 255, 255));
        for (int r = 1; r < totYDotNum-1; r++) {
            for (int c = 1; c < totXDotNum-1; c++) {
                int IX = c * DIST, IY = r * DIST;
                int CX = IX + HDIST, CY = IY + HDIST;
                if (0 == (c + r) % 2) {
                    cv::circle(mat, cv::Point(CX, CY), dotRadius, cv::Scalar(0, 0, 0), cv::FILLED);
                }
            }
        }

        if (!saveFileName.empty()) {
            saveFileName += ".png";
            cv::imwrite(saveFileName, mat);
        }

        return mat;
    }
};

调用方式举例如下,

CalibTools ct;
ct.GenerateChessboard(7, 7, 100, "GenerateChessboard");
ct.GenerateGridCircle(8, 8, 20, 80, "GeneratedGridCircles");
ct.GenerateAsymmetricGridCircle(8, 7, 6, 20, 100, "GenerateAsymmetricGridCircle");

这样,就可以生成下列形状的标定板,

OpenCV标定演示,及如何生成标定板图片_第1张图片

OpenCV标定演示,及如何生成标定板图片_第2张图片

OpenCV标定演示,及如何生成标定板图片_第3张图片

然后打印出来就可以做标定板了。

关于非对称的圆形标定板,可以参考下面的贴子,

OpenCv相机标定——圆形标定板标定_opencv圆形标定板-CSDN博客

本文结束。

你可能感兴趣的:(opencv,人工智能,计算机视觉)