相机畸变校正、求出参数、具体应用

  • 一、原理
  • 二、具体步骤
  • 三、参数获取程序代码
  • 四、使用程序
  • 后续版本:
  • 畸变矫正应用封装后,耗时6ms
  • 参数采集第二版,添加了一些异常判断和提示
    • 参数获取程序源码
  • 运行效果图
  • 判断图像采集的是否合规
  • 标定图像采集小技巧——使用代码按键采集

一、原理

https://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html
前提:摄像头固定因为摄像头一动,内参不变(畸变系数),但是外参(坐标变换)会变。
通过拍摄几张标定板的照片,然后得到畸变系数和相机内外参系数,然后每次读取摄像机图片时,将这些系数带进去,计算之后就可以得到矫正后的图片了。
效果如下:

畸变校正前

畸变校正后

显然上面图片四周直线都是弯曲的,被矫正后,变得效果不错了。

二、具体步骤

标定图:
程序在第三部分,具体步骤如下:
1、将第三步的代码复制到工程里
2、 在工程目录下(主函数.cpp相同目录下)建立一个caliberation文件夹,采集10——20张照片(不同角度,方向,但是要把角点全部显示出来),将照片放入该文件夹下。

效果如下:

3、新建一个calibdata.txt文件,将步骤2的图片路径写进去格式如下:

./caliberation/1.jpg
./caliberation/2.jpg
./caliberation/3.jpg
./caliberation/4.jpg
./caliberation/5.jpg
./caliberation/6.jpg
./caliberation/7.jpg
./caliberation/8.jpg
./caliberation/9.jpg
./caliberation/10.jpg
./caliberation/11.jpg
./caliberation/12.jpg

4、新建一个chess文件夹(名字随便,记得在程序里改),用于保存畸变校正后的图片。
5、运行程序,会生成一个caliberation_result.txt文件,里面保存了内外参等一些参数。比如畸变系数,旋转矩阵,平移矩阵等。

三、参数获取程序代码

