opencv双⽬标定+⽴体校正+⽴体匹配(源码讲解)
//双⽬标定⽆⾮就是重复两次单⽬标定的流程,单⽬标定参考我上⼀篇博客。
//在学习双⽬视觉之前,建议⼤家补充下,双⽬视觉模型,对极⼏何的知识,今天只讲源码的流程,以后出⼀篇对极⼏何的讲解。
//⽼规矩先来⼀段源码
#include"opencv2/core.hpp"
#include<opencv2/core/utility.hpp>
#include<opencv2/imgproc.hpp>
#include"opencv2/calib3d.hpp"
#include"opencv2/imgcodecs.hpp"
#include"opencv2/videoio.hpp"
#include"opencv2/highgui.hpp"
#include<cctype>
#include<stdio.h>
#include<string.h>
#include<time.h>
#include<iostream>
using namespace std;
using namespace cv;
static double computeReprojectionErrors(
const vector<vector<Point3f>>& objectPoints,
const vector<vector<Point2f>>& imagePoints,
const vector<vector<Point2f>>& imagePoints,
const vector<Mat>& rvecs,const vector<Mat>& tvecs,
const Mat& cameraMatrix,const Mat& distCoeffs
)
{
vector<Point2f> imagePoints2;
int i, totalPoints =0;
double totalErr =0, err;
for(i =0; i <(int)objectPoints.size(); i++)
{
projectPoints(Mat(objectPoints[i]), rvecs[i], tvecs[i],
cameraMatrix, distCoeffs, imagePoints2);
err =norm(Mat(imagePoints[i]),Mat(imagePoints2), NORM_L2);
int n =(int)objectPoints[i].size();
totalErr += err*err;
totalPoints += n;
}
return std::sqrt(totalErr / totalPoints);
}
int main(int argc,char** argv)
{
vector<string> files_left;
vector<string> files_right;
glob("E:\\mul_cam_images\\left1", files_left);
glob("E:\\mul_cam_images\\right1", files_right);
// 定义变量
vector<vector<Point2f>> image_leftPoints,image_rightPoints;//像点
vector<vector<Point3f>> objectPoints;//物点
TermCriteria criteria =TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER,30,0.001);//进⾏亚像素精度的调整,获得亚像素级别的⾓点坐标
int numCornersHor =8;//heigh
int numCornersVer =11;//width
int numSquares =15;//单位mm
Mat gray_l, gray_r;
Mat image_l, image_r;
vector<Point3f> obj;
for(int i =0; i < numCornersHor; i++)
for(int j =0; j < numCornersVer; j++)
obj.push_back(Point3f((float)j * numSquares,(float)i * numSquares,0));
//存放每张图的⾓点坐标,并存⼊obj中(物点)
Size s1,s2;
//像点
for(int i =0; i <16; i++){
printf("image file : %s \n", files_left[i].c_str());
image_l =imread(files_left[i]);
printf("image file : %s \n", files_right[i].c_str());
image_r =imread(files_right[i]);
s1 = image_l.size();
s2 = image_r.size();
cvtColor(image_l, gray_l, COLOR_BGR2GRAY);//转灰度
cvtColor(image_r, gray_r, COLOR_BGR2GRAY);//转灰度
vector<Point2f> corners_r;
vector<Point2f> corners_l;
bool ret1 =findChessboardCorners(gray_r,Size(11,8), corners_r, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FILTER_QUADS);//该函数的功能就是判断图像内是否包含完整的棋盘图,若能检测完全,就把他们的⾓点坐标(从上到下,从左到右)记录,并返回true,否则为false,CALIB_CB_FILTER_QUADS⽤于去除检测到的错误⽅块。
bool ret2 =findChessboardCorners(gray_l,Size(11,8), corners_l, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FILTER_QUADS);
if(ret1)
{
cornerSubPix(gray_l, corners_l,Size(11,11),Size(-1,-1), criteria);//⽤于发现亚像素精度的⾓点位置
drawChessboardCorners(image_l,Size(11,8), corners_l, ret1);//将每⼀个⾓点出做标记,此为物点对应的像点坐标
//imshow("calibration-demo1", image_l);
waitKey(500);
}
}
if(ret2)
{
cornerSubPix(gray_r, corners_r,Size(11,11),Size(-1,-1), criteria);//⽤于发现亚像素精度的⾓点位置
drawChessboardCorners(image_r,Size(11,8), corners_r, ret2);//将每⼀个⾓点出做标记,此为物点对应的像点坐标
//将⾓点坐标存⼊imagePoints中,此为像点坐标
//imshow("calibration-demo", image_r);
waitKey(500);
}
if(ret1&&ret2)
{
image_rightPoints.push_back(corners_r);
image_leftPoints.push_back(corners_l);//将⾓点坐标存⼊imagePoints中,此为像点坐标
objectPoints.push_back(obj);
}
}
//计算内参与畸变系数
Mat intrinsic_left =Mat(3,3, CV_32FC1);
Mat distCoeffs_left;//畸变矩阵
vector<Mat> rvecs_l;//旋转向量R
vector<Mat> tvecs_l;//平移向量T
//内参矩阵
intrinsic_left.ptr<float>(0)[0]=1;
intrinsic_left.ptr<float>(1)[1]=1;
calibrateCamera(objectPoints, image_leftPoints, s1, intrinsic_left, distCoeffs_left, rvecs_l, tvecs_l);
//计算内参与畸变系数
Mat intrinsic_right =Mat(3,3, CV_32FC1);
Mat distCoeffs_right;//畸变矩阵
vector<Mat> rvecs_r;//旋转向量R
vector<Mat> tvecs_r;//平移向量T
Mat R_total;//旋转向量R
Vec3d T_total;//平移向量T
Mat E ;//本质矩阵
Mat F ;//基本矩阵
intrinsic_right.ptr<float>(0)[0]=1;
intrinsic_right.ptr<float>(1)[1]=1;
calibrateCamera(objectPoints, image_rightPoints, s2, intrinsic_right, distCoeffs_right, rvecs_r, tvecs_r);
FileStorage fs("C:\\Users\\Administrator\\Desktop\\claibration——list\\双⽬参数\\l", FileStorage::WRITE);//存储标定结果,这⾥可以选择⾃⼰的存放路径
fs <<"intrinsic_left"<< intrinsic_left;//存放内参矩阵
fs <<"distCoeffs_left"<< distCoeffs_left;//存放畸变矩阵
fs <<"board_width"<<11;//存放标定板长度信息
fs <<"board_height"<<8;//存放标定板宽度信息
fs <<"square_size"<<0.015;//存放标定板格⼦尺⼨信息
fs <<"R_left"<< rvecs_l;//R
fs <<"T_left"<< tvecs_l;//T
fs <<"intrinsic_right"<< intrinsic_right;//存放内参矩阵
fs <<"distCoeffs_right"<< distCoeffs_right;//存放畸变矩阵
fs <<"R_right"<< rvecs_r;//R
fs <<"T_right"<< tvecs_r;//T
printf("Done Calibration\n");
Mat R_L;//由R_total拆分的左相机旋转量
Mat R_R;//由R_total拆分的右相机旋转量
Mat P1;
Mat P2;
Mat Q;
Rect validROIL, validROIR;
printf("Starting Rectification\n");
stereoRectify(intrinsic_left, distCoeffs_left, intrinsic_right, distCoeffs_right, s1, R_total, T_total, R_L, R_R, P1, P2, Q, CALIB_ZERO_DISPARITY,-1, s1,&v alidROIL,&validROIR);
fs <<"R_left"<< R_L;//存放内参矩阵
fs <<"R_right"<< R_R;//存放畸变矩阵
fs <<"P1"<< P1;//存放标定板长度信息
fs <<"P2"<< P2;//存放标定板宽度信息
fs <<"Q"<< Q;//存放标定板格⼦尺⼨信息
cout <<"left_Calibration error: "<<computeReprojectionErrors(objectPoints, image_leftPoints, rvecs_l, tvecs_l, intrinsic_left, distCoeffs_left)<< endl;//输出投影的误差
cout <<"right_Calibration error: "<<computeReprojectionErrors(objectPoints, image_rightPoints, rvecs_r, tvecs_r, intrinsic_right, distCoeffs_right)<< end l;//输出投影的误差
printf("Done Rectification\n");
//根据stereoRectify计算出来的R和P来计算图像的映射表 mapx,mapy
//mapx,mapy这两个映射表接下来可以给remap()函数调⽤,校正图像(使图像共勉并且⾏对准)
//ininUndistortRectifyMap()的参数newCameraMatrix就是校正后的相机矩阵,
Mat mapLx, mapLy, mapRx, mapRy;
initUndistortRectifyMap(intrinsic_left, distCoeffs_left, R_L, P1, s1, CV_16SC2, mapLx, mapLy);
initUndistortRectifyMap(intrinsic_right, distCoeffs_right, R_R, P2, s1, CV_16SC2, mapRx, mapRy);
/4.2.0/d9/d0c/group__calib3d.html#ga7dfb72c9cf9780a347fbe3d1c47e5d5a
printf("get map\n");
Mat rectifyImageL2, rectifyImageR2;
remap(image_l, rectifyImageL2, mapLx, mapLy, INTER_LINEAR);
remap(image_r, rectifyImageR2, mapRx, mapRy, INTER_LINEAR);
printf("rectify done\n");
//cout << "按Q2退出 ..." << endl;
imshow("rectifyImageL", rectifyImageL2);
imshow("rectifyImageR", rectifyImageR2);
waitKey(10000);
Mat canvas;
double sf;
int w, h;
sf =600./MAX(s1.width, s1.height);
w =cvRound(s1.width * sf);
h =cvRound(s1.height * sf);
/*左图像画到画布上*/
Mat canvasPart =canvas(Rect(w *0,0, w, h));
resize(rectifyImageL2, canvasPart, canvasPart.size(),0,0, INTER_AREA);//把图像缩放到跟canvasPart⼀样⼤⼩
Rect vroiL(cvRound(validROIL.x*sf),cvRound(validROIL.y*sf),//获得被截取的区域
cvRound(validROIL.width*sf),cvRound(validROIL.height*sf));
rectangle(canvasPart, vroiL,Scalar(0,0,255),3,8);//画上⼀个矩形
/*右图像画到画布上*/
canvasPart =canvas(Rect(w,0, w, h));//获得画布的另⼀部分
resize(rectifyImageR2, canvasPart, canvasPart.size(),0,0, INTER_LINEAR);
Rect vroiR(cvRound(validROIR.x * sf),cvRound(validROIR.y*sf),
cvRound(validROIR.width * sf),cvRound(validROIR.height * sf));
rectangle(canvasPart, vroiR,Scalar(0,255,0),3,8);
cout <<"Painted ImageR"<< endl;
/*画上对应的线条*/
for(int i =0; i < ws; i +=16)
line(canvas,Point(0, i),ls, i),Scalar(0,255,0),1,8);
imshow("rectified", canvas);
cout <<"wait key"<< endl;
waitKey(0);
return0;
}
//下⾯对源码实现流程分段讲解
//先定义⼀些变量,物点像点,图像路径,图像信息…
//将物点保存
for(int i =0; i < numCornersHor; i++)
rectangle函数opencvfor(int j =0; j < numCornersVer; j++)
obj.push_back(Point3f((float)j * numSquares,(float)i * numSquares,0));
//我在遍历像点与物点的时候,出现了如下错误:
OpenCV(4.2.0) Error: Unspecified error (> Number of object and image points must be equal (expected:
‘numberOfObjectPoints == numberOfImagePoints’)
原因是:物点,与提取的数量⾓点不匹配,之前我将提取物点的for循环放到提取像点的for循环中,导致不匹配,
解决⽅法:物点与像点提取流程分开编写
//保存像点
for(int i =0; i <16; i++){
printf("image file : %s \n", files_left[i].c_str());
image_l =imread(files_left[i]);
printf("image file : %s \n", files_right[i].c_str());
image_r =imread(files_right[i]);
s1 = image_l.size();
s2 = image_r.size();
cvtColor(image_l, gray_l, COLOR_BGR2GRAY);//转灰度
cvtColor(image_r, gray_r, COLOR_BGR2GRAY);//转灰度
vector<Point2f> corners_r;
vector<Point2f> corners_l;
bool ret1 =findChessboardCorners(gray_r,Size(11,8), corners_r, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FILTER_QUADS);//该函数的功能就是判断图像内是否包含完整的棋盘图,若能检测完全,就把他们的⾓点坐标(从上到下,从左到右)记录,并返回true,否则为false,CALIB_CB_FILTER
_QUADS⽤于去除检测到的错误⽅块。
bool ret2 =findChessboardCorners(gray_l,Size(11,8), corners_l, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FILTER_QUADS);
if(ret1)
{
cornerSubPix(gray_l, corners_l,Size(11,11),Size(-1,-1), criteria);//⽤于发现亚像素精度的⾓点位置
drawChessboardCorners(image_l,Size(11,8), corners_l, ret1);//将每⼀个⾓点出做标记,此为物点对应的像点坐标
//imshow("calibration-demo1", image_l);
waitKey(500);
}
if(ret2)
{
cornerSubPix(gray_r, corners_r,Size(11,11),Size(-1,-1), criteria);//⽤于发现亚像素精度的⾓点位置
drawChessboardCorners(image_r,Size(11,8), corners_r, ret2);//将每⼀个⾓点出做标记,此为物点对应的像点坐标
//将⾓点坐标存⼊imagePoints中,此为像点坐标
//imshow("calibration-demo", image_r);
waitKey(500);
}
if(ret1&&ret2)
{
image_rightPoints.push_back(corners_r);
image_leftPoints.push_back(corners_l);//将⾓点坐标存⼊imagePoints中,此为像点坐标
objectPoints.push_back(obj);
}
}

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。