OPENCV第十章-立体视觉
相机成像模型
单目相机模型
dx和dy对应的是1个像素对应长度和高度
由此可以得到空间某一点到图像中某一像素点的关系
注意:进行投影变换后之后的成像平面是物理坐标系,单位是米厘米或者毫米
通过K就能计算像素中u和v的值
相机畸变
模型投影函数
rvec 和 tvec可以先设置为单位1
畸变系数矩阵就是之前的k1k2k3p1p2
#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
//输入前文计算得到的内参矩阵和畸变矩阵,建议现场标定
Mat cameraMatrix = (Mat_<float>(3, 3) << 532.016297, 0, 332.172519,
0, 531.565159, 233.388075,
0, 0, 1);
Mat distCoeffs = (Mat_<float>(1, 5) << -0.285188, 0.080097, 0.001274,
-0.002415, 0.106579);
//代码清单10-10中计算的第一张图像相机坐标系与世界坐标系之间的关系
Mat rvec = (Mat_<float>(1, 3) << -1.977853, -2.002220, 0.130029);
Mat tvec = (Mat_<float>(1, 3) << -26.88155, -42.79936, 159.19703);
//生成第一张图像中内角点的三维世界坐标
Size boardSize = Size(9, 6);
Size squareSize = Size(10, 10); //棋盘格每个方格的真实尺寸
vector<Point3f> PointSets;
for (int j = 0; j < boardSize.height; j++)
{
for (int k = 0; k < boardSize.width; k++)
{
Point3f realPoint;
// 假设标定板为世界坐标系的z平面,即z=0
realPoint.x = j * squareSize.width;
realPoint.y = k * squareSize.height;
realPoint.z = 0;
PointSets.push_back(realPoint);
}
}
//根据三维坐标和相机与世界坐标系时间的关系估计内角点像素坐标
vector<Point2f> imagePoints;
projectPoints(PointSets, rvec, tvec, cameraMatrix, distCoeffs, imagePoints);
for (int i = 0; i < imagePoints.size(); i++) {
cout << "第" << to_string(i) << "个点的坐标" << imagePoints[i] << endl;
}
waitKey(0);
return 0;
}
单目相机标定
标定原理
可以对世界坐标系转换成相机坐标系下的坐标
联合以上所有函数,可以得到
z_{w}\begin{bmatrix}u\\v\\1\end{bmatrix} = K\begin{bmatrix}R & t\end{bmatrix}\begin{bmatrix}x_{w}\\y_{w}\\z_{w}\\1\end{bmatrix}
其中K是相机内参矩阵,R是世界坐标系旋转矩阵,t是平移量 通过世界坐标系中的点,也有图像中像素的坐标,就可以求出内参矩阵和外参矩阵
标定板角点提取
圆形标定板中心提取
角点位置优化
通常用(5×5)的范围
绘制内焦点提取结果
相机标定函数
第二个参数是通过角点检测得到的二维坐标
可以人为先给出内参矩阵,减少迭代次数
示例
#include <opencv2\opencv.hpp>
#include <fstream>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
//读取所有图像
vector<Mat> imgs;
string imageName;
ifstream fin("calibdata.txt");
while (getline(fin, imageName))
{
Mat img = imread(imageName);
imgs.push_back(img);
}
Size board_size = Size(9, 6); //方格标定板内角点数目(行,列)
vector<vector<Point2f>> imgsPoints;
for (int i = 0; i < imgs.size(); i++)
{
Mat img1 = imgs[i];
Mat gray1;
cvtColor(img1, gray1, COLOR_BGR2GRAY);
vector<Point2f> img1_points;
findChessboardCorners(gray1, board_size, img1_points); //计算方格标定板角点
find4QuadCornerSubpix(gray1, img1_points, Size(5, 5)); //细化方格标定板角点坐标
drawChessboardCorners(img1, board_size, img1_points, true);
imshow("img1", img1);
waitKey(0);
imgsPoints.push_back(img1_points);
}
//生成棋盘格每个内角点的空间三维坐标
Size squareSize = Size(10, 10); //棋盘格每个方格的真实尺寸
vector<vector<Point3f>> objectPoints;
for (int i = 0; i < imgsPoints.size(); i++)
{
vector<Point3f> tempPointSet;
for (int j = 0; j < board_size.height; j++)
{
for (int k = 0; k < board_size.width; k++)
{
Point3f realPoint;
// 假设标定板为世界坐标系的z平面,即z=0
realPoint.x = j * squareSize.width;
realPoint.y = k * squareSize.height;
realPoint.z = 0;
tempPointSet.push_back(realPoint);
}
}
objectPoints.push_back(tempPointSet);
}
/* 初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板 */
vector<int> point_number;
for (int i = 0; i < imgsPoints.size(); i++)
{
point_number.push_back(board_size.width * board_size.height);
}
//图像尺寸
Size imageSize;
imageSize.width = imgs[0].cols;
imageSize.height = imgs[0].rows;
Mat cameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); //摄像机内参数矩阵
Mat distCoeffs = Mat(1, 5, CV_32FC1, Scalar::all(0)); //摄像机的5个畸变系数:k1,k2,p1,p2,k3
vector<Mat> rvecs; //每幅图像的旋转向量
vector<Mat> tvecs; //每张图像的平移量
calibrateCamera(objectPoints, imgsPoints, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, 0);
cout << "相机的内参矩阵=" << endl << cameraMatrix << endl;
cout << "相机畸变系数" << distCoeffs << endl;
waitKey(0);
return 0;
}
图像校正
示例
#include <opencv2\opencv.hpp>
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
using namespace cv;
//使用initUndistortRectifyMap()函数和remap()函数校正图像
void initUndistAndRemap(vector<Mat> imgs, //所有原图像向量
Mat cameraMatrix, //计算得到的相机内参
Mat distCoeffs, //计算得到的相机畸变系数
Size imageSize, //图像的尺寸
vector<Mat>& undistImgs) //校正后的输出图像
{
//计算映射坐标矩阵
Mat R = Mat::eye(3, 3, CV_32F);
Mat mapx = Mat(imageSize, CV_32FC1);
Mat mapy = Mat(imageSize, CV_32FC1);
initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, imageSize, CV_32FC1, mapx, mapy);
//校正图像
for (int i = 0; i < imgs.size(); i++)
{
Mat undistImg;
remap(imgs[i], undistImg, mapx, mapy, INTER_LINEAR);
undistImgs.push_back(undistImg);
}
}
//用undistort()函数直接计算校正图像
void undist(vector<Mat> imgs, //所有原图像向量
Mat cameraMatrix, //计算得到的相机内参
Mat distCoeffs, //计算得到的相机畸变系数
vector<Mat>& undistImgs) //校正后的输出图像
{
for (int i = 0; i < imgs.size(); i++)
{
Mat undistImg;
undistort(imgs[i], undistImg, cameraMatrix, distCoeffs);
undistImgs.push_back(undistImg);
}
}
int main()
{
//读取所有图像
vector<Mat> imgs;
string imageName;
ifstream fin("calibdata.txt");
while (getline(fin, imageName))
{
Mat img = imread(imageName);
imgs.push_back(img);
}
//输入前文计算得到的内参矩阵
Mat cameraMatrix = (Mat_<float>(3, 3) << 532.016297, 0, 332.172519,
0, 531.565159, 233.388075,
0, 0, 1);
//输入前文计算得到的内参矩阵
Mat distCoeffs = (Mat_<float>(1, 5) << -0.285188, 0.080097, 0.001274, -0.002415, 0.106579);
vector<Mat> undistImgs;
Size imageSize;
imageSize.width = imgs[0].cols;
imageSize.height = imgs[0].rows;
//使用initUndistortRectifyMap()函数和remap()函数校正图像
initUndistAndRemap(imgs, cameraMatrix, distCoeffs, imageSize, undistImgs);
//用undistort()函数直接计算校正图像,下一行代码取消注释即可
//undist(imgs, cameraMatrix, distCoeffs, undistImgs);
//显示校正前后的图像
for (int i = 0; i < imgs.size(); i++)
{
string windowNumber = to_string(i);
imshow("未校正图像" + windowNumber, imgs[i]);
imshow("校正后图像" + windowNumber, undistImgs[i]);
}
waitKey(0);
return 0;
}
单目相机位姿估计
位姿估计函数
世界坐标系可以理解为前一时刻的相机坐标系
同样的,位姿估计函数同样也有ransac函数
inliers为强制性认为规定的匹配的点,默认情况下不输入
示例(pnp和pnpAndRansac)
#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
//读取所有图像
Mat img = imread("left01.jpg");
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
vector<Point2f> imgPoints;
Size boardSize = Size(9, 6);
findChessboardCorners(gray, boardSize, imgPoints); //计算方格标定板角点
find4QuadCornerSubpix(gray, imgPoints, Size(5, 5)); //细化方格标定板角点坐标
//生成棋盘格每个内角点的空间三维坐标
Size squareSize = Size(10, 10); //棋盘格每个方格的真实尺寸
vector<Point3f> PointSets;
for (int j = 0; j < boardSize.height; j++)
{
for (int k = 0; k < boardSize.width; k++)
{
Point3f realPoint;
// 假设标定板为世界坐标系的z平面,即z=0
realPoint.x = j * squareSize.width;
realPoint.y = k * squareSize.height;
realPoint.z = 0;
PointSets.push_back(realPoint);
}
}
//输入前文计算得到的内参矩阵和畸变矩阵
Mat cameraMatrix = (Mat_<float>(3, 3) << 532.016297, 0, 332.172519,
0, 531.565159, 233.388075,
0, 0, 1);
Mat distCoeffs = (Mat_<float>(1, 5) << -0.285188, 0.080097, 0.001274,
-0.002415, 0.106579);
//用PnP算法计算旋转和平移量
Mat rvec, tvec;
solvePnP(PointSets, imgPoints, cameraMatrix, distCoeffs, rvec, tvec);
cout << "世界坐标系变换到相机坐标系的旋转向量:" << rvec << endl;
//旋转向量转换旋转矩阵
Mat R;
Rodrigues(rvec, R);//有旋转向量变换为旋转矩阵,第一个参数输入,第二个参数输出
cout << "旋转向量转换成旋转矩阵:" << endl << R << endl;
//用PnP+Ransac算法计算旋转向量和平移向量
Mat rvecRansac, tvecRansac;
solvePnPRansac(PointSets, imgPoints, cameraMatrix, distCoeffs, rvecRansac, tvecRansac);
Mat RRansac;
Rodrigues(rvecRansac, RRansac);
cout << "旋转向量转换成旋转矩阵:" << endl << RRansac << endl;
waitKey(0);
return 0;
}
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 name1110
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果