//2018.6.19:畸变校正#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <fstream>    using namespace cv;
using namespace std;void main()
{ifstream fin("calibdata.txt"); /* 标定所用图像文件的路径 */ofstream fout("caliberation_result.txt");  /* 保存标定结果的文件 *///读取每一幅图像,从中提取出角点,然后对角点进行亚像素精确化     cout << "开始提取角点………………";int image_count = 0;  /* 图像数量 */Size image_size;  /* 图像的尺寸 */Size board_size = Size(4, 6);    /* 标定板上每行、列的角点数 */vector<Point2f> image_points_buf;  /* 缓存每幅图像上检测到的角点 */vector<vector<Point2f>> image_points_seq; /* 保存检测到的所有角点 */string filename;int count = -1;//用于存储角点个数。    while (getline(fin, filename)){image_count++;// 用于观察检验输出    cout << "image_count = " << image_count << endl;/* 输出检验*/cout << "-->count = " << count;Mat imageInput = imread(filename);if (imageInput.empty()){cout << "can not open pic!\n";exit(-1);}if (image_count == 1)  //读入第一张图片时获取图像宽高信息    {image_size.width = imageInput.cols;image_size.height = imageInput.rows;cout << "image_size.width = " << image_size.width << endl;cout << "image_size.height = " << image_size.height << endl;}/* 提取角点 */if (0 == findChessboardCorners(imageInput, board_size, image_points_buf)){cout << "can not find chessboard corners!\n"; //找不到角点    exit(1);}else{Mat view_gray;cvtColor(imageInput, view_gray, CV_RGB2GRAY);/* 亚像素精确化 */find4QuadCornerSubpix(view_gray, image_points_buf, Size(5, 5)); //对粗提取的角点进行精确化    //cornerSubPix(view_gray,image_points_buf,Size(5,5),Size(-1,-1),TermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,30,0.1));    image_points_seq.push_back(image_points_buf);  //保存亚像素角点    /* 在图像上显示角点位置 */drawChessboardCorners(view_gray, board_size, image_points_buf, false); //用于在图片中标记角点    namedWindow("Camera Calibration", 0);//创建窗口imshow("Camera Calibration", view_gray);//显示图片    waitKey(500);//暂停0.5S           }}int total = image_points_seq.size();cout << "total = " << total << endl;int CornerNum = board_size.width*board_size.height;  //每张图片上总的角点数    for (int ii = 0; ii<total; ii++){if (0 == ii % CornerNum)// 24 是每幅图片的角点个数。此判断语句是为了输出 图片号,便于控制台观看     {int i = -1;i = ii / CornerNum;int j = i + 1;cout << "--> 第 " << j << "图片的数据 --> : " << endl;}if (0 == ii % 3)  // 此判断语句,格式化输出,便于控制台查看    {cout << endl;}else{cout.width(10);}//输出所有的角点    cout << " -->" << image_points_seq[ii][0].x;cout << " -->" << image_points_seq[ii][0].y;}cout << "角点提取完成!\n";//以下是摄像机标定    cout << "开始标定………………";/*棋盘三维信息*/Size square_size = Size(10, 10);  /* 实际测量得到的标定板上每个棋盘格的大小 */vector<vector<Point3f>> object_points; /* 保存标定板上角点的三维坐标 *//*内外参数*/Mat cameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /* 摄像机内参数矩阵 */vector<int> point_counts;  // 每幅图像中角点的数量    Mat distCoeffs = Mat(1, 5, CV_32FC1, Scalar::all(0)); /* 摄像机的5个畸变系数:k1,k2,p1,p2,k3 */vector<Mat> tvecsMat;  /* 每幅图像的旋转向量 */vector<Mat> rvecsMat; /* 每幅图像的平移向量 *//* 初始化标定板上角点的三维坐标 */int i, j, t;for (t = 0; t<image_count; t++){vector<Point3f> tempPointSet;for (i = 0; i<board_size.height; i++){for (j = 0; j<board_size.width; j++){Point3f realPoint;/* 假设标定板放在世界坐标系中z=0的平面上 */realPoint.x = i * square_size.width;realPoint.y = j * square_size.height;realPoint.z = 0;tempPointSet.push_back(realPoint);}}object_points.push_back(tempPointSet);}/* 初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板 */for (i = 0; i<image_count; i++){point_counts.push_back(board_size.width*board_size.height);}/* 开始标定 */calibrateCamera(object_points, image_points_seq, image_size, cameraMatrix, distCoeffs, rvecsMat, tvecsMat, 0);cout << "标定完成!\n";//对标定结果进行评价    cout << "开始评价标定结果………………\n";double total_err = 0.0; /* 所有图像的平均误差的总和 */double err = 0.0; /* 每幅图像的平均误差 */vector<Point2f> image_points2; /* 保存重新计算得到的投影点 */cout << "\t每幅图像的标定误差:\n";fout << "每幅图像的标定误差:\n";for (i = 0; i<image_count; i++){vector<Point3f> tempPointSet = object_points[i];/* 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 */projectPoints(tempPointSet, rvecsMat[i], tvecsMat[i], cameraMatrix, distCoeffs, image_points2);/* 计算新的投影点和旧的投影点之间的误差*/vector<Point2f> tempImagePoint = image_points_seq[i];Mat tempImagePointMat = Mat(1, tempImagePoint.size(), CV_32FC2);Mat image_points2Mat = Mat(1, image_points2.size(), CV_32FC2);for (int j = 0; j < tempImagePoint.size(); j++){image_points2Mat.at<Vec2f>(0, j) = Vec2f(image_points2[j].x, image_points2[j].y);tempImagePointMat.at<Vec2f>(0, j) = Vec2f(tempImagePoint[j].x, tempImagePoint[j].y);}err = norm(image_points2Mat, tempImagePointMat, NORM_L2);total_err += err /= point_counts[i];std::cout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;}std::cout << "总体平均误差:" << total_err / image_count << "像素" << endl;fout << "总体平均误差:" << total_err / image_count << "像素" << endl << endl;std::cout << "评价完成!" << endl;//保存定标结果        std::cout << "开始保存定标结果………………" << endl;Mat rotation_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /* 保存每幅图像的旋转矩阵 */fout << "相机内参数矩阵:" << endl;fout << cameraMatrix << endl << endl;fout << "畸变系数:\n";fout << distCoeffs << endl << endl << endl;for (int i = 0; i<image_count; i++){fout << "第" << i + 1 << "幅图像的旋转向量:" << endl;fout << tvecsMat[i] << endl;/* 将旋转向量转换为相对应的旋转矩阵 */Rodrigues(tvecsMat[i], rotation_matrix);fout << "第" << i + 1 << "幅图像的旋转矩阵:" << endl;fout << rotation_matrix << endl;fout << "第" << i + 1 << "幅图像的平移向量:" << endl;fout << rvecsMat[i] << endl << endl;}std::cout << "完成保存" << endl;fout << endl;/************************************************************************显示定标结果*************************************************************************/Mat mapx = Mat(image_size, CV_32FC1);Mat mapy = Mat(image_size, CV_32FC1);Mat R = Mat::eye(3, 3, CV_32F);std::cout << "保存矫正图像" << endl;string imageFileName;std::stringstream StrStm;for (int i = 0; i != image_count; i++){std::cout << "Frame #" << i + 1 << "..." << endl;/**/initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, image_size, CV_32FC1, mapx, mapy);StrStm.clear();imageFileName.clear();string filePath = "chess";StrStm << i + 1;StrStm >> imageFileName;filePath += imageFileName;filePath += ".jpg";Mat imageSource = imread("1.jpg"); //读取畸变图片Mat newimage = imageSource.clone(); //校正后输出图片//另一种不需要转换矩阵的方式    //   undistort(imageSource,newimage,cameraMatrix,distCoeffs);    remap(imageSource, newimage, mapx, mapy, INTER_LINEAR);StrStm.clear();filePath.clear();StrStm << i + 1;StrStm >> imageFileName;imageFileName += "_d.jpg";imwrite(imageFileName, newimage);}std::cout << "保存结束" << endl;return;
}

