赞
踩
若该文为原创文章,转载请注明原文出处
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/136616551
各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究
红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…(点击传送门)
上一篇:《OpenCV开发笔记(七十六):相机标定(一):识别棋盘并绘制角点》
下一篇:持续补充中…
通过相机图片可以识别出棋盘角点了,这时候我们需要通过角点去计算相机内参矩阵,通过上篇得知畸变的原理,所以我们尽可能要全方位都能获取标定图片,全方位意思是提供的多张图综合起来基本覆盖了相机所有的像素,同时还要注意远近和斜着
本篇通过一张图片来识别计算得到相机内参矩阵,并矫正相机畸形。
做项目一定要多张且基本覆盖相机所有区域,要保证每一张截取的图片也要被识别,可以做成个软件,识别出棋盘都在一个预先指定的区域内则截图,然后下一个区域,实现半自动半人工化标定。
这里只用了一张图校准,所以可能内参矩阵经度不那么高:
注意:这里demo只使用了可识别的一张图作为计算,可能没覆盖的区域则出现不可预期的图像问题。
这里是直接填充行列的坐标,第三个是z坐标直接设置为0,为视口处:
// 步骤八:角点对应的三维坐标(一张图一组)
std::vector<std::vector<cv::Point3f>> vectorObjectPoint;
std::vector<cv::Point3f> objectPoints; // 三维世界坐标系
for(int i = 0; i < chessboardRowCornerCount; i++)
{
for(int j = 0; j < chessboardColCornerCount; j++)
{
objectPoints.push_back(cv::Point3f(j, i, 0));
}
}
vectorObjectPoint.push_back(objectPoints);
多张图放入多次,这里只有一张图:
// 步骤九:图像识别出来的角点(一张图一组)
std::vector<std::vector<cv::Point2f>> vectorImagePoint;
vectorImagePoint.push_back(vectorPoint2fCorners);
输出的参数有点多,输入的参数却不多:
// 步骤十:计算内参和畸变系数 cv::Mat cameraMatrix; // 相机矩阵(接收输出) cv::Mat distCoeffs; // 畸变系数(接收输出) cv::Mat Rotate; // 旋转量(接收输出) cv::Mat Translate; // 偏移量(接收输出) cv::calibrateCamera(vectorObjectPoint, vectorImagePoint, grayMat.size(), cameraMatrix, distCoeffs, Rotate, Translate); std::cout << "cameraMatrix:" << std::endl; std::cout << cameraMatrix << std::endl; std::cout << "distCoeffs:" << std::endl; std::cout << distCoeffs << std::endl; std::cout << "Rotate:" << std::endl; std::cout << Rotate << std::endl; std::cout << "Translate:" << std::endl; std::cout << Translate << std::endl;
这里校准相对容易,所以难点在于标定校准,做项目肯定要自己写一个标定软件了,每次这么手动查看校准肯定不行的。
// 步骤十一:畸变图像校准
cv::Mat dstMat;
cv::undistort(srcMat, dstMat, cameraMatrix, distCoeffs);
cv::imshow("6", dstMat);
OpenCV中的一个函数,用于相机标定。相机标定是估计相机内参(如焦距、主点坐标等)和畸变系数的过程,这些参数对于后续的图像处理任务(如三维重建、目标跟踪等)至关重要。
double calibrateCamera(InputArrayOfArrays objectPoints,
InputArrayOfArrays imagePoints,
Size imageSize,
OutputArray cameraMatrix,
OutputArray distCoeffs,
OutputArray rvecs,
OutputArray tvecs,
int flags=0,
TermCriteria criteria=TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 1e-6));
参数说明:
这个函数返回一个双精度浮点数,表示重投影误差的估计值,即实际图像点与通过相机参数和畸变系数计算出的图像点之间的平均误差。
为了获得准确的相机标定结果,通常需要多个视图(即多张不同角度和姿态拍摄的标定板图像),**并确保标定板在不同图像中占据足够的视场。**此外,图像应该清晰,且标定板上的特征点(如棋盘格的角点)应准确检测。
OpenCV中用于初始化用于图像去畸变和校正的映射表的函数。这个函数的目的是生成两个映射,一个用于x坐标,另一个用于y坐标,它们可以被用于 remap函数来校正图像的畸变。
void initUndistortRectifyMap(InputArray cameraMatrix,
InputArray distCoeffs,
InputArray R,
InputArray newCameraMatrix,
Size size,
int m1type,
OutputArray map1,
OutputArray map2)
参数说明
这两个映射map1和map2可以被传递给remap函数,以对图像进行去畸变和校正。
如果有一个畸变的图像distortedImage和想要得到校正后的图像undistortedImage,可以这样使用这两个函数:
Mat map1,map2;
initUndistortRectifyMap(cameraMatrix, distCoeffs, R, newCameraMatrix, size, CV_32FC1, map1, map2);
remap(distortedImage, undistortedImage, map1, map2, INTER_LINEAR);
在这个例子中,INTER_LINEAR是插值方法的类型,用于remap函数。其他的插值方法,如INTER_NEAREST、INTER_CUBIC等也可以被使用,具体取决于应用需求。
void OpenCVManager::testCorrectingChessboard() { #define TestCorrectingChessboardUseCamera 0 #if !TestCorrectingChessboardUseCamera // 使用图片 // std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/chessboard.png"; // std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/24.jpg"; // std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/27.png"; // std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/28.png"; std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/28.jpg"; cv::Mat srcMat = cv::imread(srcFilePath); #else // 使用摄像头 cv::VideoCapture capture; // 插入USB摄像头默认为0 if(!capture.open(0)) { qDebug() << __FILE__ << __LINE__ << "Failed to open camera: 0"; }else{ qDebug() << __FILE__ << __LINE__ << "Succeed to open camera: 0"; } while(true) { cv::Mat srcMat; capture >> srcMat; #endif int chessboardColCornerCount = 6; int chessboardRowCornerCount = 9; // int chessboardColCornerCount = 7; // int chessboardRowCornerCount = 7; // 步骤一:读取文件 // cv::imshow("1", srcMat); // cv::waitKey(0); // 步骤二:缩放,太大了缩放下(可省略) cv::resize(srcMat, srcMat, cv::Size(srcMat.cols / 2, srcMat.rows / 2)); cv::Mat srcMat2 = srcMat.clone(); cv::Mat srcMat3 = srcMat.clone(); cv::imshow("2", srcMat); // cv::waitKey(0); // 步骤三:灰度化 cv::Mat grayMat; cv::cvtColor(srcMat, grayMat, cv::COLOR_BGR2GRAY); cv::imshow("3", grayMat); // cv::waitKey(0); // 步骤四:检测角点 std::vector<cv::Point2f> vectorPoint2fCorners; bool patternWasFound = false; patternWasFound = cv::findChessboardCorners(grayMat, cv::Size(chessboardColCornerCount, chessboardRowCornerCount), vectorPoint2fCorners, cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_FAST_CHECK | cv::CALIB_CB_NORMALIZE_IMAGE); /* enum { CALIB_CB_ADAPTIVE_THRESH = 1, // 使用自适应阈值将图像转化成二值图像 CALIB_CB_NORMALIZE_IMAGE = 2, // 归一化图像灰度系数(用直方图均衡化或者自适应阈值) CALIB_CB_FILTER_QUADS = 4, // 在轮廓提取阶段,使用附加条件排除错误的假设 CALIB_CB_FAST_CHECK = 8 // 快速检测 }; */ cvui::printf(srcMat, 0, 0, 1.0, 0xFF0000, "found = %s", patternWasFound ? "true" : "false"); cvui::printf(srcMat, 0, 24, 1.0, 0xFF0000, "count = %d", vectorPoint2fCorners.size()); qDebug() << __FILE__ << __LINE__ << vectorPoint2fCorners.size(); // 步骤五:绘制棋盘点 cv::drawChessboardCorners(srcMat2, cv::Size(chessboardColCornerCount, chessboardRowCornerCount), vectorPoint2fCorners, patternWasFound); #if TestCorrectingChessboardUseCamera cv::imshow("0", srcMat); cv::imshow("4", srcMat2); if(!patternWasFound) { cv::imshow("5", srcMat3); cv::waitKey(1); continue; } #endif // 步骤六:进一步提取亚像素角点 cv::TermCriteria criteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, // 类型 30, // 参数二: 最大次数 0.001); // 参数三:迭代终止阈值 /* #define CV_TERMCRIT_ITER 1 // 终止条件为: 达到最大迭代次数终止 #define CV_TERMCRIT_NUMBER CV_TERMCRIT_ITER // #define CV_TERMCRIT_EPS 2 // 终止条件为: 迭代到阈值终止 */ qDebug() << __FILE__ << __LINE__ << vectorPoint2fCorners.size(); cv::cornerSubPix(grayMat, vectorPoint2fCorners, cv::Size(5, 5), cv::Size(-1, -1), criteria); // 步骤七:绘制棋盘点 cv::drawChessboardCorners(srcMat3, cv::Size(chessboardColCornerCount, chessboardRowCornerCount), vectorPoint2fCorners, patternWasFound); cv::imshow("5", srcMat3); // cv::waitKey(0); // 步骤八:角点对应的三维坐标(一张图一组) std::vector<std::vector<cv::Point3f>> vectorObjectPoint; std::vector<cv::Point3f> objectPoints; // 三维世界坐标系 for(int i = 0; i < chessboardRowCornerCount; i++) { for(int j = 0; j < chessboardColCornerCount; j++) { objectPoints.push_back(cv::Point3f(j, i, 0)); } } vectorObjectPoint.push_back(objectPoints); // 步骤九:图像识别出来的角点(一张图一组) std::vector<std::vector<cv::Point2f>> vectorImagePoint; vectorImagePoint.push_back(vectorPoint2fCorners); // 步骤十:计算内参和畸变系数 cv::Mat cameraMatrix; // 相机矩阵(接收输出) cv::Mat distCoeffs; // 畸变系数(接收输出) cv::Mat Rotate; // 旋转量(接收输出) cv::Mat Translate; // 偏移量(接收输出) cv::calibrateCamera(vectorObjectPoint, vectorImagePoint, grayMat.size(), cameraMatrix, distCoeffs, Rotate, Translate); std::cout << "cameraMatrix:" << std::endl; std::cout << cameraMatrix << std::endl; std::cout << "distCoeffs:" << std::endl; std::cout << distCoeffs << std::endl; std::cout << "Rotate:" << std::endl; std::cout << Rotate << std::endl; std::cout << "Translate:" << std::endl; std::cout << Translate << std::endl; // 步骤十一:畸变图像校准 cv::Mat dstMat; cv::undistort(srcMat, dstMat, cameraMatrix, distCoeffs); cv::imshow("6", dstMat); #if TestCorrectingChessboardUseCamera cv::waitKey(1); } // cv::imshow(_windowTitle.toStdString(), dstMat); #else cv::waitKey(0); #endif }
无法识别。
要全部棋盘视野内,且可以识别,这个确实识别不了。
换图重新来过(这是笔者随便找的图)。
四角明显不对。
这里需要多张图在能识别的情况下覆盖所有区域。
先这样,下次实际标定的时候再多张图看是否还存在该问题。
上一篇:《OpenCV开发笔记(七十六):相机标定(一):识别棋盘并绘制角点》
下一篇:持续补充中…
若该文为原创文章,转载请注明原文出处
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/136616551
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。