《视觉SLAM十四讲从理论到实践 第二版》实践部分——ch5

文章目录

  • 课内实践
    • imageBasics
    • undistortImage
    • stereoVision
    • joinMap
  • 课后实践
    • 实验一
    • 实验二

课内实践

imageBasics

  • CMakeLists.txt
# 声明要求的 cmake 最低版本
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

# 声明一个 cmake 工程
PROJECT(imageBasics)

# 设置编译模式
SET(CMAKE_BUILD_TYPE "Release")

# 设置编译选项
SET(CMAKE_CXX_FLAGS "-std=c++11 -O2")

# 添加Eigen头文件
INCLUDE_DIRECTORIES("/usr/include/eigen3")

# 寻找OpenCV库
FIND_PACKAGE(OpenCV REQUIRED)

# 添加头文件
INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS})

# 添加一个可执行程序
# 语法:ADD_EXECUTABLE( 程序名 源代码文件 )
ADD_EXECUTABLE(imageBasics imageBasics.cpp)

# 链接OpenCV库
TARGET_LINK_LIBRARIES(imageBasics ${OpenCV_LIBS})

  • imageBasics.cpp
#include 
#include 

using namespace std;

#include 
#include 

int main(int argc, char **argv) {
  // 读取argv[1]指定的图像
  cv::Mat image;
  image = cv::imread(argv[1]); //cv::imread函数读取指定路径下的图像

  // 判断图像文件是否正确读取
  if (image.data == nullptr) { //数据不存在,可能是文件不存在
    cerr << "文件" << argv[1] << "不存在." << endl;
    return 0;
  }

  // 文件顺利读取, 首先输出一些基本信息
  cout << "图像宽为" << image.cols << ",高为" << image.rows << ",通道数为" << image.channels() << endl;
  cv::imshow("image", image);      // 用cv::imshow显示图像
  cv::waitKey(0);                  // 暂停程序,等待一个按键输入

  // 判断image的类型
  if (image.type() != CV_8UC1 && image.type() != CV_8UC3) {
    // 图像类型不符合要求
    cout << "请输入一张彩色图或灰度图." << endl;
    return 0;
  }

  // 遍历图像, 请注意以下遍历方式亦可使用于随机像素访问
  // 使用 std::chrono 来给算法计时
  chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
  for (size_t y = 0; y < image.rows; y++) {
    // 用cv::Mat::ptr获得图像的行指针
    unsigned char *row_ptr = image.ptr<unsigned char>(y);  // row_ptr是第y行的头指针
    for (size_t x = 0; x < image.cols; x++) {
      // 访问位于 x,y 处的像素
      unsigned char *data_ptr = &row_ptr[x * image.channels()]; // data_ptr 指向待访问的像素数据
      // 输出该像素的每个通道,如果是灰度图就只有一个通道
      for (int c = 0; c != image.channels(); c++) {
        unsigned char data = data_ptr[c]; // data为I(x,y)第c个通道的值
      }
    }
  }
  chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
  chrono::duration<double> time_used = chrono::duration_cast < chrono::duration < double >> (t2 - t1);
  cout << "遍历图像用时:" << time_used.count() << " 秒。" << endl;

  // 关于 cv::Mat 的拷贝
  // 直接赋值并不会拷贝数据
  cv::Mat image_another = image;
  // 修改 image_another 会导致 image 发生变化
  image_another(cv::Rect(0, 0, 100, 100)).setTo(0); // 将左上角100*100的块置零
  cv::imshow("image", image);
  cv::waitKey(0);

  // 使用clone函数来拷贝数据
  cv::Mat image_clone = image.clone();
  image_clone(cv::Rect(0, 0, 100, 100)).setTo(255);
  cv::imshow("image", image);
  cv::imshow("image_clone", image_clone);
  cv::waitKey(0);

  // 对于图像还有很多基本的操作,如剪切,旋转,缩放等,限于篇幅就不一一介绍了,请参看OpenCV官方文档查询每个函数的调用方法.
  cv::destroyAllWindows();
  return 0;
}
  • 结果

《视觉SLAM十四讲从理论到实践 第二版》实践部分——ch5_第1张图片
《视觉SLAM十四讲从理论到实践 第二版》实践部分——ch5_第2张图片
《视觉SLAM十四讲从理论到实践 第二版》实践部分——ch5_第3张图片

undistortImage

  • CMakeLists.txt
# 声明要求的 cmake 最低版本
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

# 声明一个 cmake 工程
PROJECT(undistortImage)

# 设置编译模式
SET(CMAKE_BUILD_TYPE "Release")

# 设置编译选项
SET(CMAKE_CXX_FLAGS "-std=c++11 -O2")

# 添加Eigen头文件
INCLUDE_DIRECTORIES("/usr/include/eigen3")

# 寻找OpenCV库
FIND_PACKAGE(OpenCV REQUIRED)

# 添加头文件
INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS})

# 添加一个可执行程序
# 语法:ADD_EXECUTABLE( 程序名 源代码文件 )
ADD_EXECUTABLE(undistortImage undistortImage.cpp)

# 链接OpenCV库
TARGET_LINK_LIBRARIES(undistortImage ${OpenCV_LIBS})

  • undistortImage.cpp
#include 
#include 

using namespace std;

string image_file = "../distorted.png";   // 请确保路径正确

int main(int argc, char **argv) {

  // 本程序实现去畸变部分的代码。尽管我们可以调用OpenCV的去畸变,但自己实现一遍有助于理解。
  // 畸变参数
  double k1 = -0.28340811, k2 = 0.07395907, p1 = 0.00019359, p2 = 1.76187114e-05;
  // 内参
  double fx = 458.654, fy = 457.296, cx = 367.215, cy = 248.375;

  cv::Mat image = cv::imread(image_file, 0);   // 图像是灰度图,CV_8UC1
  int rows = image.rows, cols = image.cols;
  cv::Mat image_undistort = cv::Mat(rows, cols, CV_8UC1);   // 去畸变以后的图

  // 计算去畸变后图像的内容
  for (int v = 0; v < rows; v++) {
    for (int u = 0; u < cols; u++) {
      // 按照公式,计算点(u,v)对应到畸变图像中的坐标(u_distorted, v_distorted)
      double x = (u - cx) / fx, y = (v - cy) / fy;
      double r = sqrt(x * x + y * y);
      double x_distorted = x * (1 + k1 * r * r + k2 * r * r * r * r) + 2 * p1 * x * y + p2 * (r * r + 2 * x * x);
      double y_distorted = y * (1 + k1 * r * r + k2 * r * r * r * r) + p1 * (r * r + 2 * y * y) + 2 * p2 * x * y;
      double u_distorted = fx * x_distorted + cx;
      double v_distorted = fy * y_distorted + cy;

      // 赋值 (最近邻插值)
      if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < cols && v_distorted < rows) {
        image_undistort.at<uchar>(v, u) = image.at<uchar>((int) v_distorted, (int) u_distorted);
      } else {
        image_undistort.at<uchar>(v, u) = 0;
      }
    }
  }

  // 画图去畸变后图像
  cv::imshow("distorted", image);
  cv::imshow("undistorted", image_undistort);
  cv::waitKey();
  return 0;
}
  • 结果

