c++学习【3】棋盘格标定

在C++中,vector是一个容器类,用于存储动态数组。

vector表示一个Point2f类型的动态数组,可以理解为一个一维的数组。

而vector>表示一个二维的动态数组,其中每个元素都是一个Point2f类型的动态数组。

为什么要这样嵌套写呢?这是因为在计算机视觉中,我们经常需要处理的是二维的数据,例如图像中的像素、特征点、轮廓点等。因此,我们需要使用一个二维的动态数组来存储这些数据。如果直接使用一个一维的动态数组,可能会比较难以处理这些二维数据。因此,使用vector>这种嵌套写法,可以方便地存储和访问二维数据,同时也具有很大的灵活性和可扩展性。

Point3f是OpenCV库中定义的一个结构体类型,表示一个三维坐标点。在C++中,6.0f表示一个float类型的浮点数,其值为6.0。在C++中,float类型的浮点数通常使用字母"f"来表示,以便于区分double类型的浮点数。如果不加"f",则默认为double类型的浮点数。

#include 
#include 

using namespace cv;
using namespace std;

int main()
{
    // 定义和初始化一个Point3f类型的变量
    Point3f pt1(1.0f, 2.0f, 3.0f);
    // 或者使用默认构造函数来初始化
    Point3f pt2;

    // 访问Point3f变量的成员变量x、y、z
    float x = pt1.x;
    float y = pt1.y;
    float z = pt1.z;

    // 修改Point3f变量的成员变量
    pt2.x = 4.0f;
    pt2.y = 5.0f;
    pt2.z = 6.0f;
    
    cout << pt1 << endl;

    return 0;
}

遍历棋盘格角点坐标:OpenCV 库中使用的是标准库中的容器类 std::vectorcv::Point3f 是 OpenCV 库中定义的一个结构体类型,表示一个三维坐标点,包含三个 float 类型的成员变量,分别表示 x、y、z 三个坐标轴上的坐标值。在使用 std::vector 容器时,可以将 cv::Point3f 类型的对象作为元素添加到容器中,实现对一组三维坐标点的存储和管理。例如,可以定义一个 std::vector 类型的变量,用于存储多个三维坐标点。

int main() {
    // 定义棋盘格大小和个数
    Size boardSize(8, 8);
    int boardNum = 10;

    // 定义棋盘格角点坐标
    vector> objPoints;
    /*定义了一个二维向量的变量objPoints,其中每个元素都是一个三维点(Point3f)的向量(vector)。*/
    /*其中objPoints中的每个元素都是一个由相机观察到的三维物体的点云,可以用于重建三维模型或者计算相机的内外参数等。*/

    // 生成棋盘格角点坐标
    vector obj;
    for (int i = 0; i < boardSize.height; i++)
    {
        for (int j = 0; j < boardSize.width; j++)
        {
            obj.push_back(Point3f(j, i, 0));
        }
    }
    for (int i = 0; i < boardNum; i++)
    {
        objPoints.push_back(obj);
    }
    cout << objPoints.size() << endl;
    cout << objPoints[0].size() << endl;
    // 遍历并输出二维vector容器中的元素
    for (int i = 0; i < objPoints.size(); i++)
    {
        for (int j = 0; j < objPoints[i].size(); j++)
        {
            cout << objPoints[i][j] << " ";
        }
        cout << endl;
    }
}

检测棋盘格角点

int main() {
    // 定义棋盘格大小和个数
    Size boardSize(8, 8);

    Mat image = imread("0.jpg");
    resize(image, image, Size(), 0.5, 0.5);

    // 转换为灰度图
    Mat gray;
    cvtColor(image, gray, COLOR_BGR2GRAY);
    // 检测角点
    vector corners;
    bool found = findChessboardCorners(gray, boardSize, corners);

    // 绘制角点
    Mat imgCorners;
    cvtColor(gray, imgCorners, COLOR_GRAY2BGR);
    drawChessboardCorners(imgCorners, boardSize, corners, found);

    // 显示图像
    imshow("Corners", imgCorners);
    waitKey(0);
}

 检测普通角点

