Opencv——单目标定
相机标定就是设置相机各种参数的过程。当然相机厂家也会提供一些技术参数,但是对于某些任务来说,所提供的技术参数还不够精确。利用相机标定可以得到更精确的参数。
用这些参数可以更加精确的确定空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系。
标定步骤
1.一张棋盘格标定板
2.用 cv::findChessboardCorners(…) 函数获取棋盘角点
3.用 cv::drawChessboardCorners(…) 函数在图片上标出检测到的角点
4.用 cv::cornerSubPix(…)取得角点的亚像素级精度,提高标定参数的精确度
5.用 cv::calibrateCamera(…) 进行标定,取得相机矩阵、畸变矩阵以及旋转和平移矩阵
涉及的函数
//找到棋盘内部角点的位置
bool findChessboardCorners( InputArray image, //输入标定棋盘,必须是一个8位的灰度或彩色图像。
Size patternSize,//输入棋盘的角点数量
OutputArray corners,//检测到的角点列表
int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE //
);
找到并排列角点,函数返回True,否则为False
//画出角点
void drawChessboardCorners( InputOutputArray image, //输入的棋盘图片
Size patternSize,//输入棋盘的角点数量
InputArray corners,// 输入检测到的角点列表
bool patternWasFound //是否找到完整板的参数
);
//取得角点的亚像素级精度,提高标定参数的精确度
void cornerSubPix( InputArray image, //输入单通道,8位或浮点图像。
InputOutputArray corners,//输入/输出检测到的角点列表
Size winSize, //搜索窗口半径
Size zeroZone,//
TermCriteria criteria //终止标志位
);
//相机标定
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)//终止标志位
);
代码
#include <iostream>
#include <iomanip>
#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/features2d.hpp>
#include "CameraCalibrator.h"
int main()
{
cv::Mat image;
std::vector<std::string> filelist;
// generate list of chessboard image filename
// named chessboard01 to chessboard27 in chessboard sub-dir
for (int i = 1; i <= 28; i++) {
std::stringstream str;
str << "video/video" << std::setw(2) << std::setfill('0') << i << ".jpg";
std::cout << str.str() << std::endl;
filelist.push_back(str.str());
image = cv::imread(str.str(), 0);
// cv::imshow("Board Image",image);
// cv::waitKey(100);
}
// Create calibrator object
CameraCalibrator cameraCalibrator;
// add the corners from the chessboard
cv::Size boardSize(8, 6);
cameraCalibrator.addChessboardPoints(
filelist, // filenames of chessboard image
boardSize, "Detected points"); // size of chessboard
// calibrate the camera
//cameraCalibrator.setCalibrationFlag(true, true);
cameraCalibrator.calibrate(image.size());
// Exampple of Image Undistortion
image = cv::imread(filelist[14], 0);
cv::Size newSize(static_cast<int>(image.cols*1.5), static_cast<int>(image.rows*1.5));
cv::Mat uImage = cameraCalibrator.remap(image, newSize);
// display camera matrix
cv::Mat cameraMatrix = cameraCalibrator.getCameraMatrix();
std::cout << " Camera intrinsic: " << cameraMatrix.rows << "x" << cameraMatrix.cols << std::endl;
std::cout << cameraMatrix.at<double>(0, 0) << " " << cameraMatrix.at<double>(0, 1) << " " << cameraMatrix.at<double>(0, 2) << std::endl;
std::cout << cameraMatrix.at<double>(1, 0) << " " << cameraMatrix.at<double>(1, 1) << " " << cameraMatrix.at<double>(1, 2) << std::endl;
std::cout << cameraMatrix.at<double>(2, 0) << " " << cameraMatrix.at<double>(2, 1) << " " << cameraMatrix.at<double>(2, 2) << std::endl;
cv::Mat getDistCoeffs = cameraCalibrator.getDistCoeffs();
std::cout << " getDistCoeffs: " << getDistCoeffs.rows << "x" << getDistCoeffs.cols << std::endl;
std::cout << getDistCoeffs.at<double>(0, 0) << " " << getDistCoeffs.at<double>(0, 1) << " " << getDistCoeffs.at<double>(0, 2) << " " << getDistCoeffs.at<double>(0, 3) << " " << getDistCoeffs.at<double>(0, 4) << " " << getDistCoeffs.at<double>(0, 5) << " " << getDistCoeffs.at<double>(0, 6) << " " << getDistCoeffs.at<double>(0, 7) << " " << getDistCoeffs.at<double>(0, 8) << " " << getDistCoeffs.at<double>(0, 9) << " " << getDistCoeffs.at<double>(0, 10) << " " << getDistCoeffs.at<double>(0, 11) << " " << getDistCoeffs.at<double>(0, 12) << " " << getDistCoeffs.at<double>(0, 13) << std::endl;
cv::namedWindow("Original Image");
cv::imshow("Original Image", image);
cv::namedWindow("Undistorted Image");
cv::imshow("Undistorted Image", uImage);
// Store everything in a xml file
cv::FileStorage fs("calib.xml", cv::FileStorage::WRITE);
fs << "Intrinsic" << cameraMatrix;
fs << "Distortion" << cameraCalibrator.getDistCoeffs();
cv::waitKey();
return 0;
}
#ifndef CAMERACALIBRATOR_H
#define CAMERACALIBRATOR_H
#include <vector>
#include <iostream>
#include <opencv2/core.hpp>
#include "opencv2/imgproc.hpp"
#include "opencv2/calib3d.hpp"
#include <opencv2/highgui.hpp>
class CameraCalibrator {
// input points:
// the points in world coordinates
// (each square is one unit)
std::vector<std::vector<cv::Point3f> > objectPoints;
// the image point positions in pixels
std::vector<std::vector<cv::Point2f> > imagePoints;
// output Matrices
cv::Mat cameraMatrix = cv::Mat(3, 3, CV_32FC1, cv::Scalar::all(0));
cv::Mat distCoeffs = cv::Mat(1, 14, CV_32FC1, cv::Scalar::all(0));
// flag to specify how calibration is done
int flag;
// used in image undistortion
cv::Mat map1, map2;
bool mustInitUndistort;
public:
CameraCalibrator() : flag(0), mustInitUndistort(true) {}
// Open the chessboard images and extract corner points
int addChessboardPoints(const std::vector<std::string>& filelist, cv::Size & boardSize, std::string windowName = "");
// Add scene points and corresponding image points
void addPoints(const std::vector<cv::Point2f>& imageCorners, const std::vector<cv::Point3f>& objectCorners);
// Calibrate the camera
double calibrate(const cv::Size imageSize);
// Set the calibration flag
//void setCalibrationFlag(bool radial8CoeffEnabled = false, bool tangentialParamEnabled = false);
// Remove distortion in an image (after calibration)
cv::Mat remap(const cv::Mat &image, cv::Size &outputSize);
// Getters
cv::Mat getCameraMatrix() { return cameraMatrix; }
cv::Mat getDistCoeffs() { return distCoeffs; }
};
#endif // CAMERACALIBRATOR_H
#include "CameraCalibrator.h"
// Open chessboard images and extract corner points
int CameraCalibrator::addChessboardPoints(
const std::vector<std::string>& filelist, // list of filenames containing board images
cv::Size & boardSize, // size of the board
std::string windowName) { // name of window to display results
// if null, no display shown
// the points on the chessboard
std::vector<cv::Point2f> imageCorners;
std::vector<cv::Point3f> objectCorners;
// 3D Scene Points:
// Initialize the chessboard corners
// in the chessboard reference frame
// The corners are at 3D location (X,Y,Z)= (i,j,0)
for (int i = 0; i < boardSize.height; i++) {
for (int j = 0; j < boardSize.width; j++) {
objectCorners.push_back(cv::Point3f(i, j, 0.0f));
}
}
// 2D Image points:
cv::Mat image; // to contain chessboard image
int successes = 0;
// for all viewpoints
for (int i = 0; i < filelist.size(); i++) {
// Open the image
image = cv::imread(filelist[i], 1);
// Get the chessboard corners
bool found = cv::findChessboardCorners(image, // image of chessboard pattern
boardSize, // size of pattern
imageCorners); // list of detected corners
// Get subpixel accuracy on the corners
if (found) {
cv::cornerSubPix(image, imageCorners,
cv::Size(5, 5), // half size of serach window
cv::Size(-1, -1),
cv::TermCriteria(cv::TermCriteria::MAX_ITER +
cv::TermCriteria::EPS,
30, // max number of iterations
0.1)); // min accuracy
// If we have a good board, add it to our data
if (imageCorners.size() == boardSize.area()) {
// Add image and scene points from one view
addPoints(imageCorners, objectCorners);
successes++;
}
}
if (windowName.length() > 0 && imageCorners.size() == boardSize.area()) {
//Draw the corners
cv::drawChessboardCorners(image, boardSize, imageCorners, found);
cv::imshow(windowName, image);
cv::waitKey(100);
}
}
return successes;
}
// Add scene points and corresponding image points
void CameraCalibrator::addPoints(const std::vector<cv::Point2f>& imageCorners, const std::vector<cv::Point3f>& objectCorners) {
// 2D image points from one view
imagePoints.push_back(imageCorners);
// corresponding 3D scene points
objectPoints.push_back(objectCorners);
}
// Calibrate the camera
// returns the re-projection error
double CameraCalibrator::calibrate(const cv::Size imageSize)
{
// undistorter must be reinitialized
mustInitUndistort = true;
//Output rotations and translations
std::vector<cv::Mat> rvecs, tvecs;
// start calibration
return
calibrateCamera(objectPoints, // the 3D points
imagePoints, // the image points
imageSize, // image size
cameraMatrix, // output camera matrix
distCoeffs, // output distortion matrix
rvecs, tvecs, // Rs, Ts
cv::CALIB_RATIONAL_MODEL); // set options
// ,CV_CALIB_USE_INTRINSIC_GUESS);
}
// remove distortion in an image (after calibration)
cv::Mat CameraCalibrator::remap(const cv::Mat &image, cv::Size &outputSize) {
cv::Mat undistorted;
if (outputSize.height == -1)
outputSize = image.size();
if (mustInitUndistort) { // called once per calibration
cv::initUndistortRectifyMap(
cameraMatrix, // computed camera matrix
distCoeffs, // computed distortion matrix
cv::Mat(), // optional rectification (none)
cv::Mat(), // camera matrix to generate undistorted
outputSize, // size of undistorted
CV_32FC1, // type of output map
map1, map2); // the x and y mapping functions
mustInitUndistort = false;
}
// Apply mapping functions
cv::remap(image, undistorted, map1, map2,
cv::INTER_LINEAR); // interpolation type
return undistorted;
}
矫正
用已有的参数矫正图像,默认使用五个系数,也可以采用八个系数的模型,得到系数后就可以计算两个cv::Mat类型的映射函数(分别用x坐标和用y坐标),把有畸变的图像上的像素映射到未畸变的新位置
//计算映射值
void initUndistortRectifyMap(InputArray cameraMatrix, //计算得到的相机矩阵
InputArray distCoeffs,//计算得到的畸变矩阵
InputArray R, //可选矫正项
InputArray newCameraMatrix,//生成无畸变的相机矩阵
Size size,//无畸变的图像尺寸
int m1type, //输出图片类型
OutputArray map1, //映射的X坐标
OutputArray map2//映射的Y坐标
);
//把输入图片上的点映射到未畸变的新位置
void remap( InputArray src, //输入的图片
OutputArray dst,//输出矫正的图片
InputArray map1, //输入映射的X坐标
InputArray map2,//输入映射的Y坐标
int interpolation,//插值法
int borderMode = BORDER_CONSTANT,
const Scalar& borderValue = Scalar()
);