双目相机测距代码演示

news/2024/11/28 19:45:08/

双目测距的操作流程有四步:相机标定——双目校正——双目匹配——计算深度,具体内容参考 : https://blog.csdn.net/qq_38236355/article/details/88933839

其中相机标定通常用matlab现成的工具箱进行标定,具体操作参考: https://blog.csdn.net/qq_38236355/article/details/89280633

我们接下来在完成相机标定的基础上,用标定得到的数据,按上述流程对双目深度进行计算。如果用的是任意的两个单目摄像头拼成的双目相机,则需要按上述所说进行双目校正;如果用的是成品的双目相机,则不需要进行双目校正,商家已经对相机校正好了。

一、双目校正+BM算法双目匹配生成视差图+生成深度图

#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;const int imagewidth = 752;
const int imageheigh = 480; // 摄像头分辨率752*480
Size imageSize = Size(imagewidth, imageheigh);Mat grayImageL;
Mat grayImageR;
Mat rectifyImageL, rectifyImageR;
Mat disp, disp8;//视差图Rect vaildROIL;
Rect vaildROIR;//图像校正后,会对图像进行裁剪,这里的vaildROIR就是指裁剪之后的区域Mat maplx, maply, mapRx, mapRy;//映射表
Mat Rl, Rr, Pl, Pr, Q; //校正旋转矩阵R,投影矩阵P,重投影矩阵Q
Mat xyz;
int blockSize = 0, uniquenessRatio = 0, numDisparities = 0;
Ptr<StereoBM>bm = StereoBM::create(16, 9);//事先用matlab标定好的相机参数
//matlab里的左相机标定参数矩阵
Mat cameraMatrixL = (Mat_<float>(3, 3) << 165.9419, -0.2471, 372.8349, 0, 164.8281, 325.8182, 0, 0, 1);
//matlab里左相机畸变参数
Mat distCoeffL = (Mat_<double>(5, 1) << -0.0218, -0.0014, -0.0104, -0.0025, -0.000024286);
//matlab右相机标定矩阵
Mat cameraMatrixR = (Mat_<double>(3, 3) << 168.2781, 0.1610, 413.2010, 0, 167.7710, 304.7487, 0, 0, 1);
//matlab右相机畸变参数
Mat distCoffR = (Mat_<double>(5, 1) << -0.0332, 0.0033, -0.0090, -0.0029, -0.00038324);
//matlab T 平移参数
Mat T = (Mat_<double>(3, 1) << -117.2789, -0.8970, 0.9281);
//旋转矩阵
Mat R = (Mat_<double>(3, 3) << 1.0000, -0.0040, -0.000052, 0.0040, 1.0000, -0.0041, 0.0000683, 0.0041, 1.0000);
//立体匹配 BM算法
void stereo_match(int, void*)
{bm->setBlockSize(2 * blockSize + 5);//SAD窗口大小bm->setROI1(vaildROIL);bm->setROI2(vaildROIR);bm->setPreFilterCap(31);bm->setMinDisparity(0);//最小视差,默认值为0bm->setNumDisparities(numDisparities * 16 + 16);//视差窗口,最大视差值与最小视差值之差bm->setTextureThreshold(10);bm->setUniquenessRatio(uniquenessRatio);//可以防止误匹配bm->setSpeckleWindowSize(100);bm->setSpeckleRange(32);bm->setDisp12MaxDiff(-1);bm->compute(rectifyImageL, rectifyImageR, disp);//生成视差图disp.convertTo(disp8, CV_8U, 255 / ((numDisparities * 16 + 16) * 16.));//计算出的视差是CV_16S格式,将其转化为CV_8U的形式reprojectImageTo3D(disp, xyz, Q, true);xyz = xyz * 16;//在实际求距离时,ReprojectTo3D出来的X / W, Y / W, Z / W都要乘以16(也就是W除以16),才能得到正确的三维坐标信息。imshow("disp image", disp8);
}
//生成深度图
void disp2Depth(int, void*)
{Mat depthMap = Mat(disp.size(), CV_16UC1, 255);int type = disp.type();float fx = cameraMatrixL.at<float>(0, 0);float fy = cameraMatrixL.at<float>(1, 1);float cx = cameraMatrixL.at<float>(0, 2);float cy = cameraMatrixL.at<float>(1, 2);float baseline = 40; //基线距离40mmif (type == CV_16S){int height = disp.rows;int width = disp.cols;short* dispData = (short*)disp.data;ushort* depthData = (ushort*)depthMap.data;for (int i = 0; i < height; i++){for (int j = 0; j < width; j++){int id = i * width + j;if (!dispData[id]){continue;}depthData[id] = ushort((float)fx*baseline / ((float)dispData[id]));}}}else{cout << "please onfirm dispImage's type" << endl;}Mat depthMap8;depthMap.convertTo(depthMap8, CV_8U);//计算出的深度图是CV_16U格式imshow("depth image", depthMap8);
}
int main()
{//双目校正stereoRectify(cameraMatrixL, distCoeffL, cameraMatrixR, distCoffR, imageSize, R, T, Rl, Rr, Pl, Pr, Q, CALIB_ZERO_DISPARITY, -1, imageSize, &vaildROIL, &vaildROIR);initUndistortRectifyMap(cameraMatrixL, distCoeffL, Rl, Pl , imageSize, CV_32FC1, maplx, maply);initUndistortRectifyMap(cameraMatrixR, distCoffR, Rr, Pr, imageSize, CV_32FC1, mapRx, mapRy);//读取图像grayImageL = imread("D:/opencv learning/diannao_left.png", 0);grayImageR = imread("D:/opencv learning/diannao_right.png", 0);if (grayImageR.empty() || grayImageL.empty()){printf("can not load image...");return -1;}imshow("imageL before rectify", grayImageL);imshow("imageR bedore rectify", grayImageR);//经过remap之后,左右相机的图像已经共面并且行对准了remap(grayImageL, rectifyImageL, maplx, maply, INTER_LINEAR);remap(grayImageR, rectifyImageR, mapRx, mapRy, INTER_LINEAR);//把校正结果显示出来imshow("imageL after rectify", rectifyImageL);imshow("imageR after rectify", rectifyImageR);//显示在同一张图上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_8UC1);//左图像画到画布上Mat canvaspart = canvas(Rect(w * 0, 0, w, h));resize(rectifyImageL, canvaspart, canvaspart.size(), 0, 0, INTER_AREA);Rect vroil(cvRound(vaildROIL.x*sf), cvRound(vaildROIL.y*sf), cvRound(vaildROIL.width*sf), cvRound(vaildROIL.height*sf));cout << "Painted imagel" << endl;//右图像画到画布上canvaspart = canvas(Rect(w, 0, w, h));resize(rectifyImageR, canvaspart, canvaspart.size(), 0, 0, INTER_LINEAR);Rect vroiR(cvRound(vaildROIR.x*sf), cvRound(vaildROIR.y*sf), cvRound(vaildROIR.width*sf), cvRound(vaildROIR.height*sf));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("disp image", CV_WINDOW_AUTOSIZE);createTrackbar("blocksize:\n", "disp image", &blockSize, 8, stereo_match);createTrackbar("UniquenessRatio:\n", "disp image", &uniquenessRatio, 50, stereo_match);createTrackbar("NumDisparities:\n", "disp image", &numDisparities, 16, stereo_match);stereo_match(0, 0);disp2Depth(0, 0);waitKey(0);return 0;
}