《视觉SLAM十四讲从理论到实践 第二版》实践部分——ch5_第4张图片
《视觉SLAM十四讲从理论到实践 第二版》实践部分——ch5_第5张图片

stereoVision

  • CMakeLists.txt
# 声明要求的 cmake 最低版本
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

# 声明一个 cmake 工程
PROJECT(stereoVision)

# 设置编译模式
SET(CMAKE_BUILD_TYPE "Release")

# 设置编译选项
SET(CMAKE_CXX_FLAGS "-std=c++11 -O2")

# 添加Eigen头文件
INCLUDE_DIRECTORIES("/usr/include/eigen3")

# 寻找OpenCV库
FIND_PACKAGE(OpenCV REQUIRED)

# 寻找Pangolin
FIND_PACKAGE(Pangolin REQUIRED)

# 添加头文件
INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS})
INCLUDE_DIRECTORIES(${Pangolin_INCLUDE_DIRS})

# 添加一个可执行程序
# 语法:ADD_EXECUTABLE( 程序名 源代码文件 )
ADD_EXECUTABLE(stereoVision stereoVision.cpp)

# 链接OpenCV库
TARGET_LINK_LIBRARIES(stereoVision ${OpenCV_LIBS} ${Pangolin_LIBRARIES})
  • stereoVision.cpp
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
using namespace Eigen;

// 文件路径
string left_file = "../left.png";
string right_file = "../right.png";

// 在pangolin中画图,已写好,无需调整
void showPointCloud(
    const vector<Vector4d, Eigen::aligned_allocator<Vector4d>> &pointcloud);

int main(int argc, char **argv) {

    // 内参
    double fx = 718.856, fy = 718.856, cx = 607.1928, cy = 185.2157;
    // 基线
    double b = 0.573;

    // 读取图像
    cv::Mat left = cv::imread(left_file, 0);
    cv::Mat right = cv::imread(right_file, 0);
    cv::Ptr<cv::StereoSGBM> sgbm = cv::StereoSGBM::create(
        0, 96, 9, 8 * 9 * 9, 32 * 9 * 9, 1, 63, 10, 100, 32);    // 神奇的参数
    cv::Mat disparity_sgbm, disparity;
    sgbm->compute(left, right, disparity_sgbm);
    disparity_sgbm.convertTo(disparity, CV_32F, 1.0 / 16.0f);

    // 生成点云
    vector<Vector4d, Eigen::aligned_allocator<Vector4d>> pointcloud;

    // 如果你的机器慢,请把后面的v++和u++改成v+=2, u+=2
    for (int v = 0; v < left.rows; v++)
        for (int u = 0; u < left.cols; u++) {
            if (disparity.at<float>(v, u) <= 0.0 || disparity.at<float>(v, u) >= 96.0) continue;

            Vector4d point(0, 0, 0, left.at<uchar>(v, u) / 255.0); // 前三维为xyz,第四维为颜色

            // 根据双目模型计算 point 的位置
            double x = (u - cx) / fx;
            double y = (v - cy) / fy;
            double depth = fx * b / (disparity.at<float>(v, u));
            point[0] = x * depth;
            point[1] = y * depth;
            point[2] = depth;

            pointcloud.push_back(point);
        }

    cv::imshow("disparity", disparity / 96.0);
    cv::waitKey(0);
    // 画出点云
    showPointCloud(pointcloud);
    return 0;
}

void showPointCloud(const vector<Vector4d, Eigen::aligned_allocator<Vector4d>> &pointcloud) {

    if (pointcloud.empty()) {
        cerr << "Point cloud is empty!" << endl;
        return;
    }

    pangolin::CreateWindowAndBind("Point Cloud Viewer", 1024, 768);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    pangolin::OpenGlRenderState s_cam(
        pangolin::ProjectionMatrix(1024, 768, 500, 500, 512, 389, 0.1, 1000),
        pangolin::ModelViewLookAt(0, -0.1, -1.8, 0, 0, 0, 0.0, -1.0, 0.0)
    );

    pangolin::View &d_cam = pangolin::CreateDisplay()
        .SetBounds(0.0, 1.0, pangolin::Attach::Pix(175), 1.0, -1024.0f / 768.0f)
        .SetHandler(new pangolin::Handler3D(s_cam));

    while (pangolin::ShouldQuit() == false) {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        d_cam.Activate(s_cam);
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

        glPointSize(2);
        glBegin(GL_POINTS);
        for (auto &p: pointcloud) {
            glColor3f(p[3], p[3], p[3]);
            glVertex3d(p[0], p[1], p[2]);
        }
        glEnd();
        pangolin::FinishFrame();
        usleep(5000);   // sleep 5 ms
    }
    return;
}
  • 结果

《视觉SLAM十四讲从理论到实践 第二版》实践部分——ch5_第6张图片
《视觉SLAM十四讲从理论到实践 第二版》实践部分——ch5_第7张图片

joinMap

  • CMakeLists.txt
# 声明要求的 cmake 最低版本
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

# 声明一个 cmake 工程
PROJECT(joinMap)

# 设置编译模式
SET(CMAKE_BUILD_TYPE "Release")

# 设置编译选项
SET(CMAKE_CXX_FLAGS "-std=c++11 -O2")

# 添加Eigen头文件
INCLUDE_DIRECTORIES("/usr/include/eigen3")

# 寻找OpenCV库
FIND_PACKAGE(OpenCV REQUIRED)

# 寻找Pangolin
FIND_PACKAGE(Pangolin REQUIRED)

# 寻找Sophus
FIND_PACKAGE(Sophus REQUIRED)

# 添加头文件
INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS})
INCLUDE_DIRECTORIES(${Pangolin_INCLUDE_DIRS})
INCLUDE_DIRECTORIES(${Sophus_INCLUDE_DIRS})

# 添加一个可执行程序
# 语法:ADD_EXECUTABLE( 程序名 源代码文件 )
ADD_EXECUTABLE(joinMap joinMap.cpp)

# 链接OpenCV库
TARGET_LINK_LIBRARIES(joinMap ${OpenCV_LIBS} ${Pangolin_LIBRARIES})
  • jointMap.cpp
#include 
#include 
#include 
#include   // for formating strings
#include 
#include 

using namespace std;
typedef vector<Sophus::SE3d, Eigen::aligned_allocator<Sophus::SE3d>> TrajectoryType;
typedef Eigen::Matrix<double, 6, 1> Vector6d;

// 在pangolin中画图,已写好,无需调整
void showPointCloud(
    const vector<Vector6d, Eigen::aligned_allocator<Vector6d>> &pointcloud);

