OpenCV相機(jī)標(biāo)定的全過程記錄
一、OpenCV標(biāo)定的幾個常用函數(shù)
findChessboardCorners() 棋盤格角點檢測
bool findChessboardCorners( InputArray image,
Size patternSize,
OutputArray corners,
int flags = CALIB_CB_ADAPTIVE_THRESH +
CALIB_CB_NORMALIZE_IMAGE );
第一個參數(shù)是輸入的棋盤格圖像(可以是8位單通道或三通道圖像);
第二個參數(shù)是棋盤格內(nèi)部的角點的行列數(shù)(注意:不是棋盤格的行列數(shù),如棋盤格的行列數(shù)分別為4、8,而內(nèi)部角點的行列數(shù)分別是3、7,因此這里應(yīng)該指定為cv::Size(3, 7));
第三個參數(shù)是檢測到的棋盤格角點,類型為std::vectorcv::Point2f。
第四個參數(shù)flag,用于指定在檢測棋盤格角點的過程中所應(yīng)用的一種或多種過濾方法,可以使用下面的一種或多種,如果都是用則使用OR:
- cv::CALIB_CB_ADAPTIVE_THRESH:使用自適應(yīng)閾值將圖像轉(zhuǎn)化成二值圖像
- cv::CALIB_CB_NORMALIZE_IMAGE:歸一化圖像灰度系數(shù)(用直方圖均衡化或者自適應(yīng)閾值)
- cv::CALIB_CB_FILTER_QUADS:在輪廓提取階段,使用附加條件排除錯誤的假設(shè)
- cv::CALIB_CV_FAST_CHECK:快速檢測
cv::drawChessboardCorners() 棋盤格角點的繪制
drawChessboardCorners( InputOutputArray image,
Size patternSize,
InputArray corners,
bool patternWasFound );
- image為8-bit,三通道圖像
- patternSize,每一行每一列的角
- corners,已經(jīng)檢測到的角
- patternWasFound,findChessboardCorners的返回值
find4QuadCornerSubpix() 對粗提取的角點進(jìn)行精確化
find4QuadCornerSubpix( InputArray img,
InputOutputArray corners,
Size region_size );
- image源圖像
- corners,提供角點的初始坐標(biāo)
- region_size: 搜索窗口的一般尺寸
cornerSubPix() 亞像素檢測
void cornerSubPix( InputArray image,
InputOutputArray corners,
Size winSize,
Size zeroZone,
TermCriteria criteria );
- image源圖像
- corners,提供角點的初始坐標(biāo),返回更加精確的點
- winSize,搜索窗口的一般尺寸,如果winSize=Size(5,5),則search windows為11*11
- winSize,死區(qū)的一般尺寸,用來避免自相關(guān)矩陣的奇點,(-1,-1)表示沒有死區(qū)
- criteria,控制迭代次數(shù)和精度
calibrateCamera() 求解攝像機(jī)的內(nèi)在參數(shù)和外在參數(shù)
double calibrateCamera( InputArrayOfArrays objectPoints,
InputArrayOfArrays imagePoints,
Size imageSize,
InputOutputArray cameraMatrix,
InputOutputArray distCoeffs,
OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,
int flags = 0,
TermCriteria criteria = TermCriteria(TermCriteria::COUNT +
TermCriteria::EPS, 30, DBL_EPSILON) );
objectPoints,世界坐標(biāo),用vector<vector>,輸入x,y坐標(biāo),z坐標(biāo)為0
imagePoints,圖像坐標(biāo),vector<vector>
imageSize,圖像的大小用于初始化標(biāo)定攝像機(jī)的image的size
cameraMatrix,內(nèi)參數(shù)矩陣
distCoeffs,畸變矩陣
rvecs,位移向量
tvecs,旋轉(zhuǎn)向量
flags,可以組合:
CV_CALIB_USE_INTRINSIC_GUESS:使用該參數(shù)時,將包含有效的fx,fy,cx,cy的估計值的內(nèi)參矩陣cameraMatrix,作為初始值輸入,然后函數(shù)對其做進(jìn)一步優(yōu)化。如果不使用這個參數(shù),用圖像的中心點初始化光軸點坐標(biāo)(cx, cy),使用最小二乘估算出fx,fy(這種求法好像和張正友的論文不一樣,不知道為何要這樣處理)。注意,如果已知內(nèi)部參數(shù)(內(nèi)參矩陣和畸變系數(shù)),就不需要使用這個函數(shù)來估計外參,可以使用solvepnp()函數(shù)計算外參數(shù)矩陣。
CV_CALIB_FIX_PRINCIPAL_POINT:在進(jìn)行優(yōu)化時會固定光軸點,光軸點將保持為圖像的中心點。當(dāng)CV_CALIB_USE_INTRINSIC_GUESS參數(shù)被設(shè)置,保持為輸入的值。
CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只將fy作為可變量,進(jìn)行優(yōu)化計算。當(dāng)
CV_CALIB_USE_INTRINSIC_GUESS沒有被設(shè)置,fx和fy的實際輸入值將會被忽略,只有fx/fy的比值被計算和使用。
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三個畸變參數(shù)。使標(biāo)定函數(shù)使用有理模型,返回8個系數(shù)。如果沒有設(shè)置,則只計算其它5個畸變參數(shù)。
CALIB_THIN_PRISM_MODEL (薄棱鏡畸變模型):啟用畸變系數(shù)S1、S2、S3和S4。使標(biāo)定函數(shù)使用薄棱柱模型并返回12個系數(shù)。如果不設(shè)置標(biāo)志,則函數(shù)計算并返回只有5個失真系數(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個系數(shù)。如果不設(shè)置標(biāo)志,則函數(shù)計算并返回只有5個失真系數(shù)。
CALIB_FIX_TAUX_TAUY :在優(yōu)化過程中,傾斜傳感器模型的系數(shù)不被改變。如果cv_calib_use_intrinsic_guess設(shè)置,從提供的畸變系數(shù)矩陣中得到。否則,設(shè)置為0。
initUndistortRectifyMap() 計算畸變參數(shù)
void initUndistortRectifyMap(InputArray cameraMatrix,
InputArray distCoeffs,
InputArray R,
InputArray newCameraMatrix,
Size size,
int m1type,
OutputArray map1,
OutputArray map2)
- cameraMatrix,攝像機(jī)內(nèi)參數(shù)矩陣
- distCoeffs, 攝像機(jī)的5個畸變系數(shù),(k1,k2,p1,p2[,k3[,k4,k5,k6]])
- R,在客觀空間中的轉(zhuǎn)換對象
- newCameraMatrix,新的3*3的浮點型矩矩陣
- size,為失真圖像的大小
- m1type,第一個輸出的map,類型為CV_32FC1或CV_16SC2
- map1,x映射函數(shù)
- map2,y映射函數(shù)
二、繪制棋盤格,拍攝照片
這里自己畫一個棋盤格用作標(biāo)定,長度為1280像素,寬490像素,橫向10方格,縱向7方格
std_cb = Vision::makeCheckerboard(1280, 490, 10, 7, 0, (char *)"../blizzard/res/calibration/std_cb.png");
效果如圖

Vision是我個人創(chuàng)建的視覺類,可以用來繪制標(biāo)準(zhǔn)的棋盤格。
頭文件vision.h
//
// Created by czh on 18-10-16.
//
#ifndef OPENGL_PRO_VISION_H
#define OPENGL_PRO_VISION_H
#include "opencv2/opencv.hpp"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgcodecs/imgcodecs.hpp>
#include "iostream"
class Vision {
public:
static cv::Mat read(std::string file_path, int flags = cv::IMREAD_ANYCOLOR | cv::IMREAD_ANYDEPTH);
static cv::Mat write(std::string file_path, int flags = cv::IMREAD_ANYCOLOR | cv::IMREAD_ANYDEPTH);
static void dispConfig(cv::Mat img);
static cv::Mat makeCheckerboard(int bkgWidth, int bkgHeight, int sqXnum, int sqYnum = 0, int borderThickness = 0, char *savePath = NULL);
private:
};
#endif //OPENGL_PRO_VISION_H
源文件vision.cpp
//
// Created by czh on 18-10-16.
//
#include "vision.h"
#include "string.h"
using namespace std;
using namespace cv;
const char *findName(const char *ch) {
const char *name = strrchr(ch, '/');
return ++name;
}
cv::Mat Vision::read(std::string file_path, int flags) {
printf("#Vision read\n");
cv::Mat img;
img = cv::imread(file_path, flags);
if (img.data == NULL) {
printf("\tError:vision read\n");
} else {
dispConfig(img);
}
return img;
}
void Vision::dispConfig(cv::Mat img) {
printf("\tpixel:%d*%d, channels:%d\n", img.size().width, img.size().height, img.channels());
}
cv::Mat Vision::makeCheckerboard(int bkgWidth, int bkgHeight, int sqXnum, int sqYnum, int thickNum, char *savePath) {
if(sqYnum == 0){
sqYnum = sqXnum;
}
if(savePath == NULL){
char *defaultPath = (char *)"../res/calibration/maths.png";
savePath = defaultPath;
}
int checkboardX = 0;//棋盤x坐標(biāo)
int checkboardY = 0;//棋盤y坐標(biāo)
int xLen = bkgWidth / sqXnum;//x方格長度
int yLen = bkgHeight / sqYnum;//y方格長度
cv::Mat img(bkgHeight + thickNum * 2, bkgWidth + thickNum * 2, CV_8UC4, cv::Scalar(0, 255, 255, 255));
for (int i = 0; i < img.rows; i++) {
for (int j = 0; j < img.cols; j++) {
if (i < thickNum || i >= thickNum + bkgHeight || j < thickNum || j >= thickNum + bkgWidth) {
img.at<Vec<uchar, 4>>(i, j) = cv::Scalar(0, 0, 0, 255);
continue;
}
checkboardX = j - thickNum;
checkboardY = i - thickNum;
if (checkboardY / yLen % 2 == 0) {
if ((checkboardX) / xLen % 2 == 0) {
img.at<Vec<uchar, 4>>(i, j) = cv::Scalar(255, 255, 255, 255);
} else {
img.at<Vec<uchar, 4>>(i, j) = cv::Scalar(0, 0, 0, 255);
}
}
else{
if ((checkboardX) / xLen % 2 != 0) {
img.at<Vec<uchar, 4>>(i, j) = cv::Scalar(255, 255, 255, 255);
} else {
img.at<Vec<uchar, 4>>(i, j) = cv::Scalar(0, 0, 0, 255);
}
}
}
}
imwrite(savePath, img); //保存生成的圖片
printf("#makeCheckerboard %d*%d\n", bkgWidth + thickNum, bkgHeight + thickNum);
return img;
}
用A4紙打印棋盤格,相機(jī)拍攝照片。
我偷懶,拿了別人的標(biāo)定照片

三、相機(jī)標(biāo)定
下面是相機(jī)標(biāo)定代碼
cv::imwrite("../blizzard/res/calibration/cb_source.png", cb_source);
printf("#Start scan corner\n");
cv::Mat img;
std::vector<cv::Point2f> image_points;
std::vector<std::vector<cv::Point2f>> image_points_seq; /* 保存檢測到的所有角點 */
if (cv::findChessboardCorners(cb_source, cv::Size(aqXnum, aqYnum), image_points, 0) == 0) {
printf("#Error: Corners not find ");
return 0;
} else {
cvtColor(cb_source, img, CV_RGBA2GRAY);
cv::imwrite("../blizzard/res/calibration/cb_gray.png", img);
//find4QuadCornerSubpix(img, image_points, cv::Size(5, 5));
cv::cornerSubPix(img, image_points, cv::Size(11, 11), cv::Size(-1, -1),
cv::TermCriteria(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 30, 0.01));
image_points_seq.push_back(image_points);
cv::Mat cb_corner;
cb_corner = cb_source.clone();
drawChessboardCorners(cb_corner, cv::Size(aqXnum, aqYnum), image_points, true);
cv::imwrite("../blizzard/res/calibration/cb_corner.png", cb_corner);
}
printf("#Start calibrate\n");
cv::Size square_size = cv::Size(14.2222, 12);
std::vector<std::vector<cv::Point3f>> object_points; /* 保存標(biāo)定板上角點的三維坐標(biāo) */
cv::Mat cameraMatrix = cv::Mat(3, 3, CV_32FC1, cv::Scalar::all(0)); /* 攝像機(jī)內(nèi)參數(shù)矩陣 */
cv::Mat distCoeffs = cv::Mat(1, 5, CV_32FC1, cv::Scalar::all(0)); /* 攝像機(jī)的5個畸變系數(shù):k1,k2,p1,p2,k3 */
std::vector<cv::Mat> tvecsMat; /* 每幅圖像的旋轉(zhuǎn)向量 */
std::vector<cv::Mat> rvecsMat; /* 每幅圖像的平移向量 */
std::vector<cv::Point3f> realPoint;
for (int i = 0; i < aqYnum; i++) {
for (int j = 0; j < aqXnum; j++) {
cv::Point3f tempPoint;
/* 假設(shè)標(biāo)定板放在世界坐標(biāo)系中z=0的平面上 */
tempPoint.x = i * square_size.width;
tempPoint.y = j * square_size.height;
tempPoint.z = 0;
realPoint.push_back(tempPoint);
}
}
object_points.push_back(realPoint);
printf("#objectPoints: %ld\n", sizeof(object_points[0]));
std::cout << object_points[0] << std::endl;
printf("#image_points: %ld\n", sizeof(image_points_seq[0]));
std::cout << image_points << std::endl;
printf("#image size\n");
std::cout << SCREEN_WIDTH << "*" << SCREEN_HEIGHT << std::endl;
cv::calibrateCamera(object_points, image_points_seq, cb_source.size(), cameraMatrix, distCoeffs, rvecsMat, tvecsMat,
CV_CALIB_FIX_K3);
std::cout << "tvecsMat:\n" << tvecsMat[0] << std::endl;
std::cout << "rvecsMat:\n" << rvecsMat[0] << std::endl;
std::cout << "#cameraMatrix:\n" << cameraMatrix << std::endl;
std::cout << "#distCoeffs:\n" << distCoeffs << std::endl;
四、對圖片進(jìn)行校正
cv::Mat cb_final;
cv::Mat mapx = cv::Mat(cb_source.size(), CV_32FC1);
cv::Mat mapy = cv::Mat(cb_source.size(), CV_32FC1);
cv::Mat R = cv::Mat::eye(3, 3, CV_32F);
//initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cv::Mat(), cb_source.size(), CV_32FC1,
// mapx, mapy);
//cv::remap(cb_source, cb_final, mapx, mapy, cv::INTER_LINEAR);
undistort(cb_source, cb_final, cameraMatrix, distCoeffs);
cv::imwrite("../blizzard/res/calibration/cb_final.png", cb_final);
1.校正前的圖片

2.校正后的圖片

總結(jié)
到此這篇關(guān)于OpenCV相機(jī)標(biāo)定的文章就介紹到這了,更多相關(guān)OpenCV相機(jī)標(biāo)定內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言中動態(tài)內(nèi)存管理初學(xué)者容易犯的6個錯誤分享
本篇文章主要介紹了初學(xué)者使用C語言中動態(tài)內(nèi)存管理的4個函數(shù)時最容易犯的6個錯誤,以及如何避免這些錯誤,文中的示例代碼講解詳細(xì),感興趣的可以了解一下2023-04-04
linux C 打印錯誤信息和標(biāo)準(zhǔn)輸入輸出詳細(xì)介紹
這篇文章主要介紹了linux C 打印錯誤信息和標(biāo)準(zhǔn)輸入輸出詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-12-12
C語言詳細(xì)分析講解關(guān)鍵字const與volatile的用法
在C語言中,我們經(jīng)常會見到const和volatile這兩個關(guān)鍵字,那么我們今天就來介紹下這兩個關(guān)鍵字,提起?const?關(guān)鍵字,我們可能首先想到的是經(jīng)過它修飾的變量便是常量了。其實我們這種想法是錯誤的,其實?const?修飾的變量是只讀的,其本質(zhì)還是變量2022-04-04