二、成品双目无双目校正,BM算法双目匹配生成视差图+生成深度图

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;const int imagewidth = 752;
const int imageheigh = 480; // 摄像头分辨率752*480
Size imageSize = Size(imagewidth, imageheigh);int blockSize = 0, uniquenessRatio = 0, numDisparities = 0;
Ptr<StereoBM> bm = StereoBM::create(16, 9); //用Block Matching算法,实现双目匹配Mat grayImageL, grayImageR , disp, disp8;Rect vaildROIL = Rect(0, 0, 752, 480);
Rect vaildROIR = Rect(0, 0, 752, 480);Mat cameraMatrixL = (Mat_<float>(3, 3) << 165.9419, -0.2471, 372.8349, 0, 164.8281, 325.8182, 0, 0, 1);void stereo_match(int, void*); //双目匹配,生成视差图(xl-xr)
void disp2Depth(int, void*); //视差图转为深度图int main()
{grayImageL = imread("D:/opencv learning/liangdui_left.png", 0);grayImageR = imread("D:/opencv learning/liangdui_right.png", 0);if (grayImageL.empty() || grayImageR.empty()){printf("can not load image...");}imshow("input ImageL", grayImageL);imshow("input ImageR", grayImageR);//显示在同一张图上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_8UC1);//左图像画到画布上Mat canvaspart = canvas(Rect(w * 0, 0, w, h));resize(grayImageL, canvaspart, canvaspart.size(), 0, 0, INTER_AREA);Rect vroil(cvRound(vaildROIL.x*sf), cvRound(vaildROIL.y*sf), cvRound(vaildROIL.width*sf), cvRound(vaildROIL.height*sf));cout << "Painted imagel" << endl;//右图像画到画布上canvaspart = canvas(Rect(w, 0, w, h));resize(grayImageR, canvaspart, canvaspart.size(), 0, 0, INTER_LINEAR);Rect vroiR(cvRound(vaildROIR.x*sf), cvRound(vaildROIR.y*sf), cvRound(vaildROIR.width*sf), cvRound(vaildROIR.height*sf));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);createTrackbar("BlockSize:\n", "disparity", &blockSize, 8, stereo_match);//创建SAD窗口TrackbarcreateTrackbar("UniquenessRatio:\n", "disparity", &uniquenessRatio, 50, stereo_match);//创建视差唯一性百分比窗口 TrackbarcreateTrackbar("NumDisparities:\n", "disparity", &numDisparities, 16, stereo_match);// 创建视差窗口 Trackbarstereo_match(0, 0);disp2Depth(0, 0);waitKey(0);return 0;
}
void stereo_match(int, void*)
{bm->setBlockSize(2 * blockSize + 5); //SAD窗口大小,5-21之间为宜bm->setROI1(vaildROIL);bm->setROI2(vaildROIR);bm->setPreFilterCap(31);bm->setMinDisparity(0); //最小视差bm->setNumDisparities(numDisparities * 16 + 16);bm->setTextureThreshold(10);bm->setUniquenessRatio(uniquenessRatio); //uniquenessRatio主要可以防止误匹配bm->setSpeckleRange(32);bm->setSpeckleWindowSize(100);bm->setDisp12MaxDiff(-1);bm->compute(grayImageL, grayImageR, disp); //输入图像必须为灰度图,输出视差图disp.convertTo(disp8, CV_8U, 255 / ((numDisparities * 16 + 16)*16.));//计算出的视差是CV_16S格式imshow("disparity", disp8);
}
void disp2Depth(int, void*)
{Mat depthMap = Mat(disp.size(), CV_16UC1, 255);int type = disp.type();float fx = cameraMatrixL.at<float>(0, 0);float fy = cameraMatrixL.at<float>(1, 1);float cx = cameraMatrixL.at<float>(0, 2);float cy = cameraMatrixL.at<float>(1, 2);float baseline = 40; //基线距离40mmif (type == CV_16S){int height = disp.rows;int width = disp.cols;short* dispData = (short*)disp.data;ushort* depthData = (ushort*)depthMap.data;for (int i = 0; i < height; i++){for (int j = 0; j < width; j++){int id = i * width + j;if (!dispData[id]){continue;}depthData[id] = ushort((float)fx*baseline / ((float)dispData[id]));}}}else{cout << "please onfirm dispImage's type" << endl;}Mat depthMap8;depthMap.convertTo(depthMap8, CV_8U);//计算出的深度图是CV_16U格式imshow("depth image", depthMap8);
}