int main(int argc, char **argv) {
    vector<cv::Mat> colorImgs, depthImgs;    // 彩色图和深度图
    TrajectoryType poses;         // 相机位姿

    ifstream fin("./pose.txt");
    if (!fin) {
        cerr << "请在有pose.txt的目录下运行此程序" << endl;
        return 1;
    }

    for (int i = 0; i < 5; i++) {
        boost::format fmt("./%s/%d.%s"); //图像文件格式
        colorImgs.push_back(cv::imread((fmt % "color" % (i + 1) % "png").str()));
        depthImgs.push_back(cv::imread((fmt % "depth" % (i + 1) % "pgm").str(), -1)); // 使用-1读取原始图像

        double data[7] = {0};
        for (auto &d:data)
            fin >> d;
        Sophus::SE3d pose(Eigen::Quaterniond(data[6], data[3], data[4], data[5]),
                          Eigen::Vector3d(data[0], data[1], data[2]));
        poses.push_back(pose);
    }

    // 计算点云并拼接
    // 相机内参 
    double cx = 325.5;
    double cy = 253.5;
    double fx = 518.0;
    double fy = 519.0;
    double depthScale = 1000.0;
    vector<Vector6d, Eigen::aligned_allocator<Vector6d>> pointcloud;
    pointcloud.reserve(1000000);

    for (int i = 0; i < 5; i++) {
        cout << "转换图像中: " << i + 1 << endl;
        cv::Mat color = colorImgs[i];
        cv::Mat depth = depthImgs[i];
        Sophus::SE3d T = poses[i];
        for (int v = 0; v < color.rows; v++)
            for (int u = 0; u < color.cols; u++) {
                unsigned int d = depth.ptr<unsigned short>(v)[u]; // 深度值
                if (d == 0) continue; // 为0表示没有测量到
                Eigen::Vector3d point;
                point[2] = double(d) / depthScale;
                point[0] = (u - cx) * point[2] / fx;
                point[1] = (v - cy) * point[2] / fy;
                Eigen::Vector3d pointWorld = T * point;

                Vector6d p;
                p.head<3>() = pointWorld;
                p[5] = color.data[v * color.step + u * color.channels()];   // blue
                p[4] = color.data[v * color.step + u * color.channels() + 1]; // green
                p[3] = color.data[v * color.step + u * color.channels() + 2]; // red
                pointcloud.push_back(p);
            }
    }

    cout << "点云共有" << pointcloud.size() << "个点." << endl;
    showPointCloud(pointcloud);
    return 0;
}

void showPointCloud(const vector<Vector6d, Eigen::aligned_allocator<Vector6d>> &pointcloud) {

    if (pointcloud.empty()) {
        cerr << "Point cloud is empty!" << endl;
        return;
    }

    pangolin::CreateWindowAndBind("Point Cloud Viewer", 1024, 768);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    pangolin::OpenGlRenderState s_cam(
        pangolin::ProjectionMatrix(1024, 768, 500, 500, 512, 389, 0.1, 1000),
        pangolin::ModelViewLookAt(0, -0.1, -1.8, 0, 0, 0, 0.0, -1.0, 0.0)
    );

    pangolin::View &d_cam = pangolin::CreateDisplay()
        .SetBounds(0.0, 1.0, pangolin::Attach::Pix(175), 1.0, -1024.0f / 768.0f)
        .SetHandler(new pangolin::Handler3D(s_cam));

    while (pangolin::ShouldQuit() == false) {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        d_cam.Activate(s_cam);
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

        glPointSize(2);
        glBegin(GL_POINTS);
        for (auto &p: pointcloud) {
            glColor3d(p[3] / 255.0, p[4] / 255.0, p[5] / 255.0);
            glVertex3d(p[0], p[1], p[2]);
        }
        glEnd();
        pangolin::FinishFrame();
        usleep(5000);   // sleep 5 ms
    }
    return;
}
  • 结果

运行时需要返回源程序所在目录

《视觉SLAM十四讲从理论到实践 第二版》实践部分——ch5_第8张图片

课后实践

实验一

  • CMakeLists.txt
# 声明要求的 cmake 最低版本
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

# 声明一个 cmake 工程
PROJECT(test1)

# 设置编译模式
SET(CMAKE_BUILD_TYPE "Release")

# 设置编译选项
SET(CMAKE_CXX_FLAGS "-std=c++11 -O2")

# 寻找OpenCV库
FIND_PACKAGE(OpenCV REQUIRED)

# 添加头文件
INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS})

# 添加一个可执行程序
# 语法:ADD_EXECUTABLE( 程序名 源代码文件 )
ADD_EXECUTABLE(test1 test1.cpp)

# 链接OpenCV库
TARGET_LINK_LIBRARIES(test1 ${OpenCV_LIBS})
  • test1.cpp
/*************************************************************************************
*
*   Description:相机标定(张氏标定法)  单目标定
*   Author     :mmx
*   Data       :2022.7.14
*
************************************************************************************/
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace cv;
using namespace std;

