双目摄像头测距——用matlab标定摄像头参数

使用官方标定板(棋盘),测量单个棋盘边长
ps:棋盘一定要固定在一个平面上,不要弯曲卷折。
棋盘图片下载网址:http://docs.opencv.org/2.4/_downloads/pattern.png

1. matlab命令行调用stereoCameraCalibrator工具

2. 添加图片(分成两个文件夹:左和右,按数字编号)


然后按测量单个棋盘边长选择尺寸
image
可能会出现如下界面,表示符合条件的图片数量
image

3. 点击Calibrate

image
运行完成后删除误差较大的数据(0.3以下最佳)
删除方法:左键或右键点击柱状图会跳转到照片,右键remove选中照片

4. 点击后默认选项导出参数

image

5. 数据:

1.平移参数:stereoParams.PoseCamera2.Translation

2.旋转参数:(转置)stereoParams.PoseCamera2.R(RotationOfCamera2)

3.左、右两个摄像头各自的内部参数:(转置)
需matlab命令行输入代码:
% 假设 stereoParams 是导出的标定结果
leftIntrinsicMatrix = stereoParams.CameraParameters1.IntrinsicMatrix;
rightIntrinsicMatrix = stereoParams.CameraParameters2.IntrinsicMatrix;

% 显示内参矩阵
disp('Left Camera Intrinsic Matrix:');
disp(leftIntrinsicMatrix);

disp('Right Camera Intrinsic Matrix:');
disp(rightIntrinsicMatrix);

👆链接:https://blog.csdn.net/2201_75283572/article/details/144636981?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522043dc2f47b55fe83cb95d15937dbee6d%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=043dc2f47b55fe83cb95d15937dbee6d&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-1-144636981-null-null.142v102pc_search_result_base5&utm_term=IntrinsicMatrix&spm=1018.2226.3001.4187

4.径向畸变 k1 k2 : stereoParams.CameraParameters1.RadialDistortion
5.切向畸变 p1 p2 : stereoParams.CameraParameters1.TangentialDistortion
k1 k2 p1 p2 k3 (k3=0)

源码:(C++环境)

/******************************/

/* 立体匹配和测距 */

/******************************/

include <opencv2/opencv.hpp>

include <iostream>

include <math.h>

using namespace std;

using namespace cv;

const int imageWidth = 640; //摄像头的分辨率

const int imageHeight = 360;

Vec3f point3;

float d;

Size imageSize = Size(imageWidth, imageHeight);

Mat rgbImageL, grayImageL;

Mat rgbImageR, grayImageR;

Mat rectifyImageL, rectifyImageR;

Rect validROIL;//图像校正之后,会对图像进行裁剪,这里的validROI就是指裁剪之后的区域

Rect validROIR;

Mat mapLx, mapLy, mapRx, mapRy; //映射表

Mat Rl, Rr, Pl, Pr, Q; //校正旋转矩阵R,投影矩阵P 重投影矩阵Q

Mat xyz; //三维坐标

Point origin; //鼠标按下的起始点

Rect selection; //定义矩形选框

bool selectObject = false; //是否选择对象

int blockSize = 0, uniquenessRatio = 0, numDisparities = 0;

Ptr<StereoBM> bm = StereoBM::create(16, 9);

/*事先标定好的左相机的内参矩阵

fx 0 cx

0 fy cy

0 0 1

*/

Mat cameraMatrixL = (Mat_<double>(3, 3) << 485.933782310715, -0.292687497648472, 292.720069591521,

0, 485.273502858935, 214.517060356564,

0, 0, 1);

//获得的畸变参数 左相机畸变参数

/*418.523322187048 0 0

-1.26842201390676 421.222568242056 0

344.758267538961 243.318992284899 1 */ //2

Mat distCoeffL = (Mat_<double>(5, 1) << 0.127909393166272, -0.144506182077827, -0.00170475290291542, -0.000682767885032995, 0);

//[0.006636837611004,0.050240447649195] [0.006681263320267,0.003130367429418] //左相机K1,K2,P1,P2,K3

/*事先标定好的右相机的内参矩阵

fx 0 cx

0 fy cy

0 0 1

*/