我拍了一幅电脑的图片,效果如下图所示:

左右原图:
在这里插入图片描述
校正后放到同一平面:
在这里插入图片描述
视差图:
在这里插入图片描述
深度图:
在这里插入图片描述


http://www.ppmy.cn/news/516559.html

相关文章

FMCW雷达测距实验

目录 1. FMCW雷达基本原理1.1 基本结构与原理 2. L波段FMCW雷达的基本介绍2.1 参数配置2.2 数据采集板2.3 雷达测距实验2.4 干扰机实验 本文介绍如何设计FMCW雷达测距实验。 1. FMCW雷达基本原理 1.1 基本结构与原理 雷达系统所用信号的频率随时间变化呈线性升高&#xff0c;这…

激光测距仪系统设计 c语言程序),激光测距仪系统设计(机械图,电路图,c语言程序)...

激光测距仪系统设计(机械图,电路图,c语言程序)(毕业论文22000字,cad图纸,答辩ppt) 摘 要 本次激光测距仪系统设计采用的是相位式测距法,相位激光测距又称调幅连续波激光测距通常是基于对目标回波相位的探测,在诸如军事、航空、工业和体育等领域已经取得广泛的应用。相位激光测…

红外测距模块 51单片机_智能激光测距

编者按:本文转载于酷耍(http:/kooshua.com) 一、设计目的 超声波测距和激光测距是现在比较常见的两种测距方式。两种方式相对比而言,激光测距的优点是以极小的一束激光发射出去再返回,精度为毫米级,几乎不受干扰,弥补了超声波测距易受环境干扰、误差大的缺陷。因此,采用激…