int main(int argc, char **argv)
{
    //保存文件名称
    vector<string>  filenames;

    //需要更改的参数
    //左相机标定,指定左相机图片路径,以及标定结果保存文件
    string infilename = "../filename.txt";        //如果是右相机把left改为right
    string outfilename = "../caliberation_result.txt";

    //标定所用图片文件的路径,每一行保存一个标定图片的路径  ifstream 是从硬盘读到内存
    ifstream fin(infilename);
    //保存标定的结果  ofstream 是从内存写到硬盘
    ofstream fout(outfilename);

    /*
    1.读取毎一幅图像,从中提取出角点,然后对角点进行亚像素精确化、获取每个角点在像素坐标系中的坐标
    像素坐标系的原点位于图像的左上角
    */
    cout << "开始提取角点......" << endl;
    //图像数量
    int imageCount = 0;
    //图像尺寸
    Size imageSize;
    //标定板上每行每列的角点数
    Size boardSize = Size(9, 6);
    //缓存每幅图像上检测到的角点
    vector<Point2f>  imagePointsBuf;
    //保存检测到的所有角点
    vector<vector<Point2f>> imagePointsSeq;
    char filename[100];
    if (!fin.is_open())
    {
        cout << "图片路径文件打开失败......" << endl;;
    }
    else
    {
        //读取完毕?
        while (!fin.eof()&& fin.peek() != EOF)
        {
            //一次读取一行
            fin.getline(filename, sizeof(filename) / sizeof(char));
            //保存文件名
            filenames.push_back(filename);
            //读取图片
            Mat imageInput = imread(filename);
            //读入第一张图片时获取图宽高信息
            if (imageCount == 0)
            {
                imageSize.width = imageInput.cols;
                imageSize.height = imageInput.rows;
                cout << "imageSize.width = " << imageSize.width << endl;
                cout << "imageSize.height = " << imageSize.height << endl;
            }

            cout << "imageCount = " << imageCount << endl;
            imageCount++;

            //提取每一张图片的角点
            if (findChessboardCorners(imageInput, boardSize, imagePointsBuf) == 0)
            {
                //找不到角点
                cout << "Can not find chessboard corners!" << endl;
                exit(1);
            }
            else
            {
                Mat viewGray;
                if (imageInput.channels() != 1)
                {
                    //转换为灰度图片
                    cvtColor(imageInput, viewGray, COLOR_BGR2GRAY);
                }
                else
                {
                    viewGray = imageInput;
                }
                //亚像素精确化   对粗提取的角点进行精确化
                find4QuadCornerSubpix(viewGray, imagePointsBuf, Size(5, 5));
                //保存亚像素点
                imagePointsSeq.push_back(imagePointsBuf);
                //在图像上显示角点位置
                drawChessboardCorners(viewGray, boardSize, imagePointsBuf, true);
                //显示图片
                namedWindow("Camera Calibration",0);
                imshow("Camera Calibration", viewGray);
                //等待0.5s
                waitKey(500);
            }
        }

        //计算每张图片上的角点数 54
        int cornerNum = boardSize.width * boardSize.height;

        //角点总数
        int total = imagePointsSeq.size() * cornerNum;
        cout << "total = " << total << endl;

        for (int i = 0; i < total; i++)
        {
            int num = i / cornerNum;
            int p = i % cornerNum;
            //cornerNum是每幅图片的角点个数,此判断语句是为了输出,便于调试
            if (p == 0)
            {
                cout << "\n第 " << num + 1 << "张图片的数据 -->: " << endl;
            }
            //输出所有的角点
            cout << p + 1 << ":(" << imagePointsSeq[num][p].x;
            cout << imagePointsSeq[num][p].y << ")\t";
            if ((p + 1) % 3 == 0)
            {
                cout << endl;
            }
        }

        cout << "角点提取完成!" << endl;

        /*
        2.摄像机标定 世界坐标系原点位于标定板左上角(第一个方格的左上角)
        */
        cout << "开始标定" << endl;
        //棋盘三维信息,设置棋盘在世界坐标系的坐标
        //实际测量得到标定板上每个棋盘格的大小
        Size squareSize = Size(15, 15);
        //毎幅图片角点数量
        vector<int> pointCounts;
        //保存标定板上角点的三维坐标
        vector<vector<Point3f>> objectPoints;
        //摄像机内参数矩阵 M=[fx γ u0,0 fy v0,0 0 1]
        Mat cameraMatrix = Mat(3, 3, CV_64F, Scalar::all(0));
        //摄像机的5个畸变系数k1,k2,p1,p2,k3
        Mat distCoeffs = Mat(1, 5, CV_64F, Scalar::all(0));
        //每幅图片的旋转向量
        vector<Mat> tvecsMat;
        //每幅图片的平移向量
        vector<Mat> rvecsMat;

        //初始化标定板上角点的三维坐标
        int i, j, t;
        for (t = 0; t < imageCount; t++)
        {
            vector<Point3f> tempPointSet;
            //行数
            for (i = 0; i < boardSize.height; i++)
            {
                //列数
                for (j = 0; j < boardSize.width; j++)
                {
                    Point3f realPoint;
                    //假设标定板放在世界坐标系中z=0的平面上。
                    realPoint.x = i * squareSize.width;
                    realPoint.y = j * squareSize.height;
                    realPoint.z = 0;
                    tempPointSet.push_back(realPoint);
                }
            }
            objectPoints.push_back(tempPointSet);
        }

        //初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板
        for (i = 0; i < imageCount; i++)
        {
            pointCounts.push_back(boardSize.width * boardSize.height);
        }
        //开始标定
        calibrateCamera(objectPoints, imagePointsSeq, imageSize, cameraMatrix, distCoeffs, rvecsMat, tvecsMat);
        cout << "标定完成" << endl;
        //对标定结果进行评价
        cout << "开始评价标定结果......" << endl;
        //所有图像的平均误差的总和
        double totalErr = 0.0;
        //每幅图像的平均误差
        double err = 0.0;
        //保存重新计算得到的投影点
        vector<Point2f> imagePoints2;
        cout << "每幅图像的标定误差:" << endl;
        fout << "每幅图像的标定误差:" << endl;
        for (i = 0; i < imageCount; i++)
        {
            vector<Point3f> tempPointSet = objectPoints[i];
            //通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点imagePoints2(在像素坐标系下的点坐标)
            projectPoints(tempPointSet, rvecsMat[i], tvecsMat[i], cameraMatrix, distCoeffs, imagePoints2);
            //计算新的投影点和旧的投影点之间的误差
            vector<Point2f> tempImagePoint = imagePointsSeq[i];
            Mat tempImagePointMat = Mat(1, tempImagePoint.size(), CV_32FC2);
            Mat imagePoints2Mat = Mat(1, imagePoints2.size(), CV_32FC2);
            for (int j = 0; j < tempImagePoint.size(); j++)
            {
                imagePoints2Mat.at<Vec2f>(0, j) = Vec2f(imagePoints2[j].x, imagePoints2[j].y);
                tempImagePointMat.at<Vec2f>(0, j) = Vec2f(tempImagePoint[j].x, tempImagePoint[j].y);
            }
            //Calculates an absolute difference norm or a relative difference norm.
            err = norm(imagePoints2Mat, tempImagePointMat, NORM_L2);
            totalErr += err /= pointCounts[i];
            cout << "  第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
            fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;

        }
        //每张图像的平均总误差
        cout << "  总体平均误差:" << totalErr / imageCount << "像素" << endl;
        fout << "总体平均误差:" << totalErr / imageCount << "像素" << endl;
        cout << "评价完成!" << endl;
        //保存标定结果
        cout << "开始保存标定结果....." << endl;
        //保存每张图像的旋转矩阵
        Mat rotationMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0));
        fout << "相机内参数矩阵:" << endl;
        fout << cameraMatrix << endl << endl;
        fout << "畸变系数:" << endl;
        fout << distCoeffs << endl << endl;

        for (int i = 0; i < imageCount; i++)
        {
            fout << "第" << i + 1 << "幅图像的旋转向量:" << endl;
            fout << tvecsMat[i] << endl;
            //将旋转向量转换为相对应的旋转矩阵
            Rodrigues(tvecsMat[i], rotationMatrix);
            fout << "第" << i + 1 << "幅图像的旋转矩阵:" << endl;
            fout << rotationMatrix << endl;
            fout << "第" << i + 1 << "幅图像的平移向量:" << endl;
            fout << rvecsMat[i] << endl;
        }
        cout << "保存完成" << endl;

        /************************************************************************
        显示定标结果
        *************************************************************************/
        Mat mapx = Mat(imageSize, CV_32FC1);
        Mat mapy = Mat(imageSize, CV_32FC1);
        Mat R = Mat::eye(3, 3, CV_32F);
        cout << "显示矫正图像" << endl;
        for (int i = 0; i != imageCount; i++)
        {
            cout << "Frame #" << i + 1 << "..." << endl;
            //计算图片畸变矫正的映射矩阵mapx、mapy(不进行立体校正、立体校正需要使用双摄)
            initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, imageSize, CV_32FC1, mapx, mapy);
            //读取一张图片
            Mat imageSource = imread(filenames[i]);
            Mat newimage = imageSource.clone();
            //另一种不需要转换矩阵的方式
            //undistort(imageSource,newimage,cameraMatrix,distCoeffs);
            //进行校正
            remap(imageSource, newimage, mapx, mapy, INTER_LINEAR);
            namedWindow("原始图像", 0);
            namedWindow("矫正后图像", 0);
            imshow("原始图像", imageSource);
            imshow("矫正后图像", newimage);
            waitKey(1);
        }

        //释放资源
        fin.close();
        fout.close();
        while (int c = waitKey(100))
        {
            if (c == 27)//Esc键退出
            {
                break;
            }
        }
    }
    return 0;
}
  • 结果
