课内实践
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 <iostream>
#include <chrono>
using namespace std;
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
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;
}
- 结果
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 <opencv2/opencv.hpp>
#include <string>
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;
}
- 结果
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 <opencv2/opencv.hpp>
#include <vector>
#include <string>
#include <Eigen/Core>
#include <pangolin/pangolin.h>
#include <unistd.h>
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;
}
- 结果
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 <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>
#include <boost/format.hpp> // for formating strings
#include <sophus/se3.hpp>
#include <pangolin/pangolin.h>
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;
}
- 结果
运行时需要返回源程序所在目录
课后实践
实验一
- 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 <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <fstream>
#include <vector>
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 <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
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