int main() {
    cv::Mat image = cv::imread("1.jpg");
    resize(image, image, Size(), 0.5, 0.5);
    cv::Mat grayImage;
    cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY);

    std::vector corners;
    cv::goodFeaturesToTrack(grayImage, corners, 100, 0.01, 10);
    /*
    void cv::goodFeaturesToTrack(
    InputArray image, // 输入图像,可以是8位或32位浮点型灰度图像
    OutputArray corners, // 输出的角点坐标
    int maxCorners, // 最多检测的角点数目
    double qualityLevel, // 质量水平阈值,取值范围为[0,1],越大表示检测到的角点越准确
    double minDistance, // 角点间最小距离,小于该距离的角点会被忽略
    InputArray mask = noArray(), // 可选的掩膜图像,检测角点的区域
    int blockSize = 3, // 计算角点响应函数时使用的窗口大小
    bool useHarrisDetector = false, // 是否使用Harris角点检测算法,如果为false则使用Shi-Tomasi角点检测算法
    double k = 0.04 // Harris角点检测算法的k值,取值范围为[0,04,0.06]
    */

    for (size_t i = 0; i < corners.size(); i++)
    {
        cv::circle(image, corners[i], 3, cv::Scalar(0, 0, 255), -1);
    }

    cv::imshow("Corners", image);
    cv::waitKey();

    return 0;
}

c++学习【3】棋盘格标定_第1张图片 c++学习【3】棋盘格标定_第2张图片

push_back() 是向容器尾部添加元素的成员函数。

// 存储角点
imgPoints.push_back(corners);

cornerSubPix() 函数在 C++ 中的返回值类型是 void,也就是说,该函数没有返回值。在函数调用后,可以直接使用修改后的 corners 向量中的元素来获取亚像素级别的角点坐标。

 Mat是OpenCV中的一个类,用于表示矩阵,可以不指定尺寸,此时会创建一个空的矩阵。

相机内参

// 标定相机
Mat cameraMatrix, distCoeffs;
std::cout << "rows: " << cameraMatrix.rows << std::endl;
std::cout << "cols: " << cameraMatrix.cols << std::endl;
std::cout << "rows: " << distCoeffs.rows << std::endl;
std::cout << "cols: " << distCoeffs.cols << std::endl;

  输出:   rows: 3
                cols: 3
                rows: 1
                cols: 5

其中相机内参

  1. fx:x方向的焦距;
  2. fy:y方向的焦距;
  3. cx:x方向的图像中心点;
  4. cy:y方向的图像中心点;
  5. s:相机的倾斜因子;
  6. k1:径向畸变系数;
  7. k2:径向畸变系数;
  8. p1:切向畸变系数;
  9. p2:切向畸变系数。

其中畸变参数

  1. k1:径向畸变系数;
  2. k2:径向畸变系数;
  3. p1:切向畸变系数;
  4. p2:切向畸变系数;
  5. k3:径向畸变系数。

解析旋转和平移

在C++中,vector rvecsvector tvecs是相机标定过程中的输出参数,用于表示每幅图像的旋转向量和平移向量。其中

rvecs[0]:
[0.1765137348598873;
 -0.004509234502410684;
 -0.116059886448816]
tvecs[0]:
[-5.096144519206531;
 -2.976894855117076;
 22.2569114926337]
rvecs[1]:
[-0.2814441570484566;
 0.09422450690486907;
 -1.516149181978618]
tvecs[1]:
[-2.640683097822767;
 4.033523428337181;
 25.01756476495153]

......

通常使用罗德里格斯公式将旋转向量转换为旋转矩阵。罗德里格斯公式可以将一个3维向量转化为一个3x3的旋转矩阵。在OpenCV中,可以使用cv::Rodrigues函数将旋转向量转换为旋转矩阵。

    Mat R;

    for (int i = 0; i < 10; i++)
    {
        Rodrigues(rvecs[i], R);
        cout << "rvecs[" << i << "]:\n" << R << endl;// 查看 rvecs 中第 i 幅图像的旋转向量
        cout << "tvecs[" << i << "]:\n" << tvecs[i] << endl;// 查看 tvecs 中第 i 幅图像的平移向量
    }