Mat cameraMatrixR = (Mat_<double>(3, 3) << 489.689437035131, -0.582092467855873, 299.695314139699,

0, 489.272059352339, 212.644573601761,

0, 0, 1); //右相机畸变参数

/*

417.417985082506 0 0

0.498638151824367 419.795432389420 0

309.903372309072 236.256106972796 1

*/ //2

Mat distCoeffR = (Mat_<double>(5, 1) << 0.100993814523132, -0.0586344661784253, -0.00121091192337629, -0.00108736536874651, 0);

//[-0.038407383078874,0.236392800301615] [0.004121779274885,0.002296129959664]//右相机K1,K2,P1,P2,K3

Mat T = (Mat_<double>(3, 1) << -45.6655434838876, 0.0452737841151481, 0.422970847106132);//T平移向量

																						  //[-1.210187345641146e+02,0.519235426836325,-0.425535566316217]

																						  //对应Matlab所得T参数  TranslationOfCamera2

																						  //Mat rec = (Mat_&lt;double&gt;(3, 1) &lt;&lt; -0.00306, -0.03207, 0.00206);//rec旋转向量,对应matlab om参数  我 

Mat rec = (Mat_<double>(3, 3) << 0.999999575725951, 0.000388484222917634, 0.000835241238359436,

-0.000388657271549596, 0.999999903041699, 0.000207031685706618,

-0.000835160728832328, -0.000207356220449212, 0.999999629754909);                //rec旋转向量,对应matlab om参数  我 

																				//RotationOfCamera2

																			 /* 0.999341122700880    0.000660748031451783    -0.0362888948713456

																			 -0.00206388651740061    0.999250989651683   -0.0386419468010579

																			 0.0362361815232777  0.0386913826603732  0.998593969567432 */


																			 //Mat T = (Mat_&lt;double&gt;(3, 1) &lt;&lt; -48.4, 0.241, -0.0344);//T平移向量

																			 //[-1.210187345641146e+02,0.519235426836325,-0.425535566316217]

																			 //对应Matlab所得T参数

																			 //Mat rec = (Mat_&lt;double&gt;(3, 1) &lt;&lt; -0.039, -0.04658, 0.00106);//rec旋转向量,对应matlab om参数   倬华

Mat R;//R 旋转矩阵

  /*****立体匹配*****/

void stereo_match(int, void*)