利用android手机摄像头智能测量物体距离,高度

经过在下调研了利用手机摄像头智能测距&#xff0c;半个月断断续续得出几个方法&#xff0c;附上两个demo&#xff0c;然而东西都比较垃圾&#xff0c;比起人们想要的&#xff0c;差距十万八千里。 为了科技的进步&#xff0c;我就自不量力地抛砖引玉了&#xff0c;希望能有大…

双目测距 SGBM算法 Python版

前言 首先进行双目定标,获取双目摄像头内部的参数后,进行测距。本次的双目视觉测距,基于SGBM算法。 注意:双目定标的效果会影响测距的精准度,建议大家在做双目定标时,做好一些(尽量让误差小) 如果不太了解双目视觉原理,建议先看看这篇文章:一篇文章认识《双目立体视…

YOLOv5+单目测距(python)

YOLOv5单目测距&#xff08;python&#xff09; 1. 相关配置2. 测距原理3. 相机标定3.1&#xff1a;标定方法13.2&#xff1a;标定方法2 4. 相机测距4.1 测距添加4.2 细节修改&#xff08;可忽略&#xff09;4.3 主代码 5. 实验效果 相关链接 1. YOLOV7 单目测距&#xff08;p…

LabVIEW控制Arduino实现红外测距(进阶篇—6)

目录 1、项目概述 2、项目架构 3、硬件环境 4、Arduino功能设计 5、LabVIEW功能设计 5.1、前面板设计 5.2、程序框图设计 1、项目概述 红外测距是一种非直接接触的测量方式&#xff0c;由于其结构简单、抗干扰性强、成本低等优点&#xff0c;在测量测绘上得到广泛的运用…

arduino超声波测距接线图详细_Arduino系列之超声波测距模块代码(一)

这里我将简单介绍超声波测距模块 SR04超声波传感器&#xff1a; 是利用超声波特性检测距离的传感器&#xff0c;其带有两个超声波探头&#xff0c;分别用作于发射和接收超声波。范围在3-450cm。 工作原理&#xff1a; 超声波发射器向某一方向发射超声波&#xff0c;在发射的同时…