rvecs[0]:
[0.9932799434099183, 0.1148016925916202, -0.01468078327912038;
 -0.1155946774290525, 0.9777693984665894, -0.174942487613379;
 -0.005729273048360591, 0.1754638846041632, 0.9844691974002814]
tvecs[0]:
[-5.096144519206531;
 -2.976894855117076;
 22.2569114926337] 

完整代码

#include 
#include 

using namespace cv;
using namespace std;

int main()
{
    // 定义棋盘格大小和个数
    Size boardSize(8, 8);
    int boardNum = 10;

    // 定义棋盘格角点坐标
    vector> objPoints;
    /*定义了一个二维向量的变量objPoints,其中每个元素都是一个三维点(Point3f)的向量(vector)。*/
    /*其中objPoints中的每个元素都是一个由相机观察到的三维物体的点云,可以用于重建三维模型或者计算相机的内外参数等。*/
    vector> imgPoints;
    /*定义了一个二维向量的变量,每个元素都是一个二维点(Point2f)的向量(vector)*/
    /*用来存储棋盘格图案的角点坐标,用于计算相机的内外参数。*/

    // 生成棋盘格角点坐标
    vector obj;
    for (int i = 0; i < boardSize.height; i++)
    {
        for (int j = 0; j < boardSize.width; j++)
        {
            obj.push_back(Point3f(j, i, 0));
        }
    }
    for (int i = 0; i < boardNum; i++)
    {
        objPoints.push_back(obj);
    }

    // 读取多个图片并提取角点
    for (int i = 1; i <= boardNum; i++)
    {
        // 读取图片
        string filename = "images/" + to_string(i) + ".jpg";
        Mat image = imread(filename);
        resize(image, image, Size(), 0.5, 0.5);

        // 转换为灰度图
        Mat gray;
        cvtColor(image, gray, COLOR_BGR2GRAY);

        // 提取角点
        vector corners;
        bool found = findChessboardCorners(gray, boardSize, corners);

        if (found)
        {
            // 亚像素精细化
            cornerSubPix(gray, corners, Size(11, 11), Size(-1, -1), TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 30, 0.1));

            // 存储角点
            imgPoints.push_back(corners);

            // 可视化角点
            drawChessboardCorners(image, boardSize, corners, found);
            imshow("corners", image);
            waitKey(0);
        }
    }

    // 标定相机
    Mat cameraMatrix, distCoeffs;
    vector rvecs, tvecs;
    calibrateCamera(objPoints, imgPoints, Size(640, 480), cameraMatrix, distCoeffs, rvecs, tvecs);
    /*
    calibrateCamera函数的参数包括:
    objectPoints:已知的3D空间点的坐标,类型为vector>,其中Point3f是一个三维点的数据结构。
    imagePoints:对应的2D图像点的坐标,类型为vector>,其中Point2f是一个二维点的数据结构。
    imageSize:图像的大小,类型为Size。
    cameraMatrix:相机的内部参数矩阵,类型为Mat。
    distCoeffs:相机的畸变系数,类型为Mat。
    rvecs:每个图像的旋转矢量,类型为vector。
    tvecs:每个图像的平移矢量,类型为vector。
    */

    // 打印标定结果
    //cout << "camera matrix:\n" << cameraMatrix << endl;
    //cout << "distortion coefficients:\n" << distCoeffs << endl;
    
    Mat R;

    for (int i = 0; i < 10; i++)
    {
        Rodrigues(rvecs[i], R);
        cout << "rvecs[" << i << "]:\n" << R << endl;// 查看 rvecs 中第 i 幅图像的旋转向量
        cout << "tvecs[" << i << "]:\n" << tvecs[i] << endl;// 查看 tvecs 中第 i 幅图像的平移向量
    }
    

    return 0;
}

你可能感兴趣的:(c++学习,c++,学习,开发语言)