每幅图像的标定误差:
第1幅图像的平均误差:0.18676像素
第2幅图像的平均误差:0.174862像素
第3幅图像的平均误差:0.166978像素
第4幅图像的平均误差:0.234408像素
第5幅图像的平均误差:0.247064像素
第6幅图像的平均误差:0.171492像素
第7幅图像的平均误差:0.19559像素
第8幅图像的平均误差:0.185317像素
第9幅图像的平均误差:0.179189像素
第10幅图像的平均误差:0.176593像素
总体平均误差:0.191825像素
相机内参数矩阵:
[2977.371835938682, 0, 2015.193996498186;
 0, 2977.924586940979, 1501.910941371999;
 0, 0, 1]

畸变系数:
[0.1563844336425467, -1.001492230014174, 0.0004977475517197848, 0.001488388227723729, 1.877238614487959]

第1幅图像的旋转向量:
[-59.38930797210072;
 -30.78211045320226;
 134.9045819715447]
第1幅图像的旋转矩阵:
[0.9800171225585069, 0.1956563826922954, 0.03584716730664281;
 -0.191840466510037, 0.9773249283228452, -0.08962823153424779;
 -0.05257066578764019, 0.08096026426903144, 0.9953299757910101]
第1幅图像的平移向量:
[-2.200132075677376;
 -2.230930340666067;
 -0.0758200888713005]
第2幅图像的旋转向量:
[-61.59554671939376;
 -29.75829228149798;
 154.4640387057927]
第2幅图像的旋转矩阵:
[0.7893695107339748, 0.6129706663147499, 0.03409894076443795;
 -0.5817655042273183, 0.7646123099665632, -0.2773389866908911;
 -0.196073133332283, 0.1990853527642043, 0.9601668337846362]
第2幅图像的平移向量:
[2.204364804831064;
 2.232979970506114;
 0.07238288313453184]
第3幅图像的旋转向量:
[-61.80623643398209;
 -34.39246635625606;
 165.2927168571216]
第3幅图像的旋转矩阵:
[-0.5450903201351273, 0.7210710857453022, -0.4277067128267633;
 -0.4906269321335388, -0.6880383125899256, -0.5346854158041339;
 -0.6798247982827754, -0.08160741210514352, 0.7288199187242926]
第3幅图像的平移向量:
[-2.211453834642328;
 -2.219702439090737;
 -0.07361365384429419]
第4幅图像的旋转向量:
[-59.25286841015139;
 -30.32696859693395;
 137.9297980398531]
第4幅图像的旋转矩阵:
[-0.4506450391968914, -0.5071150307144368, -0.7346791097280011;
 0.7685174213111632, -0.6391133439313346, -0.03025073139856399;
 -0.4542026219524417, -0.5782460369361848, 0.6777399936399831]
第4幅图像的平移向量:
[2.327202840603296;
 2.066362098519285;
 0.08235216034154941]
第5幅图像的旋转向量:
[-58.28035970912374;
 -35.96506551851715;
 139.8117099668372]
第5幅图像的旋转矩阵:
[0.2896942815083203, 0.9558007685557478, -0.05022065401425679;
 -0.8129232179129496, 0.2180149714581153, -0.5400234383785318;
 -0.5052059629887922, 0.197267237645684, 0.8401503269726245]
第5幅图像的平移向量:
[2.242616696953213;
 2.136453844636352;
 0.06471939111404992]
第6幅图像的旋转向量:
[-46.84607296813854;
 -43.24499523803977;
 141.0275374905056]
第6幅图像的旋转矩阵:
[-0.5207686372452398, 0.814607708939396, -0.255370920424084;
 -0.5314259127931552, -0.5434432837094578, -0.649812201026607;
 -0.6681216398783215, -0.2026910899067588, 0.7159118635689121]
第6幅图像的平移向量:
[2.010316477039041;
 2.337249433958023;
 0.07256930128219492]
第7幅图像的旋转向量:
[-52.60113382816989;
 -35.27744702021112;
 141.6913999356278]
第7幅图像的旋转矩阵:
[-0.152022330396741, 0.9707198274608958, -0.1859898589575051;
 -0.7701911113974799, -0.23428024253182, -0.5932271233545767;
 -0.6194310800929015, 0.05306396643222394, 0.783255611203278]
第7幅图像的平移向量:
[2.108976796922094;
 2.246010396030473;
 0.07324938896137692]
第8幅图像的旋转向量:
[-58.36011180662006;
 -24.59347393864123;
 141.4764000602231]
第8幅图像的旋转矩阵:
[-0.2731663548281739, 0.8875896089543531, -0.3708973290091839;
 -0.7103528369663141, -0.4461124757085908, -0.5444102368903997;
 -0.6486747949502873, 0.1147534099424091, 0.7523647156152342]
第8幅图像的平移向量:
[2.221971242426484;
 2.128342781451464;
 0.07209695482538103]
第9幅图像的旋转向量:
[-60.21790207451063;
 -22.57126101093155;
 142.0940897232381]
第9幅图像的旋转矩阵:
[0.527104380240112, 0.8472473331851428, -0.06582498569959003;
 -0.7851457954672371, 0.4559026348089306, -0.4191644873249575;
 -0.3251262096352845, 0.2726256480688101, 0.9055209571419396]
第9幅图像的平移向量:
[2.32131378072042;
 2.047471967468829;
 0.07290782032641596]
第10幅图像的旋转向量:
[-62.47001137407899;
 -23.49216085524111;
 138.117271324714]
第10幅图像的旋转矩阵:
[-0.5493421376152866, -0.3475702784081317, -0.7598803309782146;
 0.579251069283809, -0.8138208656896856, -0.04651663467679718;
 -0.6022386691120317, -0.4657150417748867, 0.6483965494130763]
第10幅图像的平移向量:
[2.445253764841218;
 1.937606930638812;
 0.1002970731964215]

实验二

程序来源

  • CMakeLists.txt
# 声明要求的 cmake 最低版本
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)

# 声明一个 cmake 工程
PROJECT(test2)

# 设置编译模式
SET(CMAKE_BUILD_TYPE "Release")

# 设置编译选项
SET(CMAKE_CXX_FLAGS "-std=c++11 -O2")

