opencv相机标定(3)-鱼眼镜头成像模型与标定

成像模型

这篇博客介绍得很详细,链接
其中,opencv鱼眼相机标定后,畸变参数是4个,链接里博客的畸变参数是五个,应该是使k0=1.
在这里插入图片描述
1)世界坐标系->相机坐标系
在这里插入图片描述
2)相机坐标系->图像物理坐标系
在这里插入图片描述
注意使用的是归一化的坐标,鱼眼模型好像都是近似为单位球面投影模型,这点还是比较困惑,为什么不直接用f,而是在单位球面上建模。

径向畸变坐标
在这里插入图片描述
在这里插入图片描述
xd,yd这个公式不太明白,括号内应该是rd才对吧?不过如果是单位球面、等距投影,rd=theta_d,这样就说的通了。
在这里插入图片描述
3)图像物理坐标系->图像像素坐标系
在这里插入图片描述
综合:
在这里插入图片描述

鱼眼相机标定与调整显示范围

#include "LearnBasic.h"
#include "stdio.h"
#include <iostream>
#include <fstream>
#include <io.h>
void getFiles(string path, vector<string>& files)
{
	//文件句柄
	intptr_t hFile = 0;
	//文件信息
	struct _finddata_t fileinfo;
	string p;
	if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
	{
		do
		{
			//如果是目录,迭代之
			//如果不是,加入列表
			if ((fileinfo.attrib &  _A_SUBDIR))
			{
				if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
					getFiles(p.assign(path).append("\\").append(fileinfo.name), files);
			}
			else
			{
				//cout << fileinfo.name << endl;
				files.push_back(p.assign(path).append("\\").append(fileinfo.name));
			}
		} while (_findnext(hFile, &fileinfo) == 0);
		_findclose(hFile);
	}
}

void FishEyeCalibration()
{
	string filePath = "C:\\zyk\\cv_calibration";
	vector<string> files;
	//获取该路径下的所有文件
	int Rownum = 7;
	int Colnum = 9;
	double space = 15.0;
	Size PicSize(2592,1944);
	vector<vector<Point2f>> ImgPoints;
	vector<vector<Point3f>> WorldPoints;
	vector<cv::Point2f> ImgPoint;
	vector<Point3f> WorldPoint;
	Mat	   cameraMatrix;//相机内参,最好不要指定类型,否则某些算子类型不符合的话报错
	Mat    distCoeffs;//相机畸变系数
	vector<Mat> rvecs;//旋转矩阵
	vector<Mat> tvecs;

	getFiles(filePath, files);
	int flag = cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC | cv::fisheye::CALIB_CHECK_COND |
		cv::fisheye::CALIB_FIX_SKEW;
	int picnum = files.size();
	Mat img, img_Gray, result;
	//构建棋盘坐标点
	int NPoints = Rownum * Colnum;
	for (int j = 0; j < NPoints; j++)
	{
		WorldPoint.push_back(Point3f((j % Colnum) * space, (j / Colnum) * space, 0));
	}
	//检测棋盘点
	namedWindow("img", WINDOW_NORMAL);
	for (int i = 0; i < picnum; i++)
	{
		img = imread(files.at(i));
		cvtColor(img, img_Gray, COLOR_BGR2GRAY);
		if (cv::findChessboardCorners(img_Gray, cv::Size(Colnum, Rownum), ImgPoint, 0) == 0)
		{
			cout << "drop out:" << files.at(i) << endl;
			continue;
		}
		WorldPoints.push_back(WorldPoint);
		cornerSubPix(img_Gray, ImgPoint, cv::Size(3, 3), cv::Size(-1, -1),
					cv::TermCriteria(cv::TermCriteria::MAX_ITER + TermCriteria::EPS, 30, 0.1));
//		drawChessboardCorners(img, cv::Size(Colnum, Rownum), ImgPoint, true);
// 		imshow("img", img);
// 		waitKey(0);
		ImgPoints.push_back(ImgPoint);
	}
	//标定
	cv::fisheye::calibrate(WorldPoints, ImgPoints, PicSize, cameraMatrix, distCoeffs,
		rvecs, tvecs, flag, cv::TermCriteria(3, 20, 1e-6));
	cout << cameraMatrix << endl;
	cout << distCoeffs << endl;
	
	
	//调整相机显示
	double MechineW = 540;//设置想要的幅面宽,高按比例转换
	double ChessboardW =(Colnum-1)* space;//棋盘点范围,范围外畸变校正的效果应该比较差
	double Chessboardh = (Rownum-1)* space;

	vector<Point2f> undistort_imgpts;
	vector<Point2f> prj_imgpts;
	Mat newcameraMatrix = cameraMatrix.clone();
	cv::fisheye::projectPoints(WorldPoints[0], prj_imgpts, rvecs[0], tvecs[0], cameraMatrix, distCoeffs,0,noArray());
	cv::fisheye::undistortPoints(prj_imgpts, undistort_imgpts, cameraMatrix, distCoeffs, cv::noArray(), cameraMatrix);
	double ChessboardW_Pix = fabs(undistort_imgpts.at(0).x - undistort_imgpts.at(Rownum*Colnum - 1).x);
	double ChessboardH_Pix = fabs(undistort_imgpts.at(0).y - undistort_imgpts.at(Rownum*Colnum - 1).y);
	double pixsize = sqrt((pow(ChessboardW, 2) + pow(ChessboardW, 2)) / (pow(ChessboardW_Pix, 2) + pow(ChessboardH_Pix, 2)));
	cout << "畸变校正后像素精度:" << pixsize<< endl;
	
	double scale = MechineW / pixsize /PicSize.width;//缩放比例,确定新的内参,可以想象为把靶面尺寸缩放
	cout << "scale:"<<scale << endl;
	newcameraMatrix.at<double>(0, 0) = cameraMatrix.at<double>(0, 0) /scale;
	newcameraMatrix.at<double>(1, 1) = cameraMatrix.at<double>(1, 1) /scale;

	Mat mapx, mapy;
	cv::fisheye::initUndistortRectifyMap(cameraMatrix, distCoeffs, cv::Matx33d::eye(), newcameraMatrix,
		PicSize, CV_16SC2, mapx, mapy);
	for (int i = 0; i < picnum; i++)
	{
		img = imread(files.at(i));
		remap(img, result, mapx, mapy, INTER_LINEAR,BORDER_CONSTANT);
		imshow("img", result);
		waitKey(0);
	}
}





调整显示后,大致就是我需要的一个540宽左右区域的范围

在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值