四、使用程序

这个部分就是将得到的参数,应用到具体的程序中,不用每次进行标定,只要摄像头位置不变,就可以将畸变参数带进去就可以矫正。


void InitMat(Mat& m, float* num)
{for (int i = 0; i<m.rows; i++)for (int j = 0; j<m.cols; j++)m.at<float>(i, j) = *(num + i * m.rows + j);
}int main()
{int i = 1000;int n = 1;Mat edges;Mat frame = imread("2.jpg"); //读取畸变图片Mat R = Mat::eye(3, 3, CV_32F);Size image_size;  /* 图像的尺寸 *///获取图像大小image_size.width = 1920;image_size.height = 1080;//cameraMatrix为 "相机内参数矩阵:" << endl;Mat mapx = Mat(image_size, CV_32FC1);Mat mapy = Mat(image_size, CV_32FC1);//参数矩阵float neican_data[] = { 9558.649257742036, 0, 959.3165310990756, 0, 9435.752651759443, 532.7507141910969, 0, 0, 1 };Mat cameraMatrix(3, 3, CV_32FC1);InitMat(cameraMatrix, neican_data);cout << "cameraMatrix= " << endl << " " << cameraMatrix << endl << endl;//测得的畸变系数float jibian_data[] = { -6.956561513881647, -68.83902522804168, -0.004834538444671919, 0.01471273691928269, -0.4916103704308509 };Mat distCoeffs(1, 5, CV_32FC1); /* 摄像机的5个畸变系数:k1,k2,p1,p2,k3 */InitMat(distCoeffs, jibian_data);cout << "distCoeffs= " << endl << " " << distCoeffs << endl << endl;i = 0;namedWindow("【原始图】", 0);//参数为零,则可以自由拖动imshow("【原始图】", frame);/********相机矫正*******************************************************************************/initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, image_size, CV_32FC1, mapx, mapy);Mat imageSource = frame; //读取畸变图片Mat newimage = imageSource.clone(); //校正后输出图片remap(imageSource, newimage, mapx, mapy, INTER_LINEAR);namedWindow("畸变校正后的图片", 0);//参数为零,则可以自由拖动imshow("畸变校正后的图片", newimage);}

上面只是矫正部分的代码


分界线

后续版本:

畸变矫正应用封装后,耗时6ms

/*****************************************************
2021.4.29:畸变校正
******************************************************/
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <fstream>    using namespace cv;
using namespace std;#define SRC_WIDTH  1920
#define SRC_HEIGHT 1080void InitMat(float* num , Mat& m)
{for (int i = 0; i<m.rows; i++)for (int j = 0; j<m.cols; j++)m.at<float>(i, j) = *(num + i * m.rows + j);
}Mat mapx = Mat(Size(SRC_WIDTH, SRC_HEIGHT), CV_32FC1);
Mat mapy = Mat(Size(SRC_WIDTH, SRC_HEIGHT), CV_32FC1);
Mat newimage ; //校正后输出图片void jiaozheng_init(void)
{int OK = 0;Mat R = Mat::eye(3, 3, CV_32F);//参数矩阵float neican_data[] = { 9558.649257742036, 0, 959.3165310990756, 0, 9435.752651759443, 532.7507141910969, 0, 0, 1 };Mat cameraMatrix(3, 3, CV_32FC1);InitMat(neican_data,cameraMatrix);cout << "cameraMatrix= " << endl << " " << cameraMatrix << endl << endl;//测得的畸变系数float jibian_data[] = { -6.956561513881647, -68.83902522804168, -0.004834538444671919, 0.01471273691928269, -0.4916103704308509 };Mat distCoeffs(1, 5, CV_32FC1); /* 摄像机的5个畸变系数:k1,k2,p1,p2,k3 */InitMat( jibian_data, distCoeffs);cout << "distCoeffs= " << endl << " " << distCoeffs << endl << endl;/********相机矫正*******************************************************************************///cout << "mapx= " << endl << " " << mapx << endl << endl;//cout << "mapy= " << endl << " " << mapy << endl << endl;initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, Size(SRC_WIDTH, SRC_HEIGHT), CV_32FC1, mapx, mapy);//cout << "mapx= " << endl << " " << mapx << endl << endl;//cout << "mapy= " << endl << " " << mapy << endl << endl;}int jibianjiaozheng(Mat src_picture, Mat & dst_picture)
{int OK = 0;static unsigned int INIT_MAT_OK = 1;if (INIT_MAT_OK){newimage = src_picture.clone(); //校正后输出图片INIT_MAT_OK = 0;}remap(src_picture, newimage, mapx, mapy, INTER_LINEAR);dst_picture= newimage+1;return OK;
}int main()
{# if 0//测试图片int hrst = 0;Mat frame = imread("yuantu.jpg"); //读取畸变图片Mat after;static unsigned int jiaozheng_init_flag = 0;if (!jiaozheng_init_flag){printf("初始化\n");jiaozheng_init();jiaozheng_init_flag = 1;}hrst = jibianjiaozheng(frame, after);if (hrst != 0){printf("畸变矫正失败\n");}namedWindow("畸变校正后的图片", 0);//参数为零,则可以自由拖动imshow("畸变校正后的图片", after);
#else//测试视频VideoCapture capture;capture.open("test_1.mp4");if (!capture.isOpened()){printf("文件打开失败");}Mat frame;Mat after;int hrst = 0;unsigned int jiaozheng_init_flag = 0;int timestart, timeEnd, duration;while (true){timestart = clock() * 1000 / CLOCKS_PER_SEC;capture >> frame;if (frame.data == NULL){printf("Image is empty\n");//writer.write(frame);break;//continue;}duration = clock() - timestart;printf("读图用时约: %d毫秒 \n", duration * 1000 / CLOCKS_PER_SEC);if (!jiaozheng_init_flag){printf("初始化\n");jiaozheng_init();jiaozheng_init_flag = 1;}hrst = jibianjiaozheng(frame, after);if (hrst != 0){printf("畸变矫正失败\n");}else{//printf("畸变矫正成功\n");}timeEnd = clock() * 1000 / CLOCKS_PER_SEC;duration = timeEnd- timestart;printf("畸变校正用时约: %d毫秒 \n", duration );printf("\n");//namedWindow("畸变校正后的图片", 0);//参数为零,则可以自由拖动//imshow("畸变校正后的图片", after);//waitKey(2);}#endifreturn 0;}//
//int main()
//{//  Mat frame = imread("yuantu.jpg"); //读取畸变图片
//  //cv::VideoWriter wrt("./test.avi", cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), 30, cv::Size(1920, 1080));
//  cv::VideoWriter wrt("./test_1.mp4", cv::VideoWriter::fourcc('D', 'I', 'V', 'X'), 30, cv::Size(1920, 1080));
//
//  for(int i=0;i<600;i++)
//  {//
//      string fps_str = "frame_number: " + to_string(i);
//      Mat frame0 =frame.clone();
//      putText(frame0, fps_str, Point2f(10, 80), FONT_HERSHEY_COMPLEX_SMALL, 3.2, Scalar(50, 255, 0), 3);
//
//      namedWindow("畸变校正后的图片", 0);//参数为零,则可以自由拖动
//      imshow("畸变校正后的图片", frame0);
//      waitKey(33);
//      wrt << frame0; // where image should be cv::Mat class
//  }
//
//
//  return 0;
//}

参数采集第二版,添加了一些异常判断和提示

参数获取程序源码

/*****************************************************
2021.4.29:畸变校正标定过程,用于获取畸变矫正的几个参数根据不同的标定板需要修改;
Size board_size = Size(4, 6);    /* 标定板上每行、列的角点数
******************************************************/#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 <iostream>#include <stdlib.h> //srand()和rand()函数
#include<windows.h>using namespace cv;
using namespace std;/****************        打印相关组件 start !  ********************************************************************/#define  debug_show_picture        1 //是否显示部分调试图片,方便调试
#define  debug_save_shipin       0 //是否保存结果视频/*打印等级,修改后面的宏定义可以改变函数输出打印等级*/
#define ALG_PRTINT_LEVER PRINT_LEVEL_UNLIMIT#define ALG_PRTINT(...)  SAL_printf(__VA_ARGS__)
#define ALG_PRT(...)     ALG_PRTINT(__FUNCTION__, __LINE__, PRINT_LEVEL_UNLIMIT, __VA_ARGS__)
#define ALG_DBG(...)     ALG_PRTINT(__FUNCTION__, __LINE__, PRINT_LEVEL_DBG,     __VA_ARGS__)
#define ALG_WAR(...)     ALG_PRTINT(__FUNCTION__, __LINE__, PRINT_LEVEL_WRN,     __VA_ARGS__)
#define ALG_ERR(...)     ALG_PRTINT(__FUNCTION__, __LINE__, PRINT_LEVEL_ERR,     __VA_ARGS__)/***********************************************************************************************
* @enum     HAT_SAL_PRT_LEVEL_E
* @brief    打印输出的等级
***************************************************************************************************/
typedef enum _PRT_LEVEL_E_
{PRINT_LEVEL_ERR = 0,        /*错误打印信息*/PRINT_LEVEL_WRN = 1,        /*警告打印信息*/PRINT_LEVEL_DBG = 2,         /*调试打印信息*/PRINT_LEVEL_UNLIMIT = 3,    /*无限制打印信息*/PRINT_LEVEL_NOPRT = 4,      /*没有打印信息*/
} PRT_LEVEL_E;/*******************************************************************************
Function:   SAL_printf
Description: 该函数能够通过设置的打印等级ALG_PRTINT_LEVER,来控制是否输出相关语句
Input:
Output:
Return:
0:          Successful
ohters:     Failed
*******************************************************************************/
void SAL_printf(const char *pFun, UINT line, PRT_LEVEL_E levelParam, const char *fmt, ...)
{static INT8 g_printfInfo[4][16] = { "ERR", "WAR", "DBG", "INF" };va_list p;if (ALG_PRTINT_LEVER == PRINT_LEVEL_NOPRT || levelParam == PRINT_LEVEL_NOPRT){return;}if (levelParam <= ALG_PRTINT_LEVER){va_start(p, fmt);printf("[ALG][%s][%s][%4d] ", g_printfInfo[levelParam], pFun, line);vprintf(fmt, p);va_end(p);}
}
/******************************  打印相关组件 end   *************************************************************************/void main()
{ifstream fin("calibdata.txt"); /* 标定所用图像文件的路径 */ofstream fout("caliberation_result.txt");  /* 保存标定结果的文件 *///读取每一幅图像,从中提取出角点,然后对角点进行亚像素精确化     cout << "开始提取角点………………\n";int image_count = 0;  /* 图像数量 */Size image_size;  /* 图像的尺寸 */Size board_size = Size(6, 9);    /* 标定板上每行、列的角点数 */vector<Point2f> image_points_buf;  /* 缓存每幅图像上检测到的角点 */vector<vector<Point2f>> image_points_seq; /* 保存检测到的所有角点 */string filename;int count = -1;//用于存储角点个数。    while (getline(fin, filename)){image_count++;// 用于观察检验输出    //cout << "image_count = " << image_count << endl;/* 输出检验*///cout << "-->count = " << count << endl;Mat imageInput = imread(filename);if (imageInput.empty()){ALG_ERR("can not open pic,图片 %s 打开失败\n",filename.c_str());cout << "can not open pic!\n";Sleep(100000);//睡眠一下,不至于窗口跳出exit(-1);}Mat imageInput_copy = imageInput.clone(); //校正后输出图片if (image_count == 1)  //读入第一张图片时获取图像宽高信息    {image_size.width = imageInput.cols;image_size.height = imageInput.rows;cout << "image_size.width = " << image_size.width << endl;cout << "image_size.height = " << image_size.height << endl;}//printf("输出结果:%s\n", filename.c_str());/* 提取角点 */if (0 == findChessboardCorners(imageInput, board_size, image_points_buf)){cout << "can not find chessboard corners!\n"; //找不到角点ALG_ERR("图片: %s 找不到角点,请检查或者更换该图片\n",filename.c_str());Sleep(100000);//睡眠一下,不至于窗口跳出exit(1);}else{ALG_PRT("正在提取图片%s的角点,image_count=%d\n", filename.c_str(), image_count);Mat view_gray;cvtColor(imageInput, view_gray, COLOR_RGB2GRAY);/* 亚像素精确化 */find4QuadCornerSubpix(view_gray, image_points_buf, Size(5, 5)); //对粗提取的角点进行精确化    //cornerSubPix(view_gray,image_points_buf,Size(5,5),Size(-1,-1),TermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,30,0.1));    image_points_seq.push_back(image_points_buf);  //保存亚像素角点    /* 在图像上显示角点位置 */drawChessboardCorners(view_gray, board_size, image_points_buf, false); //用于在图片中标记角点    namedWindow("Camera Calibration", 0);//创建窗口imshow("Camera Calibration", view_gray);//显示图片    waitKey(5);//暂停0.5S //这种初始化点的方式也可以for (int i = 0;i < image_points_buf.size(); i++){Point p2;p2.x = image_points_buf[i].x;p2.y = image_points_buf[i].y;//画实心点circle(imageInput_copy, p2, 8, Scalar(0, 0, 255), -1); //第五个参数我设为-1,表明这是个实点。}namedWindow("imageInput_copy", 0);//创建窗口imshow("imageInput_copy", imageInput_copy);//显示图片    waitKey(500);//暂停0.5S }}int total = image_points_seq.size();cout << "total = " << total << endl;int CornerNum = board_size.width*board_size.height;  //每张图片上总的角点数    for (int ii = 0; ii<total; ii++){if (0 == ii % CornerNum)// 24 是每幅图片的角点个数。此判断语句是为了输出 图片号,便于控制台观看     {int i = -1;i = ii / CornerNum;int j = i + 1;cout << "--> 第 " << j << "图片的数据 --> : " << endl;}if (0 == ii % 3)  // 此判断语句,格式化输出,便于控制台查看    {cout << endl;}else{cout.width(10);}//输出所有的角点    cout << " -->" << image_points_seq[ii][0].x;cout << " -->" << image_points_seq[ii][0].y;}cout << "角点提取完成!\n";//以下是摄像机标定    cout << "开始标定………………";/*棋盘三维信息*/Size square_size = Size(10, 10);  /* 实际测量得到的标定板上每个棋盘格的大小 */vector<vector<Point3f>> object_points; /* 保存标定板上角点的三维坐标 *//*内外参数*/Mat cameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /* 摄像机内参数矩阵 */vector<int> point_counts;  // 每幅图像中角点的数量    Mat distCoeffs = Mat(1, 5, CV_32FC1, Scalar::all(0)); /* 摄像机的5个畸变系数:k1,k2,p1,p2,k3 */vector<Mat> tvecsMat;  /* 每幅图像的旋转向量 */vector<Mat> rvecsMat; /* 每幅图像的平移向量 *//* 初始化标定板上角点的三维坐标 */int i, j, t;for (t = 0; t<image_count; t++){vector<Point3f> tempPointSet;for (i = 0; i<board_size.height; i++){for (j = 0; j<board_size.width; j++){Point3f realPoint;/* 假设标定板放在世界坐标系中z=0的平面上 */realPoint.x = i * square_size.width;realPoint.y = j * square_size.height;realPoint.z = 0;tempPointSet.push_back(realPoint);}}object_points.push_back(tempPointSet);}/* 初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板 */for (i = 0; i<image_count; i++){point_counts.push_back(board_size.width*board_size.height);}/* 开始标定 */calibrateCamera(object_points, image_points_seq, image_size, cameraMatrix, distCoeffs, rvecsMat, tvecsMat, 0);cout << "标定完成!\n";//对标定结果进行评价    cout << "开始评价标定结果………………\n";double total_err = 0.0; /* 所有图像的平均误差的总和 */double err = 0.0; /* 每幅图像的平均误差 */vector<Point2f> image_points2; /* 保存重新计算得到的投影点 */cout << "\t每幅图像的标定误差:\n";fout << "每幅图像的标定误差:\n";for (i = 0; i<image_count; i++){vector<Point3f> tempPointSet = object_points[i];/* 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 */projectPoints(tempPointSet, rvecsMat[i], tvecsMat[i], cameraMatrix, distCoeffs, image_points2);/* 计算新的投影点和旧的投影点之间的误差*/vector<Point2f> tempImagePoint = image_points_seq[i];Mat tempImagePointMat = Mat(1, tempImagePoint.size(), CV_32FC2);Mat image_points2Mat = Mat(1, image_points2.size(), CV_32FC2);for (int j = 0; j < tempImagePoint.size(); j++){image_points2Mat.at<Vec2f>(0, j) = Vec2f(image_points2[j].x, image_points2[j].y);tempImagePointMat.at<Vec2f>(0, j) = Vec2f(tempImagePoint[j].x, tempImagePoint[j].y);}err = norm(image_points2Mat, tempImagePointMat, NORM_L2);total_err += err /= point_counts[i];std::cout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;}std::cout << "总体平均误差:" << total_err / image_count << "像素" << endl;fout << "总体平均误差:" << total_err / image_count << "像素" << endl << endl;std::cout << "评价完成!" << endl;//保存定标结果        std::cout << "开始保存定标结果………………" << endl;Mat rotation_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /* 保存每幅图像的旋转矩阵 */fout << "相机内参数矩阵:" << endl;fout << cameraMatrix << endl << endl;fout << "畸变系数:\n";fout << distCoeffs << endl << endl << endl;for (int i = 0; i<image_count; i++){fout << "第" << i + 1 << "幅图像的旋转向量:" << endl;fout << tvecsMat[i] << endl;/* 将旋转向量转换为相对应的旋转矩阵 */Rodrigues(tvecsMat[i], rotation_matrix);fout << "第" << i + 1 << "幅图像的旋转矩阵:" << endl;fout << rotation_matrix << endl;fout << "第" << i + 1 << "幅图像的平移向量:" << endl;fout << rvecsMat[i] << endl << endl;}std::cout << "完成保存" << endl;fout << endl;/************************************************************************显示定标结果*************************************************************************/Mat mapx = Mat(image_size, CV_32FC1);Mat mapy = Mat(image_size, CV_32FC1);Mat R = Mat::eye(3, 3, CV_32F);std::cout << "保存矫正图像" << endl;string imageFileName;std::stringstream StrStm;for (int i = 0; i != image_count; i++){std::cout << "Frame #" << i + 1 << "..." << endl;initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, image_size, CV_32FC1, mapx, mapy);StrStm.clear();imageFileName.clear();string filePath = "./caliberation";StrStm << i + 1;StrStm >> imageFileName;filePath += imageFileName;filePath += ".jpg";Mat imageSource = imread("./caliberation/01.jpg"); //读取畸变图片//Mat imageSource = imread(filePath); //读取畸变图片Mat newimage = imageSource.clone(); //校正后输出图片//另一种不需要转换矩阵的方式    //   undistort(imageSource,newimage,cameraMatrix,distCoeffs);    remap(imageSource, newimage, mapx, mapy, INTER_LINEAR);StrStm.clear();filePath.clear();StrStm << i + 1;StrStm >> imageFileName;imageFileName += "_d.jpg";imwrite(imageFileName, newimage);}std::cout << "保存结束" << endl;return;
}

运行效果图

判断图像采集的是否合规

/*****************************************************
2021.6.8:2021.4.29:畸变校正标定过程,用于获取畸变矫正的几个参数根据不同的标定板需要修改;
Size board_size = Size(4, 6);    /* 标定板上每行、列的角点数
******************************************************/#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 <iostream>#include <stdlib.h> //srand()和rand()函数
#include<windows.h>using namespace cv;
using namespace std;/****************        打印相关组件 start !  ********************************************************************/#define  debug_show_picture        1 //是否显示部分调试图片,方便调试
#define  debug_save_shipin       0 //是否保存结果视频/*打印等级,修改后面的宏定义可以改变函数输出打印等级*/
#define ALG_PRTINT_LEVER PRINT_LEVEL_UNLIMIT#define ALG_PRTINT(...)  SAL_printf(__VA_ARGS__)
#define ALG_PRT(...)     ALG_PRTINT(__FUNCTION__, __LINE__, PRINT_LEVEL_UNLIMIT, __VA_ARGS__)
#define ALG_DBG(...)     ALG_PRTINT(__FUNCTION__, __LINE__, PRINT_LEVEL_DBG,     __VA_ARGS__)
#define ALG_WAR(...)     ALG_PRTINT(__FUNCTION__, __LINE__, PRINT_LEVEL_WRN,     __VA_ARGS__)
#define ALG_ERR(...)     ALG_PRTINT(__FUNCTION__, __LINE__, PRINT_LEVEL_ERR,     __VA_ARGS__)/***********************************************************************************************
* @enum     HAT_SAL_PRT_LEVEL_E
* @brief    打印输出的等级
***************************************************************************************************/
typedef enum _PRT_LEVEL_E_
{PRINT_LEVEL_ERR = 0,        /*错误打印信息*/PRINT_LEVEL_WRN = 1,        /*警告打印信息*/PRINT_LEVEL_DBG = 2,         /*调试打印信息*/PRINT_LEVEL_UNLIMIT = 3,    /*无限制打印信息*/PRINT_LEVEL_NOPRT = 4,      /*没有打印信息*/
} PRT_LEVEL_E;/*******************************************************************************
Function:   SAL_printf
Description: 该函数能够通过设置的打印等级ALG_PRTINT_LEVER,来控制是否输出相关语句
Input:
Output:
Return:
0:          Successful
ohters:     Failed
*******************************************************************************/
void SAL_printf(const char *pFun, UINT line, PRT_LEVEL_E levelParam, const char *fmt, ...)
{static INT8 g_printfInfo[4][16] = { "ERR", "WAR", "DBG", "INF" };va_list p;if (ALG_PRTINT_LEVER == PRINT_LEVEL_NOPRT || levelParam == PRINT_LEVEL_NOPRT){return;}if (levelParam <= ALG_PRTINT_LEVER){va_start(p, fmt);printf("[ALG][%s][%s][%4d] ", g_printfInfo[levelParam], pFun, line);vprintf(fmt, p);va_end(p);}
}
/******************************  打印相关组件 end   *************************************************************************/void main()
{ifstream fin("calibdata.txt"); /* 标定所用图像文件的路径 */ofstream fout("caliberation_result.txt");  /* 保存标定结果的文件 *///读取每一幅图像,从中提取出角点,然后对角点进行亚像素精确化     cout << "开始提取角点………………\n";int image_count = 0;  /* 图像数量 */Size image_size;  /* 图像的尺寸 */Size board_size = Size(6, 9);    /* 标定板上每行、列的角点数 */vector<Point2f> image_points_buf;  /* 缓存每幅图像上检测到的角点 */vector<vector<Point2f>> image_points_seq; /* 保存检测到的所有角点 */string filename;int count = -1;//用于存储角点个数。    while (getline(fin, filename)){image_count++;// 用于观察检验输出    //cout << "image_count = " << image_count << endl;/* 输出检验*///cout << "-->count = " << count << endl;Mat imageInput = imread(filename);if (imageInput.empty()){ALG_ERR("can not open pic,图片 %s 打开失败\n", filename.c_str());cout << "can not open pic!\n";Sleep(100000);//睡眠一下,不至于窗口跳出exit(-1);}Mat imageInput_copy = imageInput.clone(); //校正后输出图片if (image_count == 1)  //读入第一张图片时获取图像宽高信息    {image_size.width = imageInput.cols;image_size.height = imageInput.rows;cout << "image_size.width = " << image_size.width << endl;cout << "image_size.height = " << image_size.height << endl;}//printf("输出结果:%s\n", filename.c_str());/* 提取角点 */if (0 == findChessboardCorners(imageInput, board_size, image_points_buf)){cout << "can not find chessboard corners!\n"; //找不到角点ALG_ERR("图片: %s 找不到角点,请检查或者更换该图片\n", filename.c_str());continue;}else{ALG_PRT("正在提取图片%s的角点,image_count=%d\n", filename.c_str(), image_count);Mat view_gray;cvtColor(imageInput, view_gray, COLOR_RGB2GRAY);/* 亚像素精确化 */find4QuadCornerSubpix(view_gray, image_points_buf, Size(5, 5)); //对粗提取的角点进行精确化    //cornerSubPix(view_gray,image_points_buf,Size(5,5),Size(-1,-1),TermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,30,0.1));    image_points_seq.push_back(image_points_buf);  //保存亚像素角点    /* 在图像上显示角点位置 */drawChessboardCorners(view_gray, board_size, image_points_buf, false); //用于在图片中标记角点    namedWindow("Camera Calibration", 0);//创建窗口imshow("Camera Calibration", view_gray);//显示图片    waitKey(5);//暂停0.5S //这种初始化点的方式也可以for (int i = 0; i < image_points_buf.size(); i++){Point p2;p2.x = image_points_buf[i].x;p2.y = image_points_buf[i].y;//画实心点circle(imageInput_copy, p2, 8, Scalar(0, 0, 255), -1); //第五个参数我设为-1,表明这是个实点。}namedWindow("imageInput_copy", 0);//创建窗口imshow("imageInput_copy", imageInput_copy);//显示图片    waitKey(500);//暂停0.5S }}waitKey(5000000000000);//暂停0.5S
}

标定图像采集小技巧——使用代码按键采集

参考我的另外一篇文章:
opencv通过按键采集图片源码

相机畸变校正、求出参数、具体应用相关推荐

  1. matlab相机畸变校正csdn,android广角相机畸变校正算法和实现示例

    1.光学相机镜头一般都存在畸变的问题,畸变属于成像的几何失真,它是由于焦平面上不同区域对影像的放大率不同而形成的画面扭曲变形现象.除了一些特定的场合,大部分情况下,这些失真都是需要校正到正常人眼不产生 ...

  2. 使用直线标定板进行相机畸变校正,并且进行9点标定(halcon)

    直线标定板图片: 机械吸头位置图片: 处理代码 read_image (Image, '直线标定板图片/Left201118140641772.bmp') get_image_size (Image, ...

  3. 单双目相机畸变校正--极线校正

    单目:https://blog.csdn.net/zkl99999/article/details/48371877 (注意:opencv 中前五个畸变参数顺序是 [k1, k2, p1, p2, k ...

  4. 单目相机 双目相机 OpenCV 标定求内外参数 笔记留着需要的时候用

     http://blog.csdn.net/bcj296050240/article/details/52778741

  5. 鱼眼摄像头标定与畸变校正(OPENCV版)

    转载请注明作者和出处:http://blog.csdn.net/u011475210 代码地址:https://github.com/WordZzzz/fisheye_calibration 软件版本 ...

  6. 鱼眼摄像头标定与畸变校正(双OPENCV版本)

    转载请注明作者和出处:http://blog.csdn.net/u011475210 代码地址:https://github.com/WordZzzz/fisheye_calibration 软件版本 ...

  7. 南方h5手簿求转换参数_工程之星、 gps操作、RTK求转换参数操作步骤

    二.移动站 移动站开机后,接收到基准站电台信号,STA灯和DL灯以发射间隔均匀闪烁. 工程之星操作步骤: 1. 打开工程之星,路径:我的电脑→FLASH DISK→STEUP→工程之星3.0(根据应用 ...

  8. 【计算机视觉】摄像机标定与畸变校正

    任务1:利用OpenCV实现摄像机参数标定 设置好棋盘格参数(边长.行列数等),打印方形棋盘格并粘贴,拍摄多张标定图像: 估计标定参数,可视化标定结果.        任务2:根据标定参数进行畸变校正 ...

  9. 基于OpenCV的鱼眼相机畸变矫正(含代码)

    首先展示一下实现的效果: 校正前: 校正后: 本文分为两部分,一部分是介绍鱼眼相机畸变校正的原理,一部分是手撕OpenCV相机矫正代码. 文章主要结构如下图所示: 一.介绍鱼眼相机的原理 1.什么是鱼 ...

最新文章

  1. 算法实践1_线性回归
  2. Windows PE第6章 栈与重定位表
  3. python对XML的解析
  4. python post请求参数为list_浅谈python3发送post请求参数为空的情况
  5. 1320A. Journey Planning
  6. 2017模拟赛:还款计算
  7. asterisk php agi,asterisk AMI入门
  8. WireShark下载:官网、源码
  9. js html table转json 反向生成数据源
  10. Linq分页(skip和Take)
  11. 萤石视频监控模式的参数decoderPath配置问题
  12. Sorry ,中产 -20160929
  13. 深入浅出Mybatis-改造Cache
  14. 无土栽培远程监空技术方案
  15. Excel 设置下拉框-显示中文而实际数字
  16. 噼里啪智能·财税产品技术 VP 张芳:DI 和 AI 双引擎为智能财税保驾护航
  17. Qemu虚拟化PowerPc
  18. 信息安全与网络安全有什么区别?
  19. Emgu CV4图像处理之轮廓查找与绘制15(C#)
  20. DAO与DAL的区别

热门文章

  1. windows上的LLVM pass瞎折腾记录
  2. 莫兰指数是怎么分析的???
  3. 动态规划9:马尔可夫决策过程
  4. pandas计算皮尔逊相关系数
  5. 下拉列表的两种实现方式
  6. css中盒子模型图片,CSS 盒子模型
  7. 关于C语言中.h和.c文件
  8. linux查看漏洞修复情况,linux 漏洞扫描补丁修复
  9. 公网远程ERP - 在外远程登录公司局域网金蝶云ERP管理系统
  10. EFI Network…