# 寻找OpenCV库
FIND_PACKAGE(OpenCV REQUIRED)

# 添加头文件
INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS})

# 添加一个可执行程序
# 语法:ADD_EXECUTABLE( 程序名 源代码文件 )
ADD_EXECUTABLE(test2 test2.cpp)

# 链接OpenCV库
TARGET_LINK_LIBRARIES(test2 ${OpenCV_LIBS})
  • test2.cpp
//---------------------------------【头文件、命名空间包含部分】-----------------------------
//		描述:包含程序所使用的头文件和命名空间
//-------------------------------------------------------------------------------------------------
#include 
#include 
#include 
using namespace cv;
using namespace std;
 
 
 
//---------------------------------【宏定义部分】---------------------------------------------
//		描述:包含程序所使用宏定义
//-------------------------------------------------------------------------------------------------
#define NTESTS 14
#define NITERATIONS 20
 
 
 
//----------------------------------------- 【方法一】-------------------------------------------
//		说明:利用.ptr 和 []
//-------------------------------------------------------------------------------------------------
void colorReduce0(Mat &image, int div=64) {
 
	int nl= image.rows; //行数
	int nc= image.cols * image.channels(); //每行元素的总元素数量
 
	for (int j=0; j<nl; j++) 
	{
 
		uchar* data= image.ptr<uchar>(j);
 
		for (int i=0; i<nc; i++) 
		{
 
			//-------------开始处理每个像素-------------------
 
			data[i]= data[i]/div*div + div/2;
 
			//-------------结束像素处理------------------------
 
		} //单行处理结束                  
	}
}
 
//-----------------------------------【方法二】-------------------------------------------------
//		说明:利用 .ptr 和 * ++ 
//-------------------------------------------------------------------------------------------------
void colorReduce1(Mat &image, int div=64) {
 
	int nl= image.rows; //行数
	int nc= image.cols * image.channels(); //每行元素的总元素数量
 
	for (int j=0; j<nl; j++) 
	{
 
		uchar* data= image.ptr<uchar>(j);
 
		for (int i=0; i<nc; i++) 
		{
 
			//-------------开始处理每个像素-------------------
 
			*data++= *data/div*div + div/2;
 
			//-------------结束像素处理------------------------
 
		} //单行处理结束              
	}
}
 
//-----------------------------------------【方法三】-------------------------------------------
//		说明:利用.ptr 和 * ++ 以及模操作
//-------------------------------------------------------------------------------------------------
void colorReduce2(Mat &image, int div=64) {
 
	int nl= image.rows; //行数
	int nc= image.cols * image.channels(); //每行元素的总元素数量
 
	for (int j=0; j<nl; j++) 
	{
 
		uchar* data= image.ptr<uchar>(j);
 
		for (int i=0; i<nc; i++) 
		{
 
			//-------------开始处理每个像素-------------------
 
			int v= *data;
			*data++= v - v%div + div/2;
 
			//-------------结束像素处理------------------------
 
		} //单行处理结束                   
	}
}
 
//----------------------------------------【方法四】---------------------------------------------
//		说明:利用.ptr 和 * ++ 以及位操作
//----------------------------------------------------------------------------------------------------
void colorReduce3(Mat &image, int div=64) {
 
	int nl= image.rows; //行数
	int nc= image.cols * image.channels(); //每行元素的总元素数量
	int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
	//掩码值
	uchar mask= 0xFF<<n; // e.g. 对于 div=16, mask= 0xF0
 
	for (int j=0; j<nl; j++) {
 
		uchar* data= image.ptr<uchar>(j);
 
		for (int i=0; i<nc; i++) {
 
			//------------开始处理每个像素-------------------
 
			*data++= *data&mask + div/2;
 
			//-------------结束像素处理------------------------
		}  //单行处理结束            
	}
}
 
 
//----------------------------------------【方法五】----------------------------------------------
//		说明:利用指针算术运算
//---------------------------------------------------------------------------------------------------
void colorReduce4(Mat &image, int div=64) {
 
	int nl= image.rows; //行数
	int nc= image.cols * image.channels(); //每行元素的总元素数量
	int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
	int step= image.step; //有效宽度
	//掩码值
	uchar mask= 0xFF<<n; // e.g. 对于 div=16, mask= 0xF0
 
	//获取指向图像缓冲区的指针
	uchar *data= image.data;
 
	for (int j=0; j<nl; j++)
	{
 
		for (int i=0; i<nc; i++) 
		{
 
			//-------------开始处理每个像素-------------------
 
			*(data+i)= *data&mask + div/2;
 
			//-------------结束像素处理------------------------
 
		} //单行处理结束              
 
		data+= step;  // next line
	}
}
 
//---------------------------------------【方法六】----------------------------------------------
//		说明:利用 .ptr 和 * ++以及位运算、image.cols * image.channels()
//-------------------------------------------------------------------------------------------------
void colorReduce5(Mat &image, int div=64) {
 
	int nl= image.rows; //行数
	int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
	//掩码值
	uchar mask= 0xFF<<n; // e.g. 例如div=16, mask= 0xF0
 
	for (int j=0; j<nl; j++) 
	{
 
		uchar* data= image.ptr<uchar>(j);
 
		for (int i=0; i<image.cols * image.channels(); i++) 
		{
 
			//-------------开始处理每个像素-------------------
 
			*data++= *data&mask + div/2;
 
			//-------------结束像素处理------------------------
 
		} //单行处理结束            
	}
}
 
// -------------------------------------【方法七】----------------------------------------------
//		说明:利用.ptr 和 * ++ 以及位运算(continuous)
//-------------------------------------------------------------------------------------------------
void colorReduce6(Mat &image, int div=64) {
 
	int nl= image.rows; //行数
	int nc= image.cols * image.channels(); //每行元素的总元素数量
 
	if (image.isContinuous())  
	{
		//无填充像素
		nc= nc*nl; 
		nl= 1;  // 为一维数列
	}
 
	int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
	//掩码值
	uchar mask= 0xFF<<n; // e.g. 比如div=16, mask= 0xF0
 
	for (int j=0; j<nl; j++) {
 
		uchar* data= image.ptr<uchar>(j);
 
		for (int i=0; i<nc; i++) {
 
			//-------------开始处理每个像素-------------------
 
			*data++= *data&mask + div/2;
 
			//-------------结束像素处理------------------------
 
		} //单行处理结束                   
	}
}
 
