使用 OpenCV 2.3.1 和 C++ 校准单个相机
Posted
技术标签:
【中文标题】使用 OpenCV 2.3.1 和 C++ 校准单个相机【英文标题】:Calibrate single camera using OpenCV 2.3.1 and C++ 【发布时间】:2012-04-05 12:00:28 【问题描述】:我正在尝试使用 OpenCV 2.3.1 和 Visual Studio 2010(c++ 控制台应用程序)校准网络摄像头。我正在使用这个类:
class CameraCalibrator
private:
std::vector<std::vector<cv::Point3f>> objectPoints;
std::vector<std::vector<cv::Point2f>> imagePoints;
//Square Lenght
float squareLenght;
//output Matrices
cv::Mat cameraMatrix; //intrinsic
cv::Mat distCoeffs;
//flag to specify how calibration is done
int flag;
//used in image undistortion
cv::Mat map1,map2;
bool mustInitUndistort;
public:
CameraCalibrator(): flag(0), squareLenght(36.0), mustInitUndistort(true);
int addChessboardPoints(const std::vector<std::string>& filelist,cv::Size& boardSize)
std::vector<std::string>::const_iterator itImg;
std::vector<cv::Point2f> imageCorners;
std::vector<cv::Point3f> objectCorners;
//initialize the chessboard corners in the chessboard reference frame
//3d scene points
for(int i = 0; i<boardSize.height; i++)
for(int j=0;j<boardSize.width;j++)
objectCorners.push_back(cv::Point3f(float(i)*squareLenght,float(j)*squareLenght,0.0f));
//2D Image points:
cv::Mat image; //to contain chessboard image
int successes = 0;
//cv::namedWindow("Chess");
for(itImg=filelist.begin(); itImg!=filelist.end(); itImg++)
image = cv::imread(*itImg,0);
bool found = cv::findChessboardCorners(image, boardSize, imageCorners);
//cv::drawChessboardCorners(image, boardSize, imageCorners, found);
//cv::imshow("Chess",image);
//cv::waitKey(1000);
cv::cornerSubPix(image, imageCorners, cv::Size(5,5),cv::Size(-1,-1),
cv::TermCriteria(cv::TermCriteria::MAX_ITER+cv::TermCriteria::EPS,30,0.1));
//if we have a good board, add it to our data
if(imageCorners.size() == boardSize.area())
addPoints(imageCorners,objectCorners);
successes++;
return successes;
void addPoints(const std::vector<cv::Point2f>& imageCorners,const std::vector<cv::Point3f>& objectCorners)
//2D image point from one view
imagePoints.push_back(imageCorners);
//corresponding 3D scene points
objectPoints.push_back(objectCorners);
double calibrate(cv::Size &imageSize)
mustInitUndistort = true;
std::vector<cv::Mat> rvecs,tvecs;
return
cv::calibrateCamera(objectPoints, //the 3D points
imagePoints,
imageSize,
cameraMatrix, //output camera matrix
distCoeffs,
rvecs,tvecs,
flag);
void remap(const cv::Mat &image, cv::Mat &undistorted)
std::cout << cameraMatrix;
if(mustInitUndistort) //called once per calibration
cv::initUndistortRectifyMap(
cameraMatrix,
distCoeffs,
cv::Mat(),
cameraMatrix,
image.size(),
CV_32FC1,
map1,map2);
mustInitUndistort = false;
//apply mapping functions
cv::remap(image,undistorted,map1,map2,cv::INTER_LINEAR);
;
我正在使用 10 张棋盘图像(假设这足以进行校准),分辨率为 640x480。主函数如下所示:
int main()
CameraCalibrator calibrateCam;
std::vector<std::string> filelist;
filelist.push_back("img10.jpg");
filelist.push_back("img09.jpg");
filelist.push_back("img08.jpg");
filelist.push_back("img07.jpg");
filelist.push_back("img06.jpg");
filelist.push_back("img05.jpg");
filelist.push_back("img04.jpg");
filelist.push_back("img03.jpg");
filelist.push_back("img02.jpg");
filelist.push_back("img01.jpg");
cv::Size boardSize(8,6);
double calibrateError;
int success;
success = calibrateCam.addChessboardPoints(filelist,boardSize);
std::cout<<"Success:" << success << std::endl;
cv::Size imageSize;
cv::Mat inputImage, outputImage;
inputImage = cv::imread("img10.jpg",0);
outputImage = inputImage.clone();
imageSize = inputImage.size();
calibrateError = calibrateCam.calibrate(imageSize);
std::cout<<"Calibration error:" << calibrateError << std::endl;
calibrateCam.remap(inputImage,outputImage);
cv::namedWindow("Original");
cv::imshow("Original",inputImage);
cv::namedWindow("Undistorted");
cv::imshow("Undistorted",outputImage);
cv::waitKey();
return 0;
一切运行都没有错误。 cameraMatrix 看起来像这样(大约):
685.65 0 365.14 0 686.38 206.98 0 0 1
校准误差为0.310157,可以接受。
但是当我使用重映射时,输出图像看起来比原始图像更糟糕。这是示例:
原图:]
未失真的图像:]
那么,问题是,我在校准过程中是否做错了什么? 10个不同的棋盘图像是否足以校准?你有什么建议吗?
【问题讨论】:
OpenCV Transform using Chessboard 的可能重复项 嗨,我正在使用该课程。它对我来说非常好。我唯一的问题是如何读取相机内在函数。我正在尝试使用 .at相机矩阵不会使镜头不失真,这 4 个值只是焦距(H 和 V)和图像中心(X 和 Y)
还有另一个包含镜头映射的 3 或 4 值行矩阵(代码中的distCoeffs
) - 请参阅 Karl 的回答以获取示例代码
【讨论】:
是的,我明白这一点。我从 Karl 的回答中看到了示例代码。正如您在我的代码中看到的那样,distCoeffs
包含在函数 cv::initUndistortRectifyMap
中,我想知道我是否正确编写了该函数,因为在使用 cv::remap
后,我得到的结果比原来的要差,正如您在我的图片中看到的那样样本。感谢您的帮助。
@Banana - 检查 distcoefs 中的值,它们是空的还是奇怪的(我很感激很难知道什么是好的值!)但是你可以用你的相机运行 opencv 示例软件,看看它是什么计算
顺便说一句,图像中心在 365.14,206.98 对于 640x480 图像方式。它很可能更接近 320,240。我有一个中心点为 317.66,240.11 的 640x480 相机。【参考方案2】:
校准是通过数值优化完成的,该数值优化在解附近具有相当浅的斜率。此外,被最小化的函数是非常非线性的。所以,我的猜测是你的 10 张图片还不够。我使用非常广角镜头(即非常失真的图像)校准相机,并尝试获得 50 或 60 张图像。
我尝试使用棋盘在图像的每个边缘的 3 或 4 个位置以及中间的一些位置获取图像,相对于相机具有多个方向和 3 个不同的距离(超近、典型和远)你可以得到并且仍然解决棋盘)。
让棋盘靠近角落非常重要。您的示例图像在图像角落附近没有棋盘。正是这些点限制了校准在图像非常扭曲的部分(角落)做正确的事情。
【讨论】:
感谢您的解释。几天后我会按照您的描述尝试相机校准,并在此处写下所有结果。以上是关于使用 OpenCV 2.3.1 和 C++ 校准单个相机的主要内容,如果未能解决你的问题,请参考以下文章