精华内容
下载资源
问答
  • 双目视觉

    千次阅读 2018-05-15 18:37:25
    双目视觉简介双目视觉广泛应用在机器人导航,精密工业测量、物体识别、虚拟现实、场景重建,勘测领域。什么是双目视觉双目视觉是模拟人类视觉原理,使用计算机被动感知距离的方法。从两个或者多个点观察一个物体...

    转载自:http://blog.sina.com.cn/s/blog_4a540be60102v44s.html


    1. 双目视觉算法简介


    1.1. 双目视觉简介

    双目视觉广泛应用在机器人导航,精密工业测量、物体识别、虚拟现实、场景重建,勘测领域。

    什么是双目视觉?

    双目视觉是模拟人类视觉原理,使用计算机被动感知距离的方法。从两个或者多个点观察一个物体,获取在不同视角下的图像,根据图像之间像素的匹配关系,通过三角测量原理计算出像素之间的偏移来获取物体的三维信息。

    得到了物体的景深信息,就可以计算出物体与相机之间的实际距离,物体3维大小,两点之间实际距离。目前也有很多研究机构进行3维物体识别,来解决2D算法无法处理遮挡,姿态变化的问题,提高物体的识别率。

    1.2. 算法一般流程

                                               图1 双目视觉算法流程

    1.2.1. 离线标定

    标定的目的是获取相机的内参(焦距,图像中心,畸变系数等)和外参(R(旋转)矩阵T(平移)矩阵,用于对其两个相机)。目前比较常用的方法为张正友的棋盘格标定方法,Opencv和Matlab上均有实现。但是一般为了获取更高的标定精度,采用工业级的(60*60格子)玻璃面板效果会更好。并且有人也建议使用Matlab,因为精度包括可视化效果会更好一些,并且Matlab的结果保存为xml,Opencv也可以直接读入,但是步骤相对于Opencv的麻烦了一些。这是Matlab标定工具箱的连接:http://www.vision.caltech.edu/bouguetj/calib_doc/

    步骤为:

    (1)左摄像头标定,获取内外参数。

    (2)右参数摄像头标定获取外参。

    (3) 双目标定,获取相机之间的平移旋转关系。


    图2  Matlab双目视觉标定

     

    1.2.2. 双目矫正

    矫正的目的是得到的参考图与目标图之间,只存在X方向上的差异。提高视差计算的准确性。

    矫正分为两个步骤

    l  畸变矫正

     


    图3 畸变矫正

    l  将相机转化为标准形式

    图4 转换为标准形式

    因为矫正部分,会对图像所有点的位置进行重新计算,因而算法处理的分辨率越大耗时越大,而且一般需要实时处理两张图像。而且这种算法并行化强标准化程度较高,建议使用IVE进行硬化,类似Opencv中的加速模式,先得到映射Map,再并行化使用映射Map重新得到像素位置。Opencv中的矫正函数为cvStereoRectify。

    1.2.3. 双目匹配

    双目匹配是双目深度估计的核心部分,发展了很多年,也有非常多的算法,主要目的是计算参考图与目标图之间像素的相对匹配关系,主要分为局部和非局部的算法。一般有下面几个步骤。

    l  匹配误差计算

    l  误差集成

    l  视差图计算/优化

    l  视差图矫正

    一般局部算法,使用固定大小或者非固定大小窗口,计算与之所在一行的最优匹配位置。如下图为最简单的局部方式,求一行最佳对应点位置,左右视图X坐标位置差异为视差图。为了增加噪声,光照的鲁棒性可以使用固定窗口进行匹配,也可以对图像使用LBP变换之后再进行匹配。一般的匹配损失计算函数有:SAD,SSD,NCC等。一般采用最大视差也可以限制最大搜索范围,也可以使用积分图和Box Filter进行加速计算。目前效果较好的局部匹配算法为基于Guided Filter的使用Box Filter和积分图的双目匹配算法,局部算法易于并行化,计算速度快,但是对于纹理较少的区域效果不佳,一般对图像分割,将图像分为纹理丰富和纹理稀疏的区域,调整匹配窗大小,纹理稀疏使用小窗口,来提高匹配效果。


    图5 基于线性搜索的视差计算

    非局部的匹配算法,将搜索视差的任务看做最小化一个确定的基于全部双目匹配对的损失函数,求该损失函数的最小值即可得到最佳的视差关系,着重解决图像中不确定区域的匹配问题,主要有动态规划(Dynamic Programming),信任传播(Blief Propagation),图割算法(Graph Cut)。目前效果最好的也是图割算法,Opencv中提供的图割算法匹配耗时很大。

    图割算法主要是为了解决动态规划算法不能融合水平和竖直方向连续性约束的问题,将匹配问题看成是利用这些约束在图像中寻求最小割问题。

    图6 基于图割(左),动态规划(中),相关性计算(右)的效果。

    因为考虑到全局能量最小化,非局部算法一般耗时较大,不太好使用硬件加速。但是对于遮挡,纹理稀疏的情况解决的较好。

    得到了匹配点之后,一般通过左右视线一致性的方式,检测和确定具有高置信度的匹配点。很类似前后向光流匹配的思想,只有通过左右视线一致性检验的点才认为是稳定匹配点。这样也可以找出因为遮挡,噪声,误匹配得到的点。

    那么我们如何得到视差和深度信息呢?

    关于视差图的后处理,一般采用中值滤波的方法,对当前点的灰度值使用邻域像素的中值来代替,这种方法可以很好去除椒盐噪声。可以去除因为噪声或者弱纹理匹配失败的孤立点。
    展开全文
  • 双目视觉检测

    2018-03-11 20:42:02
    双目视觉检测 特征点匹配 基于双目摄像头
  • 双目视觉成像原理1.引言双目立体视觉(BinocularStereoVision)是机器视觉的一种重要形式,它是基于视差原理并利用成像设备从不同的位置获取被测物体的两幅图像,通过计算图像对应点间的位置偏差,来获取物体三维几何...

    双目视觉成像原理

    1.

    引言

    双目立体视觉

    (Binocular

    Stereo

    Vision)

    是机器视觉的一种重要形式,它是基于视差原理

    并利用成像设备从不同的位置获取被测物体的两幅图像,通过计算图像对应点间的位置偏

    差,

    来获取物体三维几何信息的方法。

    融合两只眼睛获得的图像并观察它们之间的差别,

    使

    我们可以获得明显的深度感,

    建立特征间的对应关系,

    将同一空间物理点在不同图像中的映

    像点对应起来,这个差别,我们称作视差

    (Disparity)

    图。

    双目立体视觉测量方法具有效率高、

    精度合适、系统结构简单、成本低等优点,非常适

    合于制造现场的在线、非接触产品检测和质量控制。对运动物体(包括动物和人体形体)测

    量中,

    由于图像获取是在瞬间完成的,

    因此立体视觉方法是一种更有效的测量方法。

    双目立

    体视觉系统是计算机视觉的关键技术之一,

    获取空间三维场景的距离信息也是计算机视觉研

    究中最基础的内容。

    2.

    双目立体视觉系统

    立体视觉系统由左右两部摄像机组成。如图一所示,图中分别以下标

    L

    r

    标注左、右

    摄像机的相应参数。世界空间中一点

    A(X

    Y

    Z)

    在左右摄像机的成像面

    C

    L

    C

    R

    上的像点分

    别为

    al(ul

    vl)

    ar(ur

    vr)

    这两个像点是世界空间中同一个对象点

    A

    的像,

    称为

    “共轭点”

    知道了这两个共轭像点,分别作它们与各自相机的光心

    Ol

    Or

    的连线,即投影线

    alOl

    arOr

    ,它们的交点即为世界空间中的对象点

    A(X

    Y

    Z)

    。这就是立体视觉的基本原理。

    展开全文
  • 双目视觉ppt

    2018-12-11 14:57:18
    双目视觉ppt,写的很不错,在找一份代码结合看会比较好
  • 双目立体视觉是一门有着广阔应用前景的学科,根据双目立体视觉CCAS提供的思路及组成原理,随着光学、电子...双目视觉三维定位原理双目立体视觉三维测量是基于视差原理。其中基线距B=两摄像机的投影中心连线的距离;...

    双目立体视觉是一门有着广阔应用前景的学科,根据双目立体视觉CCAS提供的思路及组成原理,随着光学、电子学以及计算机技术的发展,将不断进步,逐渐实用化,不仅将成为工业检测、生物医学、虚拟现实等领域的关键技术,还有可能应用于航天遥测、军事侦察等领域。目前在国外,双目立体视觉技术已广泛应用于生产、生活中。

    双目视觉三维定位原理

    双目立体视觉三维测量是基于视差原理。

    其中基线距B=两摄像机的投影中心连线的距离;相机焦距为f。设两摄像机在同一时刻观看空间物体的同一特征点(xc,yc,zc),分别在“左眼”和“右眼”上获取了点P的图像,它们的图像坐标分别为pleft=(Xleft,Yleft),pright=(Xright,Yright)。

    现两摄像机的图像在同一个平面上,则特征点P的图像坐标Y坐标相同,即Yleft=Yright=Y,则由三角几何关系得到:

    则视差为:leftrightDisparityXX。由此可计算出特征点P在相机坐标系下的三维坐标为:

    因此,左相机像面上的任意一点只要能在右相机像面上找到对应的匹配点,就可以确定出该点的三维坐标。这种方法是完全的点对点运算,像面上所有点只要存在相应的匹配点,就可以参与上述运算,从而获取其对应的三维坐标。

    双目立体视觉的三维测量方法

    双目立体测量的特点类似于“双目导航、定位”,一般来说:第一、需要提取的点不多,仅把被测物需要测量部分的点找到即可;第二、速度要求较高,一般要求一秒中测量多个产品;第三、对光照环境要求高。因为测量的时候相机和被测物必然有相对位移,所以拍摄到的图像可能各个角度都会有。一般的实现步骤如下:

    第一、相机参数标定。这部分前文已经说过,不再细说了,有兴趣的可以去看CCAS双目立体视觉的相关资料。这部分的主要目的:获取相机的内参数和镜头畸变系数、获取双相机在当前角度下立体参数。

    第二、对被测物进行拍摄,并获取被测部分的特征点。这是双目立体视觉测量最重要的部分。主要难点在于该用哪些算法来获取特征点,并且这种特征点的获取方式上,不能用打激光点或手动贴特征点这样的方式来“取巧”,必须尊重原图。根据CCAS提供的一些图像预处理算法进行实验后发现,一般提取特征点时需要用到数十种以上的预处理算法,而这些算法中的参数需要不断的去实验。由于CCAS提供二次开发库,所以在一些极端情况下用户还可以在其中加入一些其他的算法。其最终目的还是把特征点准确的找到。

    第三、三维坐标获取。完成以上步骤后,就可以进行立体匹配并计算三维坐标来。这部分比较简单,只要给出来数学模型了直接带入公式即可。CCAS提供了相应的算法模型和和例程。

    第四、指定测量范围并输出结果。

    展开全文
  • 双目视觉资料整理

    2018-07-24 12:37:02
    双目视觉在机器视觉应用中较为普遍,个人整理的关于双目视觉的一些资料。
  • 双目视觉资源

    2018-03-20 15:48:54
    本文档记录了双目视觉的一般步骤与资源,可以为开发人员提供理论指导与支持
  • opencv实现双目视觉测距

    万次阅读 多人点赞 2017-12-26 11:47:21
    最近一直在研究双目视觉测距,资料真的特别多网上,有matlab 的,python的,C++的,但个人感觉都不详细,对于小白,特别不容易上手,在这里我提供一个傻瓜式教程吧,利用matlab来进行标注,图形界面,无须任何代码,...

    有个群193369905,相关毕设也可找群主,最近一直在研究双目视觉测距,资料真的特别多网上,有matlab 的,python的,C++的,但个人感觉都不详细,对于小白,特别不容易上手,在这里我提供一个傻瓜式教程吧,利用matlab来进行标注,图形界面,无须任何代码,然后利用C++实现测距与深度图,原理太多我就不提了,小白直接照做就OK
    1、准备工作
    硬件准备
    https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-17093912817.2.6af681c0jaZTur&id=562773790704
    摄像头一个(如图),淘宝连接
    这里写图片描述
    软件准备
    VS+opencv3.1
    Matlab+toolbox标定工具箱
    C++代码
    Vs+opencv配置各位见这篇博客 https://www.cnblogs.com/linshuhe/p/5764394.html,讲解的够详细了,我们需要用VS+opencv3.1实现实时测距

    2matlab标定****

    matlab用于单目摄像头,双目摄像头的标定,这个我也已经做过了,各位直接参考这篇博客 http://blog.csdn.net/hyacinthkiss/article/details/41317087
    各位准备好上面那些工具以后我们才能正式开始
    我们通过matlab标定可以获得以下数据

    这里写图片描述

    或者使用C++ 进行单双目标定

    单目标定

    #include <iostream>
    #include <sstream>
    #include <time.h>
    #include <stdio.h>
    #include <fstream>
    
    #include <opencv2/core/core.hpp>
    #include <opencv2/imgproc/imgproc.hpp>
    #include <opencv2/calib3d/calib3d.hpp>
    #include <opencv2/highgui/highgui.hpp>
    
    using namespace cv;
    using namespace std;
    #define calibration
    
    int main()
    {
    #ifdef calibration
    
    	ifstream fin("right_img.txt");             /* 标定所用图像文件的路径 */
    	ofstream fout("caliberation_result_right.txt");  /* 保存标定结果的文件 */
    
    	// 读取每一幅图像,从中提取出角点,然后对角点进行亚像素精确化
    	int image_count = 0;  /* 图像数量 */
    	Size image_size;      /* 图像的尺寸 */
    	Size board_size = Size(11,8);             /* 标定板上每行、列的角点数 */
    	vector<Point2f> image_points_buf;         /* 缓存每幅图像上检测到的角点 */
    	vector<vector<Point2f>> image_points_seq; /* 保存检测到的所有角点 */
    	string filename;      // 图片名
    	vector<string> filenames;
    
    	while (getline(fin, filename))
    	{
    		++image_count;
    		Mat imageInput = imread(filename);
    		filenames.push_back(filename);
    
    		// 读入第一张图片时获取图片大小
    		if (image_count == 1)
    		{
    			image_size.width = imageInput.cols;
    			image_size.height = imageInput.rows;
    		}
    
    		/* 提取角点 */
    		if (0 == findChessboardCorners(imageInput, board_size, image_points_buf))
    		{
    			//cout << "can not find chessboard corners!\n";  // 找不到角点
    			cout << "**" << filename << "** can not find chessboard corners!\n";
    			exit(1);
    		}
    		else
    		{
    			Mat view_gray;
    			cvtColor(imageInput, view_gray, CV_RGB2GRAY);  // 转灰度图
    
    			/* 亚像素精确化 */
    			// image_points_buf 初始的角点坐标向量,同时作为亚像素坐标位置的输出
    			// Size(5,5) 搜索窗口大小
    			// (-1,-1)表示没有死区
    			// TermCriteria 角点的迭代过程的终止条件, 可以为迭代次数和角点精度两者的组合
    			cornerSubPix(view_gray, image_points_buf, Size(5, 5), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
    
    			image_points_seq.push_back(image_points_buf);  // 保存亚像素角点
    
    			/* 在图像上显示角点位置 */
    			drawChessboardCorners(view_gray, board_size, image_points_buf, false); // 用于在图片中标记角点
    
    			imshow("Camera Calibration", view_gray);       // 显示图片
    
    			waitKey(500); //暂停0.5S      
    		}
    	}
    	int CornerNum = board_size.width * board_size.height;  // 每张图片上总的角点数
    
    	//-------------以下是摄像机标定------------------
    
    	/*棋盘三维信息*/
    	Size square_size = Size(60, 60);         /* 实际测量得到的标定板上每个棋盘格的大小 */
    	vector<vector<Point3f>> object_points;   /* 保存标定板上角点的三维坐标 */
    
    	/*内外参数*/
    	Mat cameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0));  /* 摄像机内参数矩阵 */
    	vector<int> point_counts;   // 每幅图像中角点的数量
    	Mat distCoeffs = Mat(1, 5, CV_32FC1, Scalar::all(0));       /* 摄像机的5个畸变系数:k1,k2,p1,p2,k3 */
    	vector<Mat> tvecsMat;      /* 每幅图像的旋转向量 */
    	vector<Mat> rvecsMat;      /* 每幅图像的平移向量 */
    
    	/* 初始化标定板上角点的三维坐标 */
    	int i, j, t;
    	for (t = 0; t<image_count; t++)
    	{
    		vector<Point3f> tempPointSet;
    		for (i = 0; i<board_size.height; i++)
    		{
    			for (j = 0; j<board_size.width; j++)
    			{
    				Point3f realPoint;
    
    				/* 假设标定板放在世界坐标系中z=0的平面上 */
    				realPoint.x = i * square_size.width;
    				realPoint.y = j * square_size.height;
    				realPoint.z = 0;
    				tempPointSet.push_back(realPoint);
    			}
    		}
    		object_points.push_back(tempPointSet);
    	}
    
    	/* 初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板 */
    	for (i = 0; i<image_count; i++)
    	{
    		point_counts.push_back(board_size.width * board_size.height);
    	}
    
    	/* 开始标定 */
    	// object_points 世界坐标系中的角点的三维坐标
    	// image_points_seq 每一个内角点对应的图像坐标点
    	// image_size 图像的像素尺寸大小
    	// cameraMatrix 输出,内参矩阵
    	// distCoeffs 输出,畸变系数
    	// rvecsMat 输出,旋转向量
    	// tvecsMat 输出,位移向量
    	// 0 标定时所采用的算法
    	calibrateCamera(object_points, image_points_seq, image_size, cameraMatrix, distCoeffs, rvecsMat, tvecsMat, 0);
    
    	//------------------------标定完成------------------------------------
    
    	// -------------------对标定结果进行评价------------------------------
    
    	double total_err = 0.0;         /* 所有图像的平均误差的总和 */
    	double err = 0.0;               /* 每幅图像的平均误差 */
    	vector<Point2f> image_points2;  /* 保存重新计算得到的投影点 */
    	fout << "每幅图像的标定误差:\n";
    
    	for (i = 0; i<image_count; i++)
    	{
    		vector<Point3f> tempPointSet = object_points[i];
    
    		/* 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 */
    		projectPoints(tempPointSet, rvecsMat[i], tvecsMat[i], cameraMatrix, distCoeffs, image_points2);
    
    		/* 计算新的投影点和旧的投影点之间的误差*/
    		vector<Point2f> tempImagePoint = image_points_seq[i];
    		Mat tempImagePointMat = Mat(1, tempImagePoint.size(), CV_32FC2);
    		Mat image_points2Mat = Mat(1, image_points2.size(), CV_32FC2);
    
    		for (int j = 0; j < tempImagePoint.size(); j++)
    		{
    			image_points2Mat.at<Vec2f>(0, j) = Vec2f(image_points2[j].x, image_points2[j].y);
    			tempImagePointMat.at<Vec2f>(0, j) = Vec2f(tempImagePoint[j].x, tempImagePoint[j].y);
    		}
    		err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
    		total_err += err /= point_counts[i];
    		fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
    	}
    	fout << "总体平均误差:" << total_err / image_count << "像素" << endl << endl;
    
    	//-------------------------评价完成---------------------------------------------
    
    	//-----------------------保存定标结果------------------------------------------- 
    	Mat rotation_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0));  /* 保存每幅图像的旋转矩阵 */
    	fout << "相机内参数矩阵:" << endl;
    	fout << cameraMatrix << endl << endl;
    	fout << "畸变系数:\n";
    	fout << distCoeffs << endl << endl << endl;
    	for (int i = 0; i<image_count; i++)
    	{
    		fout << "第" << i + 1 << "幅图像的旋转向量:" << endl;
    		fout << tvecsMat[i] << endl;
    
    		/* 将旋转向量转换为相对应的旋转矩阵 */
    		Rodrigues(tvecsMat[i], rotation_matrix);
    		fout << "第" << i + 1 << "幅图像的旋转矩阵:" << endl;
    		fout << rotation_matrix << endl;
    		fout << "第" << i + 1 << "幅图像的平移向量:" << endl;
    		fout << rvecsMat[i] << endl << endl;
    	}
    	fout << endl;
    
    	//--------------------标定结果保存结束-------------------------------
    
    	//----------------------显示定标结果--------------------------------
    
    	Mat mapx = Mat(image_size, CV_32FC1);
    	Mat mapy = Mat(image_size, CV_32FC1);
    	Mat R = Mat::eye(3, 3, CV_32F);
    	string imageFileName;
    	std::stringstream StrStm;
    	for (int i = 0; i != image_count; i++)
    	{
    		initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, image_size, CV_32FC1, mapx, mapy);
    		Mat imageSource = imread(filenames[i]);
    		Mat newimage = imageSource.clone();
    		remap(imageSource, newimage, mapx, mapy, INTER_LINEAR);
    		StrStm.clear();
    		imageFileName.clear();
    		StrStm << i + 1;
    		StrStm >> imageFileName;
    		imageFileName += "_d.jpg";
    		imwrite(imageFileName, newimage);
    	}
    
    	fin.close();
    	fout.close();
    
    #else 
    		/// 读取一副图片,不改变图片本身的颜色类型(该读取方式为DOS运行模式)
    		Mat src = imread("F:\\lane_line_detection\\left_img\\1.jpg");
    		Mat distortion = src.clone();
    		Mat camera_matrix = Mat(3, 3, CV_32FC1);
    		Mat distortion_coefficients;
    
    
    		//导入相机内参和畸变系数矩阵
    		FileStorage file_storage("F:\\lane_line_detection\\left_img\\Intrinsic.xml", FileStorage::READ);
    		file_storage["CameraMatrix"] >> camera_matrix;
    		file_storage["Dist"] >> distortion_coefficients;
    		file_storage.release();
    
    		//矫正
    		cv::undistort(src, distortion, camera_matrix, distortion_coefficients);
    
    		cv::imshow("img", src);
    		cv::imshow("undistort", distortion);
    		cv::imwrite("undistort.jpg", distortion);
    
    		cv::waitKey(0);
    #endif // DEBUG
    	return 0;
    }
    
    
    

    双目标定

    //双目相机标定 
    #include <opencv2/core/core.hpp>
    #include <opencv2/imgproc/imgproc.hpp>
    #include <opencv2/calib3d/calib3d.hpp>
    #include <opencv2/highgui/highgui.hpp>
    
    #include <vector>
    #include <string>
    #include <algorithm>
    #include <iostream>
    #include <iterator>
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    
    #include <opencv2/opencv.hpp>
    //#include <cv.h>
    //#include <cv.hpp>
    
    using namespace std;
    using namespace cv;
    //摄像头的分辨率
    const int imageWidth = 640;
    const int imageHeight = 480;
    //横向的角点数目
    const int boardWidth = 11;
    //纵向的角点数目
    const int boardHeight = 8;
    //总的角点数目
    const int boardCorner = boardWidth * boardHeight;
    //相机标定时需要采用的图像帧数
    const int frameNumber = 8;
    //标定板黑白格子的大小 单位是mm
    const int squareSize = 60;
    //标定板的总内角点
    const Size boardSize = Size(boardWidth, boardHeight);
    Size imageSize = Size(imageWidth, imageHeight);
    
    Mat R, T, E, F;
    //R旋转矢量 T平移矢量 E本征矩阵 F基础矩阵
    vector<Mat> rvecs; //R
    vector<Mat> tvecs; //T
    //左边摄像机所有照片角点的坐标集合
    vector<vector<Point2f>> imagePointL;
    //右边摄像机所有照片角点的坐标集合
    vector<vector<Point2f>> imagePointR;
    //各图像的角点的实际的物理坐标集合
    vector<vector<Point3f>> objRealPoint;
    //左边摄像机某一照片角点坐标集合
    vector<Point2f> cornerL;
    //右边摄像机某一照片角点坐标集合
    vector<Point2f> cornerR;
    
    Mat rgbImageL, grayImageL;
    Mat rgbImageR, grayImageR;
    
    Mat intrinsic;
    Mat distortion_coeff;
    //校正旋转矩阵R,投影矩阵P,重投影矩阵Q
    Mat Rl, Rr, Pl, Pr, Q;
    //映射表
    Mat mapLx, mapLy, mapRx, mapRy;
    Rect validROIL, validROIR;
    //图像校正之后,会对图像进行裁剪,其中,validROI裁剪之后的区域
    /*事先标定好的左相机的内参矩阵
    fx 0 cx
    0 fy cy
    0  0  1
    */
    Mat cameraMatrixL = (Mat_<double>(3,3) << 271.7792785637638, 0, 313.4559554347688,
    	0, 271.9513066781816, 232.7561625477742,
    	0, 0, 1);
    //获得的畸变参数
    Mat distCoeffL = (Mat_<double>(5,1) << -0.3271838086967946, 0.1326861805365006, -0.0008527407221595511, -0.0003398213328658643, -0.02847446149341753);
    /*事先标定好的右相机的内参矩阵
    fx 0 cx
    0 fy cy
    0  0  1
    */
    Mat cameraMatrixR = (Mat_<double>(3,3) << 268.4990780091891, 0, 325.75156647688,
    	0, 269.7906504513069, 212.5928387210573,
    	0, 0, 1);
    Mat distCoeffR = (Mat_<double>(5,1) << -0.321298212260166, 0.1215100334221875, -0.0007504391036193558, -1.732473939234179e-05, -0.02234659175488724);
    
    /*计算标定板上模块的实际物理坐标*/
    void calRealPoint(vector<vector<Point3f>>& obj, int boardWidth, int boardHeight, int imgNumber, int squareSize)
    {
        vector<Point3f> imgpoint;
        for (int rowIndex = 0; rowIndex < boardHeight; rowIndex++)
        {
            for (int colIndex = 0; colIndex < boardWidth; colIndex++)
            {
                imgpoint.push_back(Point3f(rowIndex * squareSize, colIndex * squareSize, 0));
            }
        }
        for (int imgIndex = 0; imgIndex < imgNumber; imgIndex++)
        {
            obj.push_back(imgpoint);
        }
    }
    
    
    
    void outputCameraParam(void)
    {
    	/*保存数据*/
    	/*输出数据*/
    	FileStorage fs("intrisics.yml", FileStorage::WRITE);
    	if (fs.isOpened())
    	{
    		fs << "cameraMatrixL" << cameraMatrixL << "cameraDistcoeffL" << distCoeffL << "cameraMatrixR" << cameraMatrixR << "cameraDistcoeffR" << distCoeffR;
    		fs.release();
    		cout << "cameraMatrixL=:" << cameraMatrixL << endl << "cameraDistcoeffL=:" << distCoeffL << endl << "cameraMatrixR=:" << cameraMatrixR << endl << "cameraDistcoeffR=:" << distCoeffR << endl;
    	}
    	else
    	{
    		cout << "Error: can not save the intrinsics!!!!" << endl;
    	}
    
    	fs.open("extrinsics.yml", FileStorage::WRITE);
    	if (fs.isOpened())
    	{
    		fs << "R" << R << "T" << T << "Rl" << Rl << "Rr" << Rr << "Pl" << Pl << "Pr" << Pr << "Q" << Q;
    		cout << "R=" << R << endl << "T=" << T << endl << "Rl=" << Rl << endl << "Rr" << Rr << endl << "Pl" << Pl << endl << "Pr" << Pr << endl << "Q" << Q << endl;
    		fs.release();
    	}
    	else
    	{
    		cout << "Error: can not save the extrinsic parameters\n";
    	}
    
    }
    
    
    int main(int argc, char* argv[])
    {
        Mat img;
        int goodFrameCount = 0;
        while (goodFrameCount < frameNumber)
        {
            char filename[100];
            /*读取左边的图像*/
            sprintf(filename, "/home/crj/calibration/left_img/left%d.jpg", goodFrameCount + 1);
    		
            rgbImageL = imread(filename, CV_LOAD_IMAGE_COLOR);
    		imshow("chessboardL", rgbImageL);
            cvtColor(rgbImageL, grayImageL, CV_BGR2GRAY);
            /*读取右边的图像*/
            sprintf(filename, "/home/crj/calibration/right_img/right%d.jpg", goodFrameCount + 1);
            rgbImageR = imread(filename, CV_LOAD_IMAGE_COLOR);
            cvtColor(rgbImageR, grayImageR, CV_BGR2GRAY);
    
            bool isFindL, isFindR;
            isFindL = findChessboardCorners(rgbImageL, boardSize, cornerL);
            isFindR = findChessboardCorners(rgbImageR, boardSize, cornerR);
            if (isFindL == true && isFindR == true)
            {
                cornerSubPix(grayImageL, cornerL, Size(5,5), Size(-1,1), TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 20, 0.1));
                drawChessboardCorners(rgbImageL, boardSize, cornerL, isFindL);
                imshow("chessboardL", rgbImageL);
                imagePointL.push_back(cornerL);
    
                cornerSubPix(grayImageR, cornerR, Size(5,5), Size(-1,-1), TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 20, 0.1));
                drawChessboardCorners(rgbImageR, boardSize, cornerR, isFindR);
                imshow("chessboardR", rgbImageR);
                imagePointR.push_back(cornerR);
    
                goodFrameCount++;
                cout << "the image" << goodFrameCount << " is good" << endl;
            }
            else
            {
                cout << "the image is bad please try again" << endl;
            }
            if (waitKey(10) == 'q')
            {
                break;
            }
        }
    
        //计算实际的校正点的三维坐标,根据实际标定格子的大小来设置
        calRealPoint(objRealPoint, boardWidth, boardHeight, frameNumber, squareSize);
        cout << "cal real successful" << endl;
    
        //标定摄像头
        double rms = stereoCalibrate(objRealPoint, imagePointL, imagePointR,
            cameraMatrixL, distCoeffL,
            cameraMatrixR, distCoeffR,
            Size(imageWidth, imageHeight), R, T, E, F, CALIB_USE_INTRINSIC_GUESS,
            TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 100, 1e-5));
    
        cout << "Stereo Calibration done with RMS error = " << rms << endl;
    
        stereoRectify(cameraMatrixL, distCoeffL, cameraMatrixR, distCoeffR, imageSize, R, T, Rl, 
            Rr, Pl, Pr, Q, CALIB_ZERO_DISPARITY, -1, imageSize, &validROIL,&validROIR);
        
    
        //摄像机校正映射
        initUndistortRectifyMap(cameraMatrixL, distCoeffL, Rl, Pl, imageSize, CV_32FC1, mapLx, mapLy);
        initUndistortRectifyMap(cameraMatrixR, distCoeffR, Rr, Pr, imageSize, CV_32FC1, mapRx, mapRy);
    
        Mat rectifyImageL, rectifyImageR;
        cvtColor(grayImageL, rectifyImageL, CV_GRAY2BGR);
        cvtColor(grayImageR, rectifyImageR, CV_GRAY2BGR);
    
        imshow("Recitify Before", rectifyImageL);
        cout << "按Q1退出..." << endl;
        //经过remap之后,左右相机的图像已经共面并且行对准了
        Mat rectifyImageL2, rectifyImageR2;
        remap(rectifyImageL, rectifyImageL2, mapLx, mapLy, INTER_LINEAR);
        remap(rectifyImageR, rectifyImageR2, mapRx, mapRy, INTER_LINEAR);
        cout << "按Q2退出..." << endl;
    
        imshow("rectifyImageL", rectifyImageL2);
        imshow("rectifyImageR", rectifyImageR2);
    
        outputCameraParam();
    
        //显示校正结果
        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(0, 0, w, h));
        resize(rectifyImageL2, canvasPart, canvasPart.size(), 0, 0, INTER_AREA);
        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 << "Painted ImageL" << endl;
    
        //右图像画到画布上
        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 < canvas.rows; i += 16)
            line(canvas, Point(0, i), Point(canvas.cols, i), Scalar(0, 255, 0), 1, 8);
        
        imshow("rectified", canvas);
        
        cout << "wait key" << endl;
        waitKey(0);
        return 0;
    }
    
    

    **3 **C++与opencv实现测距 ****
    我们把matlab数据填写到下列代码中,各位切记不要填写错误,以免导致出现离奇的数据,我已经注释的非常清楚了;

    /*
    事先标定好的相机的参数
    fx 0 cx
    0 fy cy
    0 0  1
    */
    Mat cameraMatrixL = (Mat_<double>(3, 3) << 682.55880, 0, 384.13666,
        0, 682.24569, 311.19558,
        0, 0, 1);
    //对应matlab里的左相机标定矩阵
    Mat distCoeffL = (Mat_<double>(5, 1) << -0.51614, 0.36098, 0.00523, -0.00225, 0.00000);
    //对应Matlab所得左i相机畸变参数
    
    Mat cameraMatrixR = (Mat_<double>(3, 3) << 685.03817, 0, 397.39092,
        0, 682.54282, 272.04875,
        0, 0, 1);
    //对应matlab里的右相机标定矩阵
    
    Mat distCoeffR = (Mat_<double>(5, 1) << -0.46640, 0.22148, 0.00947, -0.00242, 0.00000);
    //对应Matlab所得右相机畸变参数
    
    Mat T = (Mat_<double>(3, 1) << -61.34485, 2.89570, -4.76870);//T平移向量
                                                        //对应Matlab所得T参数
    Mat rec = (Mat_<double>(3, 1) << -0.00306, -0.03207, 0.00206);//rec旋转向量,对应matlab om参数
    Mat R;//R 旋转矩阵
    
    

    4、完整代码 (我这里以上述摄像头拍摄的两张图来作为测距,同样可修改作为视频的实时测距,一定要将图片拷贝到你的工程目录下)

    
    /******************************/
    /*        立体匹配和测距        */
    /******************************/
    
    #include <opencv2/opencv.hpp>  
    #include <iostream>  
    
    using namespace std;
    using namespace cv;
    
    const int imageWidth = 800;                             //摄像头的分辨率  
    const int imageHeight = 600;
    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) << 682.55880, 0, 384.13666,
    	0, 682.24569, 311.19558,
    	0, 0, 1);
    //对应matlab里的左相机标定矩阵
    Mat distCoeffL = (Mat_<double>(5, 1) << -0.51614, 0.36098, 0.00523, -0.00225, 0.00000);
    //对应Matlab所得左i相机畸变参数
    
    Mat cameraMatrixR = (Mat_<double>(3, 3) << 685.03817, 0, 397.39092,
    	0, 682.54282, 272.04875,
    	0, 0, 1);
    //对应matlab里的右相机标定矩阵
    
    Mat distCoeffR = (Mat_<double>(5, 1) << -0.46640, 0.22148, 0.00947, -0.00242, 0.00000);
    //对应Matlab所得右相机畸变参数
    
    Mat T = (Mat_<double>(3, 1) << -61.34485, 2.89570, -4.76870);//T平移向量
                                                        //对应Matlab所得T参数
    Mat rec = (Mat_<double>(3, 1) << -0.00306, -0.03207, 0.00206);//rec旋转向量,对应matlab om参数
    Mat R;//R 旋转矩阵
    
    
    	  /*****立体匹配*****/
    void stereo_match(int, void*)
    {
    	bm->setBlockSize(2 * blockSize + 5);     //SAD窗口大小,5~21之间为宜
    	bm->setROI1(validROIL);
    	bm->setROI2(validROIR);
    	bm->setPreFilterCap(31);
    	bm->setMinDisparity(0);  //最小视差,默认值为0, 可以是负值,int型
    	bm->setNumDisparities(numDisparities * 16 + 16);//视差窗口,即最大视差值与最小视差值之差,窗口大小必须是16的整数倍,int型
    	bm->setTextureThreshold(10);
    	bm->setUniquenessRatio(uniquenessRatio);//uniquenessRatio主要可以防止误匹配
    	bm->setSpeckleWindowSize(100);
    	bm->setSpeckleRange(32);
    	bm->setDisp12MaxDiff(-1);
    	Mat disp, disp8;
    	bm->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("disparity", 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 << origin << "in world coordinate is: " << xyz.at<Vec3f>(origin) << endl;
    		break;
    	case EVENT_LBUTTONUP:    //鼠标左按钮释放的事件
    		selectObject = false;
    		if (selection.width > 0 && selection.height > 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, &validROIL, &validROIR);
    	initUndistortRectifyMap(cameraMatrixL, distCoeffL, Rl, Pr, imageSize, CV_32FC1, mapLx, mapLy);
    	initUndistortRectifyMap(cameraMatrixR, distCoeffR, Rr, Pr, imageSize, CV_32FC1, mapRx, mapRy);
    
    	/*
    	读取图片
    	*/
    	rgbImageL = imread("left.bmp", CV_LOAD_IMAGE_COLOR);
    	cvtColor(rgbImageL, grayImageL, CV_BGR2GRAY);
    	rgbImageR = imread("right.bmp", CV_LOAD_IMAGE_COLOR);
    	cvtColor(rgbImageR, grayImageR, CV_BGR2GRAY);
    
    	imshow("ImageL Before Rectify", grayImageL);
    	imshow("ImageR Before Rectify", 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("ImageL After Rectify", rgbRectifyImageL);
    	imshow("ImageR After Rectify", 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 << "Painted ImageL" << 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 << "Painted ImageR" << endl;
    
    	//画上对应的线条
    	for (int i = 0; i < canvas.rows; i += 16)
    		line(canvas, Point(0, i), Point(canvas.cols, i), Scalar(0, 255, 0), 1, 8);
    	imshow("rectified", canvas);
    
    	/*
    	立体匹配
    	*/
    	namedWindow("disparity", CV_WINDOW_AUTOSIZE);
    	// 创建SAD窗口 Trackbar
    	createTrackbar("BlockSize:\n", "disparity", &blockSize, 8, stereo_match);
    	// 创建视差唯一性百分比窗口 Trackbar
    	createTrackbar("UniquenessRatio:\n", "disparity", &uniquenessRatio, 50, stereo_match);
    	// 创建视差窗口 Trackbar
    	createTrackbar("NumDisparities:\n", "disparity", &numDisparities, 16, stereo_match);
    	//鼠标响应函数setMouseCallback(窗口名称, 鼠标回调函数, 传给回调函数的参数,一般取0)
    	setMouseCallback("disparity", onMouse, 0);
    	stereo_match(0, 0);
    
    	waitKey(0);
    	return 0;
    }
    

    如果对此代码和标定过程,或者遇到什么困难,大家可发邮件到1039463596@qq.com,或者加入到 193369905进行讨论,谢谢大家,原创不易,欢迎打赏
    在这里插入图片描述

    展开全文
  • 双目视觉工程答辩

    2018-01-11 15:21:52
    介绍双目视觉测距的工程答辩,是我双目视觉测距系列博文(一)中提到的PPT
  • 双目视觉的一些原理,包括双目视觉的系统标定,双目视觉的一些成功案例应用已经双目视觉当前研究难点热点
  • 仿生双目视觉传感器

    2018-10-17 14:21:15
    仿生双目视觉传感器PPT。关于生物双眼视觉系统的原理以及工学实现
  • 双目视觉标定

    2014-05-05 16:55:57
    基于双目视觉的代码调试
  • 双目视觉的三维重建

    2018-06-11 21:13:55
    双目视觉的三维重建 双目视觉的三维重建 双目视觉的三维重建
  • 双目视觉FPGA代码

    2018-05-20 22:43:51
    双目视觉FPGA代码,VERILOG语言描述的,贡献给你们网站,机器视觉相关的
  • 在研究双目视觉研究过程中,遇到了许许多多的问题,在最初入门的时候,摄像头的选择问题就成了我最头疼的问题,先后换过5个摄像头,老师的钱花了许多,可是却没有效果,想想新人入门是多么的难啊,现在我对我的所...
  • 双目视觉论文

    2012-02-27 18:02:37
    双目视觉测距论文
  • 球体的双目视觉定位(matlab,附代码)标签(空格分隔): 机器视觉引言双目视觉定位是我们的一个课程设计,最近刚做完,拿出来与大家分享一下,实验的目的是在拍摄的照片中识别球体,并求出该球体到相机的实际距离吗,...
  • 双目视觉相机标定

    2018-04-19 10:25:21
    本资源对双目视觉相机进行标定,获取目标的世界坐标和图像坐标系,求解M矩阵,得出相机内外参数
  • 综述了镜像双目视觉测量技术的发展现状,从测量原理及传感器、测量模型及标定、测量精度及评价等方面分析了镜像双目视觉测量结构模型,主要包括基于平面折反射的镜像双目结构和基于曲面折反射的镜像双目结构,总结了...
  • halcon 双目视觉原理解密,入门halcon基础视频.

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,739
精华内容 1,095
关键字:

双目视觉