{

bm-&gt;setBlockSize(2 * blockSize + 5);     //SAD窗口大小,5~21之间为宜

bm-&gt;setROI1(validROIL);

bm-&gt;setROI2(validROIR);

bm-&gt;setPreFilterCap(31);

bm-&gt;setMinDisparity(0);  //最小视差,默认值为0, 可以是负值,int型

bm-&gt;setNumDisparities(numDisparities * 16 + 16);//视差窗口,即最大视差值与最小视差值之差,窗口大小必须是16的整数倍,int型

bm-&gt;setTextureThreshold(10);

bm-&gt;setUniquenessRatio(uniquenessRatio);//uniquenessRatio主要可以防止误匹配

bm-&gt;setSpeckleWindowSize(100);

bm-&gt;setSpeckleRange(32);

bm-&gt;setDisp12MaxDiff(-1);

Mat disp, disp8;

bm-&gt;compute(rectifyImageL, rectifyImageR, disp);//输入图像必须为灰度图

disp.convertTo(disp8, CV_8U, 255 / ((numDisparities * 16 + 16)*16.));//计算出的视差是CV_16S格式

reprojectImageTo3D(disp, xyz, Q, true); //在实际求距离时,ReprojectTo3D出来的X / W, Y / W, Z / W都要乘以16(也就是W除以16),才能得到正确的三维坐标信息。

xyz = xyz * 16;

imshow(&#34;disparity&#34;, disp8);

}

/描述:鼠标操作回调/

static void onMouse(int event, int x, int y, int, void*)

{

if (selectObject)

{

	selection.x = MIN(x, origin.x);

	selection.y = MIN(y, origin.y);

	selection.width = std::abs(x - origin.x);

	selection.height = std::abs(y - origin.y);

}


switch (event)

{

case EVENT_LBUTTONDOWN:   //鼠标左按钮按下的事件

	origin = Point(x, y);

	selection = Rect(x, y, 0, 0);

	selectObject = true;

	//cout &lt;&lt; origin &lt;&lt; &#34;in world coordinate is: &#34; &lt;&lt; xyz.at&lt;Vec3f&gt;(origin) &lt;&lt; endl;

	point3 = xyz.at&lt;Vec3f&gt;(origin);

	point3[0];

	//cout &lt;&lt; &#34;point3[0]:&#34; &lt;&lt; point3[0] &lt;&lt; &#34;point3[1]:&#34; &lt;&lt; point3[1] &lt;&lt; &#34;point3[2]:&#34; &lt;&lt; point3[2]&lt;&lt;endl;

	cout &lt;&lt; &#34;世界坐标:&#34; &lt;&lt; endl;

	cout &lt;&lt; &#34;x: &#34; &lt;&lt; point3[0] &lt;&lt; &#34;  y: &#34; &lt;&lt; point3[1] &lt;&lt; &#34;  z: &#34; &lt;&lt; point3[2] &lt;&lt; endl;

	d = point3[0] * point3[0] + point3[1] * point3[1] + point3[2] * point3[2];

	d = sqrt(d);   //mm

				   // cout &lt;&lt; &#34;距离是:&#34; &lt;&lt; d &lt;&lt; &#34;mm&#34; &lt;&lt; endl;


	d = d / 10.0;   //cm

	cout &lt;&lt; &#34;距离是:&#34; &lt;&lt; d &lt;&lt; &#34;cm&#34; &lt;&lt; endl;


	// d = d/1000.0;   //m

	// cout &lt;&lt; &#34;距离是:&#34; &lt;&lt; d &lt;&lt; &#34;m&#34; &lt;&lt; endl;


	break;

case EVENT_LBUTTONUP:    //鼠标左按钮释放的事件

	selectObject = false;

	if (selection.width &gt; 0 &amp;&amp; selection.height &gt; 0)

		break;

}

}

/主函数/

int main()

{

/*

立体校正

*/

Rodrigues(rec, R); //Rodrigues变换

stereoRectify(cameraMatrixL, distCoeffL, cameraMatrixR, distCoeffR, imageSize, R, T, Rl, Rr, Pl, Pr, Q, CALIB_ZERO_DISPARITY,

	0, imageSize, &amp;validROIL, &amp;validROIR);

initUndistortRectifyMap(cameraMatrixL, distCoeffL, Rl, Pl, imageSize, CV_32FC1, mapLx, mapLy);

initUndistortRectifyMap(cameraMatrixR, distCoeffR, Rr, Pr, imageSize, CV_32FC1, mapRx, mapRy);


/*

打开摄像头

*/

VideoCapture cap;


cap.open(0);                             //打开相机,电脑自带摄像头一般编号为0,外接摄像头编号为1,主要是在设备管理器中查看自己摄像头的编号。


cap.set(CV_CAP_PROP_FRAME_WIDTH, 2560);  //设置捕获视频的宽度

cap.set(CV_CAP_PROP_FRAME_HEIGHT, 720);  //设置捕获视频的高度


if (!cap.isOpened())                         //判断是否成功打开相机


{


	cout &lt;&lt; &#34;摄像头打开失败!&#34; &lt;&lt; endl;


	return -1;


}


Mat frame, frame_L, frame_R;

cap &gt;&gt; frame;                                //从相机捕获一帧图像


cout &lt;&lt; &#34;Painted ImageL&#34; &lt;&lt; endl;

cout &lt;&lt; &#34;Painted ImageR&#34; &lt;&lt; endl;


while (1) {


	double fScale = 0.5;                         //定义缩放系数,对2560*720图像进行缩放显示(2560*720图像过大,液晶屏分辨率较小时,需要缩放才可完整显示在屏幕)  


	Size dsize = Size(frame.cols*fScale, frame.rows*fScale);

	Mat imagedst = Mat(dsize, CV_32S);


	resize(frame, imagedst, dsize);

	char image_left[200];

	char image_right[200];

	frame_L = imagedst(Rect(0, 0, 640, 360));  //获取缩放后左Camera的图像

											   //  namedWindow(&#34;Video_L&#34;, 1);

											   //  imshow(&#34;Video_L&#34;, frame_L);


	frame_R = imagedst(Rect(640, 0, 640, 360)); //获取缩放后右Camera的图像

												//      namedWindow(&#34;Video_R&#34;, 2);

												//          imshow(&#34;Video_R&#34;, frame_R);

	cap &gt;&gt; frame;

	/*

	读取图片

	*/

	//rgbImageL = imread(&#34;image_left_1.jpg&#34;, CV_LOAD_IMAGE_COLOR);

	cvtColor(frame_L, grayImageL, CV_BGR2GRAY);

	//rgbImageR = imread(&#34;image_right_1.jpg&#34;, CV_LOAD_IMAGE_COLOR);

	cvtColor(frame_R, grayImageR, CV_BGR2GRAY);


	//  imshow(&#34;ImageL Before Rectify&#34;, grayImageL);

	//  imshow(&#34;ImageR Before Rectify&#34;, grayImageR);


	/*

	经过remap之后,左右相机的图像已经共面并且行对准了

	*/

	remap(grayImageL, rectifyImageL, mapLx, mapLy, INTER_LINEAR);

	remap(grayImageR, rectifyImageR, mapRx, mapRy, INTER_LINEAR);


	/*

	把校正结果显示出来

	*/

	Mat rgbRectifyImageL, rgbRectifyImageR;

	cvtColor(rectifyImageL, rgbRectifyImageL, CV_GRAY2BGR);  //伪彩色图

	cvtColor(rectifyImageR, rgbRectifyImageR, CV_GRAY2BGR);


	//单独显示

	//rectangle(rgbRectifyImageL, validROIL, Scalar(0, 0, 255), 3, 8);

	//rectangle(rgbRectifyImageR, validROIR, Scalar(0, 0, 255), 3, 8);

	//  imshow(&#34;ImageL After Rectify&#34;, rgbRectifyImageL);

	//  imshow(&#34;ImageR After Rectify&#34;, rgbRectifyImageR);


	//显示在同一张图上

	Mat canvas;

	double sf;

	int w, h;

	sf = 600. / MAX(imageSize.width, imageSize.height);

	w = cvRound(imageSize.width * sf);

	h = cvRound(imageSize.height * sf);

	canvas.create(h, w * 2, CV_8UC3);   //注意通道


										//左图像画到画布上

	Mat canvasPart = canvas(Rect(w * 0, 0, w, h));                                //得到画布的一部分  

	resize(rgbRectifyImageL, 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);                      //画上一个矩形  

	//  cout &lt;&lt; &#34;Painted ImageL&#34; &lt;&lt; endl;


	//右图像画到画布上

	canvasPart = canvas(Rect(w, 0, w, h));                                      //获得画布的另一部分  

	resize(rgbRectifyImageR, 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, 0, 255), 3, 8);

	//  cout &lt;&lt; &#34;Painted ImageR&#34; &lt;&lt; endl;


	//画上对应的线条

	for (int i = 0; i &lt; canvas.rows; i += 16)

		line(canvas, Point(0, i), Point(canvas.cols, i), Scalar(0, 255, 0), 1, 8);

	imshow(&#34;rectified&#34;, canvas);


	/*

	立体匹配

	*/

	namedWindow(&#34;disparity&#34;, CV_WINDOW_AUTOSIZE);

	// 创建SAD窗口 Trackbar

	createTrackbar(&#34;BlockSize:\n&#34;, &#34;disparity&#34;, &amp;blockSize, 8, stereo_match);

	// 创建视差唯一性百分比窗口 Trackbar

	createTrackbar(&#34;UniquenessRatio:\n&#34;, &#34;disparity&#34;, &amp;uniquenessRatio, 50, stereo_match);

	// 创建视差窗口 Trackbar

	createTrackbar(&#34;NumDisparities:\n&#34;, &#34;disparity&#34;, &amp;numDisparities, 16, stereo_match);

	//鼠标响应函数setMouseCallback(窗口名称, 鼠标回调函数, 传给回调函数的参数,一般取0)

	setMouseCallback(&#34;disparity&#34;, onMouse, 0);

	stereo_match(0, 0);


	waitKey(10);


} //wheil

return 0;

}

posted @ 2025-05-17 23:38  zhuosen  阅读(151)  评论(0)    收藏  举报