若該文為原創(chuàng)文章,轉(zhuǎn)載請注明原文出處
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/136616551
各位讀者,知識(shí)無窮而人力有窮,要么改需求,要么找專業(yè)人士,要么自己研究
紅胖子(紅模仿)的博文大全:開發(fā)技術(shù)集合(包含Qt實(shí)用技術(shù)、樹莓派、三維、OpenCV、OpenGL、ffmpeg、OSG、單片機(jī)、軟硬結(jié)合等等)持續(xù)更新中…(點(diǎn)擊傳送門)
OpenCV開發(fā)專欄(點(diǎn)擊傳送門)
上一篇:《OpenCV開發(fā)筆記(七十六):相機(jī)標(biāo)定(一):識(shí)別棋盤并繪制角點(diǎn)》
下一篇:持續(xù)補(bǔ)充中…文章來源:http://www.zghlxwxcb.cn/news/detail-839238.html
前言
??通過相機(jī)圖片可以識(shí)別出棋盤角點(diǎn)了,這時(shí)候我們需要通過角點(diǎn)去計(jì)算相機(jī)內(nèi)參矩陣,通過上篇得知畸變的原理,所以我們盡可能要全方位都能獲取標(biāo)定圖片,全方位意思是提供的多張圖綜合起來基本覆蓋了相機(jī)所有的像素,同時(shí)還要注意遠(yuǎn)近和斜著
??本篇通過一張圖片來識(shí)別計(jì)算得到相機(jī)內(nèi)參矩陣,并矯正相機(jī)畸形。
補(bǔ)充
??做項(xiàng)目一定要多張且基本覆蓋相機(jī)所有區(qū)域,要保證每一張截取的圖片也要被識(shí)別,可以做成個(gè)軟件,識(shí)別出棋盤都在一個(gè)預(yù)先指定的區(qū)域內(nèi)則截圖,然后下一個(gè)區(qū)域,實(shí)現(xiàn)半自動(dòng)半人工化標(biāo)定。
Demo
??
??這里只用了一張圖校準(zhǔn),所以可能內(nèi)參矩陣經(jīng)度不那么高:
??
一張圖校準(zhǔn)的實(shí)例
?? 注意:這里demo只使用了可識(shí)別的一張圖作為計(jì)算,可能沒覆蓋的區(qū)域則出現(xiàn)不可預(yù)期的圖像問題。
步驟一:世界坐標(biāo)系初始化
?? 這里是直接填充行列的坐標(biāo),第三個(gè)是z坐標(biāo)直接設(shè)置為0,為視口處:
// 步驟八:角點(diǎn)對應(yīng)的三維坐標(biāo)(一張圖一組)
std::vector<std::vector<cv::Point3f>> vectorObjectPoint;
std::vector<cv::Point3f> objectPoints; // 三維世界坐標(biāo)系
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);
步驟二:識(shí)別的角點(diǎn)放入列表
?? 多張圖放入多次,這里只有一張圖:
// 步驟九:圖像識(shí)別出來的角點(diǎn)(一張圖一組)
std::vector<std::vector<cv::Point2f>> vectorImagePoint;
vectorImagePoint.push_back(vectorPoint2fCorners);
步驟三:計(jì)算內(nèi)參和畸變系數(shù)
?? 輸出的參數(shù)有點(diǎn)多,輸入的參數(shù)卻不多:
// 步驟十:計(jì)算內(nèi)參和畸變系數(shù)
cv::Mat cameraMatrix; // 相機(jī)矩陣(接收輸出)
cv::Mat distCoeffs; // 畸變系數(shù)(接收輸出)
cv::Mat Rotate; // 旋轉(zhuǎn)量(接收輸出)
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;
步驟四:畸變函數(shù)校準(zhǔn)
?? 這里校準(zhǔn)相對容易,所以難點(diǎn)在于標(biāo)定校準(zhǔn),做項(xiàng)目肯定要自己寫一個(gè)標(biāo)定軟件了,每次這么手動(dòng)查看校準(zhǔn)肯定不行的。
// 步驟十一:畸變圖像校準(zhǔn)
cv::Mat dstMat;
cv::undistort(srcMat, dstMat, cameraMatrix, distCoeffs);
cv::imshow("6", dstMat);
函數(shù)原型
calibrateCamera:相機(jī)標(biāo)定求解函數(shù)
?? OpenCV中的一個(gè)函數(shù),用于相機(jī)標(biāo)定。相機(jī)標(biāo)定是估計(jì)相機(jī)內(nèi)參(如焦距、主點(diǎn)坐標(biāo)等)和畸變系數(shù)的過程,這些參數(shù)對于后續(xù)的圖像處理任務(wù)(如三維重建、目標(biāo)跟蹤等)至關(guān)重要。
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));
?? 參數(shù)說明:
- objectPoints:世界坐標(biāo)系中的三維點(diǎn)。通常,這些點(diǎn)是通過在標(biāo)定板上定義的一系列點(diǎn)來獲取的,這些點(diǎn)的坐標(biāo)是已知的。對于每個(gè)圖像,它應(yīng)該是一個(gè) Nx3 的數(shù)組(或數(shù)組列表),其中 N 是點(diǎn)的數(shù)量,而 3 表示每個(gè)點(diǎn)的 (X, Y, Z) 坐標(biāo)。
- imagePoints:圖像坐標(biāo)系中的二維點(diǎn),即對應(yīng)于 objectPoints 中的三維點(diǎn)在圖像中的投影。對于每個(gè)圖像,它應(yīng)該是一個(gè) Nx2 的數(shù)組(或數(shù)組列表),其中 N 是點(diǎn)的數(shù)量,而 2 表示每個(gè)點(diǎn)的 (x, y) 坐標(biāo)。
- imageSize:圖像的大小,表示為 Size 類型的對象,包含圖像的寬度和高度。
- cameraMatrix:輸出參數(shù),存儲(chǔ) 3x3 的相機(jī)內(nèi)參矩陣。
- distCoeffs:輸出參數(shù),存儲(chǔ)畸變系數(shù)。通常有 5 個(gè)系數(shù)(k1, k2, p1, p2, k3)對于徑向和切向畸變,或 8 個(gè)系數(shù)(k1, k2, k3, k4, k5, k6, p1, p2)對于魚眼相機(jī)模型。
- rvecs:輸出參數(shù),對于每個(gè)圖像,存儲(chǔ)旋轉(zhuǎn)向量的數(shù)組。
- tvecs:輸出參數(shù),對于每個(gè)圖像,存儲(chǔ)平移向量的數(shù)組。
-
flags:不同標(biāo)志的組合,用于指定標(biāo)定過程中使用的算法。
CV_CALIB_USE_INTRINSIC_GUESS:使用該參數(shù)時(shí),將包含有效的fx,fy,cx,cy的估計(jì)值的內(nèi)參矩陣cameraMatrix,作為初始值輸入,然后函數(shù)對其做進(jìn)一步優(yōu)化。如果不使用這個(gè)參數(shù),用圖像的中心點(diǎn)初始化光軸點(diǎn)坐標(biāo)(cx, cy),使用最小二乘估算出fx,fy(這種求法好像和張正友的論文不一樣,不知道為何要這樣處理)。注意,如果已知內(nèi)部參數(shù)(內(nèi)參矩陣和畸變系數(shù)),就不需要使用這個(gè)函數(shù)來估計(jì)外參,可以使用solvepnp()函數(shù)計(jì)算外參數(shù)矩陣。
CV_CALIB_FIX_PRINCIPAL_POINT:在進(jìn)行優(yōu)化時(shí)會(huì)固定光軸點(diǎn),光軸點(diǎn)將保持為圖像的中心點(diǎn)。當(dāng)CV_CALIB_USE_INTRINSIC_GUESS參數(shù)被設(shè)置,保持為輸入的值。
CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只將fy作為可變量,進(jìn)行優(yōu)化計(jì)算。當(dāng) CV_CALIB_USE_INTRINSIC_GUESS沒有被設(shè)置,fx和fy的實(shí)際輸入值將會(huì)被忽略,只有fx/fy的比值被計(jì)算和使用。
CV_CALIB_ZERO_TANGENT_DIST:切向畸變系數(shù)(P1,P2)被設(shè)置為零并保持為零。
CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6:對應(yīng)的徑向畸變系數(shù)在優(yōu)化中保持不變。如果設(shè)置了CV_CALIB_USE_INTRINSIC_GUESS參數(shù),就從提供的畸變系數(shù)矩陣中得到。否則,設(shè)置為0。
CV_CALIB_RATIONAL_MODEL(理想模型):啟用畸變k4,k5,k6三個(gè)畸變參數(shù)。使標(biāo)定函數(shù)使用有理模型,返回8個(gè)系數(shù)。如果沒有設(shè)置,則只計(jì)算其它5個(gè)畸變參數(shù)。
CALIB_THIN_PRISM_MODEL (薄棱鏡畸變模型):啟用畸變系數(shù)S1、S2、S3和S4。使標(biāo)定函數(shù)使用薄棱柱模型并返回12個(gè)系數(shù)。如果不設(shè)置標(biāo)志,則函數(shù)計(jì)算并返回只有5個(gè)失真系數(shù)。
CALIB_FIX_S1_S2_S3_S4 :優(yōu)化過程中不改變薄棱鏡畸變系數(shù)S1、S2、S3、S4。如果cv_calib_use_intrinsic_guess設(shè)置,使用提供的畸變系數(shù)矩陣中的值。否則,設(shè)置為0。
CALIB_TILTED_MODEL (傾斜模型):啟用畸變系數(shù)tauX and tauY。標(biāo)定函數(shù)使用傾斜傳感器模型并返回14個(gè)系數(shù)。如果不設(shè)置標(biāo)志,則函數(shù)計(jì)算并返回只有5個(gè)失真系數(shù)。
CALIB_FIX_TAUX_TAUY :在優(yōu)化過程中,傾斜傳感器模型的系數(shù)不被改變。如果cv_calib_use_intrinsic_guess設(shè)置,從提供的畸變系數(shù)矩陣中得到。否則,設(shè)置為0。 - criteria:迭代優(yōu)化的終止條件。通常包含最大迭代次數(shù)和收斂的精度。
?? 這個(gè)函數(shù)返回一個(gè)雙精度浮點(diǎn)數(shù),表示重投影誤差的估計(jì)值,即實(shí)際圖像點(diǎn)與通過相機(jī)參數(shù)和畸變系數(shù)計(jì)算出的圖像點(diǎn)之間的平均誤差。
?? 為了獲得準(zhǔn)確的相機(jī)標(biāo)定結(jié)果,通常需要多個(gè)視圖(即多張不同角度和姿態(tài)拍攝的標(biāo)定板圖像),**并確保標(biāo)定板在不同圖像中占據(jù)足夠的視場。**此外,圖像應(yīng)該清晰,且標(biāo)定板上的特征點(diǎn)(如棋盤格的角點(diǎn))應(yīng)準(zhǔn)確檢測。
initUndistortRectifyMap:計(jì)算畸變參數(shù)
?? OpenCV中用于初始化用于圖像去畸變和校正的映射表的函數(shù)。這個(gè)函數(shù)的目的是生成兩個(gè)映射,一個(gè)用于x坐標(biāo),另一個(gè)用于y坐標(biāo),它們可以被用于 remap函數(shù)來校正圖像的畸變。
void initUndistortRectifyMap(InputArray cameraMatrix,
InputArray distCoeffs,
InputArray R,
InputArray newCameraMatrix,
Size size,
int m1type,
OutputArray map1,
OutputArray map2)
?? 參數(shù)說明
- cameraMatrix:相機(jī)的內(nèi)參矩陣,一個(gè)3x3的浮點(diǎn)數(shù)矩陣。
- distCoeffs:畸變系數(shù),一個(gè)1x5或1x8的向量,包含徑向和切向畸變系數(shù)。
- R:可選的旋轉(zhuǎn)矩陣,一個(gè)3x3的浮點(diǎn)數(shù)矩陣,表示從原相機(jī)坐標(biāo)系到新的相機(jī)坐標(biāo)系的旋轉(zhuǎn)。如果這個(gè)參數(shù)是空的,那么newCameraMatrix必須是cameraMatrix。
- newCameraMatrix:新的相機(jī)內(nèi)參矩陣,一個(gè)3x3的浮點(diǎn)數(shù)矩陣。這個(gè)矩陣可以是原始相機(jī)矩陣,或者經(jīng)過getOptimalNewCameraMatrix調(diào)整后的矩陣,以考慮圖像的有效視場。
- size:輸出映射的尺寸,表示為Size類型的對象,包含圖像的寬度和高度。
- m1type:輸出映射的類型,可以是CV_32FC1或CV_16SC2。
- map1:輸出的第一個(gè)映射,用于x坐標(biāo),可以被傳遞給remap函數(shù)。
- map2:輸出的第二個(gè)映射,用于y坐標(biāo),可以被傳遞給remap函數(shù)。
?? 這兩個(gè)映射map1和map2可以被傳遞給remap函數(shù),以對圖像進(jìn)行去畸變和校正。
?? 如果有一個(gè)畸變的圖像distortedImage和想要得到校正后的圖像undistortedImage,可以這樣使用這兩個(gè)函數(shù):
Mat map1,map2;
initUndistortRectifyMap(cameraMatrix, distCoeffs, R, newCameraMatrix, size, CV_32FC1, map1, map2);
remap(distortedImage, undistortedImage, map1, map2, INTER_LINEAR);
?? 在這個(gè)例子中,INTER_LINEAR是插值方法的類型,用于remap函數(shù)。其他的插值方法,如INTER_NEAREST、INTER_CUBIC等也可以被使用,具體取決于應(yīng)用需求。
Demo源碼
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;
// 插入U(xiǎn)SB攝像頭默認(rèn)為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);
// 步驟四:檢測角點(diǎn)
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, // 使用自適應(yīng)閾值將圖像轉(zhuǎn)化成二值圖像
CALIB_CB_NORMALIZE_IMAGE = 2, // 歸一化圖像灰度系數(shù)(用直方圖均衡化或者自適應(yīng)閾值)
CALIB_CB_FILTER_QUADS = 4, // 在輪廓提取階段,使用附加條件排除錯(cuò)誤的假設(shè)
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();
// 步驟五:繪制棋盤點(diǎn)
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
// 步驟六:進(jìn)一步提取亞像素角點(diǎn)
cv::TermCriteria criteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, // 類型
30, // 參數(shù)二: 最大次數(shù)
0.001); // 參數(shù)三:迭代終止閾值
/*
#define CV_TERMCRIT_ITER 1 // 終止條件為: 達(dá)到最大迭代次數(shù)終止
#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);
// 步驟七:繪制棋盤點(diǎn)
cv::drawChessboardCorners(srcMat3,
cv::Size(chessboardColCornerCount, chessboardRowCornerCount),
vectorPoint2fCorners,
patternWasFound);
cv::imshow("5", srcMat3);
// cv::waitKey(0);
// 步驟八:角點(diǎn)對應(yīng)的三維坐標(biāo)(一張圖一組)
std::vector<std::vector<cv::Point3f>> vectorObjectPoint;
std::vector<cv::Point3f> objectPoints; // 三維世界坐標(biāo)系
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);
// 步驟九:圖像識(shí)別出來的角點(diǎn)(一張圖一組)
std::vector<std::vector<cv::Point2f>> vectorImagePoint;
vectorImagePoint.push_back(vectorPoint2fCorners);
// 步驟十:計(jì)算內(nèi)參和畸變系數(shù)
cv::Mat cameraMatrix; // 相機(jī)矩陣(接收輸出)
cv::Mat distCoeffs; // 畸變系數(shù)(接收輸出)
cv::Mat Rotate; // 旋轉(zhuǎn)量(接收輸出)
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;
// 步驟十一:畸變圖像校準(zhǔn)
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
}
對應(yīng)工程模板v1.68.0
??
入坑
入坑一:無法識(shí)別圖像
問題
??無法識(shí)別。
??
原理
??要全部棋盤視野內(nèi),且可以識(shí)別,這個(gè)確實(shí)識(shí)別不了。
解決
??換圖重新來過(這是筆者隨便找的圖)。
入坑二:校準(zhǔn)之后四角不準(zhǔn)
問題
??四角明顯不對。
??
原理
??這里需要多張圖在能識(shí)別的情況下覆蓋所有區(qū)域。
解決
??先這樣,下次實(shí)際標(biāo)定的時(shí)候再多張圖看是否還存在該問題。
上一篇:《OpenCV開發(fā)筆記(七十六):相機(jī)標(biāo)定(一):識(shí)別棋盤并繪制角點(diǎn)》
下一篇:持續(xù)補(bǔ)充中…
若該文為原創(chuàng)文章,轉(zhuǎn)載請注明原文出處
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/136616551文章來源地址http://www.zghlxwxcb.cn/news/detail-839238.html
到了這里,關(guān)于OpenCV開發(fā)筆記(七十七):相機(jī)標(biāo)定(二):通過棋盤標(biāo)定計(jì)算相機(jī)內(nèi)參矩陣矯正畸變攝像頭圖像的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!