OpenCV相機標定與畸變校正
點擊上方“小白學視覺”,選擇加"星標"或“置頂”
重磅干貨,第一時間送達
相機標定定義與原理
在圖像測量過程以及機器視覺應用中,為確定空間物體表面某點的三維幾何位置與其在圖像中對應點之間的相互關系,必須建立相機成像的幾何模型,這些幾何模型參數(shù)就是相機參數(shù)。在大多數(shù)條件下這些參數(shù)必須通過實驗與計算才能得到,這個求解參數(shù)的過程就稱之為相機標定(或攝像機標定)。相機標定常見的分為:
單目相機標定
雙目相機標定
相機標定是想從二維的圖像中獲取三維信息,實現(xiàn)圖像的畸變校正、對象測量、三維重建等。由于光線投射導致實際對象物體跟投影到2D平面的圖像不一致,幸運的是這種不一致性是穩(wěn)定的,我們可以通過對相機標定,計算出畸變參數(shù)來實現(xiàn)對后續(xù)圖像的畸變校正。根據(jù)標定技術不一樣可以分為下面幾類標定方法:
基于3D對象參照標定
基于2D平面標定
基于1D線性標定
自標定
最常見的相機成像方式是基于pinhole的模型、它的成像模型可以圖示如下:

下面我們首先對這個相機成像模型做一番解釋

通過標定算法同時求出相機內參與外參。最常用的算法是張正友標定算法。OpenCV/Matlab中均已經實現(xiàn)該算法。
標定板介紹與制作
要想實現(xiàn)對相機的標定,我們首先需要給相機找到個參考對象,常見的就是標定版的類型有如下幾種
Chessboard
Circel-grid
RandPattern
ArUco
ChArUc

OpenCV源碼在其sample/data目錄下面一個自帶的棋盤圖(chessboard.png),顯示如下:

在標定的時候,算法要求提供的棋盤格的寬度與高度,還有他們的間隔距離。需要特別注意是這里的寬高是指他們的內部交叉點的個數(shù),以上圖為例,它的大小為7x7而不是8x8。間隔是指棋盤格之間的距離,可以用像素距離表示,也可以用實際毫米為單位表示。
制作標定版與圖像生成
最簡單的辦法就是把上述圖像直接打印出來,貼到一個塑料底板上就好啦。如果是土豪可以直接購買各種玻璃底板的標定板。另外還有一個更惡搞的方法,連打印都省啦,直接把chessboard.png這張圖在另外一臺電腦的顯示器上顯示,然后把攝像頭對著它各種牌即可完成圖像數(shù)據(jù)采集。這個是我手寫的采集程序代碼,每次想保存圖像的時候請安Q字母鍵即可,代碼如下:
void create_images() {
Mat frame;
VideoCapture capture(0);
int index = 1;
while (true) {
bool ret = capture.read(frame);
flip(frame, frame, 1);
if (!ret) break;
imshow("frame", frame);
char c = waitKey(50);
printf("%d
", c);
if (c == 113) { // Q
imwrite(format("D:/images/zsxq/%d.png", index), frame);
index += 1;
}
if (c == 27) {
break; // ESC
}
}
capture.release();
}記得拿著棋盤格圖,在鏡頭面前各種擺POSE,這個是屬于你的表演時間,不要客氣!具體參考下圖:

相機標定程序實現(xiàn)
大家好,現(xiàn)在我們開始程序實現(xiàn)環(huán)節(jié),OpenCV中在camera模塊中已經實現(xiàn)了張正友標定算法。我們只需要正確調用,就可以計算出相機的內參與外參,完成相機的標定。具體的代碼實現(xiàn)步驟如下:
定義相機標定的相關常量設置與變量
// load image files
vector<string> files;
glob("D:/images/camera2d", files);
// 定義變量
vector<vector<Point2f>> imagePoints;
vector<vector<Point3f>> objectPoints;
TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 30, 0.001);
int numCornersHor = 7;
int numCornersVer = 7;
int numSquares = 50;
vector<Point3f> obj;
for (int i = 0; i < numCornersHor; i++)
for (int j = 0; j < numCornersVer; j++)
obj.push_back(Point3f((float)j * numSquares, (float)i * numSquares, 0));發(fā)現(xiàn)與繪制棋盤格位置
// 發(fā)現(xiàn)棋盤格與繪制
Size s;
for (int i = 0; i < files.size(); i++) {
printf("image file : %s
", files[i].c_str());
Mat image = imread(files[i]);
s = image.size();
Mat gray;
cvtColor(image, gray, COLOR_BGR2GRAY);
vector<Point2f> corners;
bool ret = findChessboardCorners(gray, Size(7, 7), corners, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FILTER_QUADS);
if (ret) {
cornerSubPix(gray, corners, Size(11, 11), Size(-1, -1), criteria);
drawChessboardCorners(image, Size(7, 7), corners, ret);
imagePoints.push_back(corners);
objectPoints.push_back(obj);
imshow("calibration-demo", image);
waitKey(500);
}
}發(fā)現(xiàn)棋盤格顯示如下(我是直接打印OpenCV自帶那張圖的)

相機校正-計算內參數(shù)
// 相機校正
Mat intrinsic = Mat(3, 3, CV_32FC1);
Mat distCoeffs;
vector<Mat> rvecs;
vector<Mat> tvecs;
intrinsic.ptr<float>(0)[0] = 1;
intrinsic.ptr<float>(1)[1] = 1;
calibrateCamera(objectPoints, imagePoints, s, intrinsic, distCoeffs, rvecs, tvecs);畸變圖像校正

關于畸變類型,常見的圖像畸變類型有徑向與切向畸變、OpenCV中的相機標定方法只能對徑向畸變有效,使用內參對畸變圖像實現(xiàn)校正。相關的代碼如下:
// 畸變校正
for (int i = 0; i < files.size(); i++) {
Mat dst;
Mat image = imread(files[i]);
undistort(image, dst, intrinsic, distCoeffs);
imshow("image", image);
imshow("undistort image", dst);
waitKey(1000);
}
好消息!
小白學視覺知識星球
開始面向外開放啦??????
下載1:OpenCV-Contrib擴展模塊中文版教程 在「小白學視覺」公眾號后臺回復:擴展模塊中文教程,即可下載全網(wǎng)第一份OpenCV擴展模塊教程中文版,涵蓋擴展模塊安裝、SFM算法、立體視覺、目標跟蹤、生物視覺、超分辨率處理等二十多章內容。 下載2:Python視覺實戰(zhàn)項目52講 在「小白學視覺」公眾號后臺回復:Python視覺實戰(zhàn)項目,即可下載包括圖像分割、口罩檢測、車道線檢測、車輛計數(shù)、添加眼線、車牌識別、字符識別、情緒檢測、文本內容提取、面部識別等31個視覺實戰(zhàn)項目,助力快速學校計算機視覺。 下載3:OpenCV實戰(zhàn)項目20講 在「小白學視覺」公眾號后臺回復:OpenCV實戰(zhàn)項目20講,即可下載含有20個基于OpenCV實現(xiàn)20個實戰(zhàn)項目,實現(xiàn)OpenCV學習進階。 交流群
歡迎加入公眾號讀者群一起和同行交流,目前有SLAM、三維視覺、傳感器、自動駕駛、計算攝影、檢測、分割、識別、醫(yī)學影像、GAN、算法競賽等微信群(以后會逐漸細分),請掃描下面微信號加群,備注:”昵稱+學校/公司+研究方向“,例如:”張三 + 上海交大 + 視覺SLAM“。請按照格式備注,否則不予通過。添加成功后會根據(jù)研究方向邀請進入相關微信群。請勿在群內發(fā)送廣告,否則會請出群,謝謝理解~