//------------------------------------【方法八】------------------------------------------------
//		说明:利用 .ptr 和 * ++ 以及位运算 (continuous+channels)
//-------------------------------------------------------------------------------------------------
void colorReduce7(Mat &image, int div=64) {
 
	int nl= image.rows; //行数
	int nc= image.cols ; //列数
 
	if (image.isContinuous())  
	{
		//无填充像素
		nc= nc*nl; 
		nl= 1;  // 为一维数组
	}
 
	int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
	//掩码值
	uchar mask= 0xFF<<n; // e.g. 比如div=16, mask= 0xF0
 
	for (int j=0; j<nl; j++) {
 
		uchar* data= image.ptr<uchar>(j);
 
		for (int i=0; i<nc; i++) {
 
			//-------------开始处理每个像素-------------------
 
			*data++= *data&mask + div/2;
			*data++= *data&mask + div/2;
			*data++= *data&mask + div/2;
 
			//-------------结束像素处理------------------------
 
		} //单行处理结束                    
	}
}
 
 
// -----------------------------------【方法九】 ------------------------------------------------
//		说明:利用Mat_ iterator
//-------------------------------------------------------------------------------------------------
void colorReduce8(Mat &image, int div=64) {
 
	//获取迭代器
	Mat_<Vec3b>::iterator it= image.begin<Vec3b>();
	Mat_<Vec3b>::iterator itend= image.end<Vec3b>();
 
	for ( ; it!= itend; ++it) {
 
		//-------------开始处理每个像素-------------------
 
		(*it)[0]= (*it)[0]/div*div + div/2;
		(*it)[1]= (*it)[1]/div*div + div/2;
		(*it)[2]= (*it)[2]/div*div + div/2;
 
		//-------------结束像素处理------------------------
	}//单行处理结束  
}
 
//-------------------------------------【方法十】-----------------------------------------------
//		说明:利用Mat_ iterator以及位运算
//-------------------------------------------------------------------------------------------------
void colorReduce9(Mat &image, int div=64) {
 
	// div必须是2的幂
	int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
	//掩码值
	uchar mask= 0xFF<<n; // e.g. 比如 div=16, mask= 0xF0
 
	// 获取迭代器
	Mat_<Vec3b>::iterator it= image.begin<Vec3b>();
	Mat_<Vec3b>::iterator itend= image.end<Vec3b>();
 
	//扫描所有元素
	for ( ; it!= itend; ++it) 
	{
 
		//-------------开始处理每个像素-------------------
 
		(*it)[0]= (*it)[0]&mask + div/2;
		(*it)[1]= (*it)[1]&mask + div/2;
		(*it)[2]= (*it)[2]&mask + div/2;
 
		//-------------结束像素处理------------------------
	}//单行处理结束  
}
 
//------------------------------------【方法十一】---------------------------------------------
//		说明:利用Mat Iterator_
//-------------------------------------------------------------------------------------------------
void colorReduce10(Mat &image, int div=64) {
 
	//获取迭代器
	Mat_<Vec3b> cimage= image;
	Mat_<Vec3b>::iterator it=cimage.begin();
	Mat_<Vec3b>::iterator itend=cimage.end();
 
	for ( ; it!= itend; it++) { 
 
		//-------------开始处理每个像素-------------------
 
		(*it)[0]= (*it)[0]/div*div + div/2;
		(*it)[1]= (*it)[1]/div*div + div/2;
		(*it)[2]= (*it)[2]/div*div + div/2;
 
		//-------------结束像素处理------------------------
	}
}
 
//--------------------------------------【方法十二】--------------------------------------------
//		说明:利用动态地址计算配合at
//-------------------------------------------------------------------------------------------------
void colorReduce11(Mat &image, int div=64) {
 
	int nl= image.rows; //行数
	int nc= image.cols; //列数
 
	for (int j=0; j<nl; j++) 
	{
		for (int i=0; i<nc; i++) 
		{
 
			//-------------开始处理每个像素-------------------
 
			image.at<Vec3b>(j,i)[0]=	 image.at<Vec3b>(j,i)[0]/div*div + div/2;
			image.at<Vec3b>(j,i)[1]=	 image.at<Vec3b>(j,i)[1]/div*div + div/2;
			image.at<Vec3b>(j,i)[2]=	 image.at<Vec3b>(j,i)[2]/div*div + div/2;
 
			//-------------结束像素处理------------------------
 
		} //单行处理结束                 
	}
}
 
//----------------------------------【方法十三】----------------------------------------------- 
//		说明:利用图像的输入与输出
//-------------------------------------------------------------------------------------------------
void colorReduce12(const Mat &image, //输入图像
	Mat &result,      // 输出图像
	int div=64) {
 
		int nl= image.rows; //行数
		int nc= image.cols ; //列数
 
		//准备好初始化后的Mat给输出图像
		result.create(image.rows,image.cols,image.type());
 
		//创建无像素填充的图像
		nc= nc*nl; 
		nl= 1;  //单维数组
 
		int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
		//掩码值
		uchar mask= 0xFF<<n; // e.g.比如div=16, mask= 0xF0
 
		for (int j=0; j<nl; j++) {
 
			uchar* data= result.ptr<uchar>(j);
			const uchar* idata= image.ptr<uchar>(j);
 
			for (int i=0; i<nc; i++) {
 
				//-------------开始处理每个像素-------------------
 
				*data++= (*idata++)&mask + div/2;
				*data++= (*idata++)&mask + div/2;
				*data++= (*idata++)&mask + div/2;
 
				//-------------结束像素处理------------------------
 
			} //单行处理结束                   
		}
}
 
//--------------------------------------【方法十四】------------------------------------------- 
//		说明:利用操作符重载
//-------------------------------------------------------------------------------------------------
void colorReduce13(Mat &image, int div=64) {
 
	int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
	//掩码值
	uchar mask= 0xFF<<n; // e.g. 比如div=16, mask= 0xF0
 
	//进行色彩还原
	image=(image&Scalar(mask,mask,mask))+Scalar(div/2,div/2,div/2);
}
 
 
 
 
//-----------------------------------【ShowHelpText( )函数】-----------------------------
//		描述:输出一些帮助信息
//----------------------------------------------------------------------------------------------
void ShowHelpText()
{
	//输出欢迎信息和OpenCV版本
	printf("\n\n\t\t\t非常感谢购买《OpenCV3编程入门》一书!\n");
	printf("\n\n\t\t\t此为本书OpenCV2版的第24个配套示例程序\n");
	printf("\n\n\t\t\t   当前使用的OpenCV版本为:" CV_VERSION );
	printf("\n\n  ----------------------------------------------------------------------------\n");
 
	printf("\n\n正在进行存取操作,请稍等……\n\n");
}
 
 
 
 
//-----------------------------------【main( )函数】--------------------------------------------
//		描述:控制台应用程序的入口函数,我们的程序从这里开始
//-------------------------------------------------------------------------------------------------
int main( )
{
	int64 t[NTESTS],tinit;
	Mat image0;
	Mat image1;
	Mat image2;
 
	system("color 4F");
 
	ShowHelpText();
	 	
	image0= imread("../ubuntu.png");
	if (!image0.data)
		return 0; 
 
	//时间值设为0
	for (int i=0; i<NTESTS; i++)
		t[i]= 0;
 
 
	// 多次重复测试
	int n=NITERATIONS;
	for (int k=0; k<n; k++)
	{
		cout << k << " of " << n << endl; 
 
		image1= imread("../ubuntu.png");
		//【方法一】利用.ptr 和 []
		tinit= getTickCount();
		colorReduce0(image1);
		t[0]+= getTickCount()-tinit;
 
		//【方法二】利用 .ptr 和 * ++ 
		image1= imread("../ubuntu.png");
		tinit= getTickCount();
		colorReduce1(image1);
		t[1]+= getTickCount()-tinit;
 
		//【方法三】利用.ptr 和 * ++ 以及模操作
		image1= imread("../ubuntu.png");
		tinit= getTickCount();
		colorReduce2(image1);
		t[2]+= getTickCount()-tinit;
 
		//【方法四】 利用.ptr 和 * ++ 以及位操作
		image1= imread("../ubuntu.png");
		tinit= getTickCount();
		colorReduce3(image1);
		t[3]+= getTickCount()-tinit;
 
		//【方法五】 利用指针的算术运算
		image1= imread("../ubuntu.png");
		tinit= getTickCount();
		colorReduce4(image1);
		t[4]+= getTickCount()-tinit;
 
		//【方法六】利用 .ptr 和 * ++以及位运算、image.cols * image.channels()
		image1= imread("../ubuntu.png");
		tinit= getTickCount();
		colorReduce5(image1);
		t[5]+= getTickCount()-tinit;
 
		//【方法七】利用.ptr 和 * ++ 以及位运算(continuous)
		image1= imread("../ubuntu.png");
		tinit= getTickCount();
		colorReduce6(image1);
		t[6]+= getTickCount()-tinit;
 
		//【方法八】利用 .ptr 和 * ++ 以及位运算 (continuous+channels)
		image1= imread("../ubuntu.png");
		tinit= getTickCount();
		colorReduce7(image1);
		t[7]+= getTickCount()-tinit;
 
		//【方法九】 利用Mat_ iterator
		image1= imread("../ubuntu.png");
		tinit= getTickCount();
		colorReduce8(image1);
		t[8]+= getTickCount()-tinit;
 
		//【方法十】 利用Mat_ iterator以及位运算
		image1= imread("../ubuntu.png");
		tinit= getTickCount();
		colorReduce9(image1);
		t[9]+= getTickCount()-tinit;
 
		//【方法十一】利用Mat Iterator_
		image1= imread("../ubuntu.png");
		tinit= getTickCount();
		colorReduce10(image1);
		t[10]+= getTickCount()-tinit;
 
		//【方法十二】 利用动态地址计算配合at
		image1= imread("../ubuntu.png");
		tinit= getTickCount();
		colorReduce11(image1);
		t[11]+= getTickCount()-tinit;
 
		//【方法十三】 利用图像的输入与输出
		image1= imread("../ubuntu.png");
		tinit= getTickCount();
		Mat result;
		colorReduce12(image1, result);
		t[12]+= getTickCount()-tinit;
		image2= result;
 
		//【方法十四】 利用操作符重载
		image1= imread("../ubuntu.png");
		tinit= getTickCount();
		colorReduce13(image1);
		t[13]+= getTickCount()-tinit;
 
		//------------------------------
	}
	//输出图像   
	imshow("原始图像",image0);
	imshow("结果",image2);
	imshow("图像结果",image1);
 
	// 输出平均执行时间
	cout << endl << "-------------------------------------------" << endl << endl;
	cout << "\n【方法一】利用.ptr 和 []的方法所用时间为 " << 1000.*t[0]/getTickFrequency()/n << "ms" << endl;
	cout << "\n【方法二】利用 .ptr 和 * ++ 的方法所用时间为" << 1000.*t[1]/getTickFrequency()/n << "ms" << endl;
	cout << "\n【方法三】利用.ptr 和 * ++ 以及模操作的方法所用时间为" << 1000.*t[2]/getTickFrequency()/n << "ms" << endl;
	cout << "\n【方法四】利用.ptr 和 * ++ 以及位操作的方法所用时间为" << 1000.*t[3]/getTickFrequency()/n << "ms" << endl;
	cout << "\n【方法五】利用指针算术运算的方法所用时间为" << 1000.*t[4]/getTickFrequency()/n << "ms" << endl;
	cout << "\n【方法六】利用 .ptr 和 * ++以及位运算、channels()的方法所用时间为" << 1000.*t[5]/getTickFrequency()/n << "ms" << endl;
	cout << "\n【方法七】利用.ptr 和 * ++ 以及位运算(continuous)的方法所用时间为" << 1000.*t[6]/getTickFrequency()/n << "ms" << endl;
	cout << "\n【方法八】利用 .ptr 和 * ++ 以及位运算 (continuous+channels)的方法所用时间为" << 1000.*t[7]/getTickFrequency()/n << "ms" << endl;
	cout << "\n【方法九】利用Mat_ iterator 的方法所用时间为" << 1000.*t[8]/getTickFrequency()/n << "ms" << endl;
	cout << "\n【方法十】利用Mat_ iterator以及位运算的方法所用时间为" << 1000.*t[9]/getTickFrequency()/n << "ms" << endl;
	cout << "\n【方法十一】利用Mat Iterator_的方法所用时间为" << 1000.*t[10]/getTickFrequency()/n << "ms" << endl;	
	cout << "\n【方法十二】利用动态地址计算配合at 的方法所用时间为" << 1000.*t[11]/getTickFrequency()/n << "ms" << endl;	
	cout << "\n【方法十三】利用图像的输入与输出的方法所用时间为" << 1000.*t[12]/getTickFrequency()/n << "ms" << endl;	
	cout << "\n【方法十四】利用操作符重载的方法所用时间为" << 1000.*t[13]/getTickFrequency()/n << "ms" << endl;	
 
	waitKey();
	return 0;
}
  • 结果
【方法一】利用.ptr 和 []的方法所用时间为 0.316854ms

【方法二】利用 .ptr 和 * ++ 的方法所用时间为0.296142ms

【方法三】利用.ptr 和 * ++ 以及模操作的方法所用时间为0.230324ms

【方法四】利用.ptr 和 * ++ 以及位操作的方法所用时间为0.201886ms

【方法五】利用指针算术运算的方法所用时间为0.851921ms

【方法六】利用 .ptr 和 * ++以及位运算、channels()的方法所用时间为2.33311ms

【方法七】利用.ptr 和 * ++ 以及位运算(continuous)的方法所用时间为0.173723ms

【方法八】利用 .ptr 和 * ++ 以及位运算 (continuous+channels)的方法所用时间为0.17203ms

【方法九】利用Mat_ iterator 的方法所用时间为1.81503ms

【方法十】利用Mat_ iterator以及位运算的方法所用时间为2.28199ms

【方法十一】利用Mat Iterator_的方法所用时间为1.81912ms

【方法十二】利用动态地址计算配合at 的方法所用时间为2.01833ms

【方法十三】利用图像的输入与输出的方法所用时间为0.356391ms

【方法十四】利用操作符重载的方法所用时间为0.764589ms

你可能感兴趣的:(视觉SLAM,计算机视觉,计算机视觉,学习)