赞
踩
使用的Matlab标定工具箱,需要注意的点有:
我使用的非常基础的USB双目相机,两个相机之间居然还存在色差。。。videoCapture就可以了
立体校正对于双目的最主要的目的是为稠密匹配算法提供便利。opencv中有具体的立体校正函数
stereoRectify(cameraMatrixL, distCoeffL, cameraMatrixR, distCoeffR,
imageSize, R, T, Rl, Rr, Pl, Pr, Q, CALIB_ZERO_DISPARITY,
0, imageSize, &validROIL, &validROIR);
//再采用映射变换计算函数initUndistortRectifyMap得出校准映射参数,该函数功能是计算畸变矫正和立体校正的映射变换
initUndistortRectifyMap(cameraMatrixL, distCoeffL, Rl, Pl(Rect(0, 0, 3, 3)),
imageSize, CV_32FC1, mapLx, mapLy);
initUndistortRectifyMap(cameraMatrixR, distCoeffR, Rr, Pr(Rect(0, 0, 3, 3)),
imageSize, CV_32FC1, mapRx, mapRy);
remap(grayImageL, rectifyImageL, mapLx, mapLy, INTER_LINEAR);
remap(grayImageR, rectifyImageR, mapRx, mapRy, INTER_LINEAR);//立体校正完成的图片即保存在rectifyImageL/R中
使用SGM算法计算左目视差,其实为了增加准确程度,也会计算右目的视差用于补偿左目视差。但是为了实时性,在这里只计算了左目视差。我自己学习了SGM算法的源代码,也可以使用opencv自带的函数,具体如何使用网上教程很多很多,这里只给出一个示例:
cv::Ptr<cv::StereoSGBM> sgbm = StereoSGBM::create(0, 16, 3);
sgbm->setPreFilterCap(63);
int SADWindowSize = 5;
int sgbmWinSize = SADWindowSize > 0 ? SADWindowSize : 3;
sgbm->setBlockSize(sgbmWinSize);
int cn = rectifyImageL.channels();
sgbm->setP1(8 * cn*sgbmWinSize*sgbmWinSize);
sgbm->setP2(32 * cn*sgbmWinSize*sgbmWinSize);
sgbm->setMinDisparity(0);
sgbm->setNumDisparities(numberOfDisparities);
sgbm->setUniquenessRatio(10);
sgbm->setSpeckleWindowSize(100);
sgbm->setSpeckleRange(1);
sgbm->setDisp12MaxDiff(1);
sgbm->setMode(cv::StereoSGBM::MODE_SGBM);
sgbm->compute(rectifyImageL,//250, 100, 250, 100;150, 100, 400, 300
rectifyImageR, disp);
我只想说,opencv挺好用
disp.convertTo(disp8, CV_32F, 255 / (numberOfDisparities * 16.)); //1.0/16计算出的视差是CV_16S格式 CV_32F, 255 / ((numDisparities * 16 + 16)*16.) 255 / (numberOfDisparities*16.)
reprojectImageTo3D(disp8, xyz, Q, true);//Q是之前立体校正得到的参数计算3D点云保存在xyz中
在上述过程中获得了稠密点的坐标xyz,将其赋值给点云,使用点云滤波即可去噪。PCL点云库的使用移步这里
void two_Eyes::cal_cloud(int disflag)
{
double plane[3];
mutex1.lock();
param_3D.xyz.copyTo(xyz);
plane[0] = param_3D.plane[0];
plane[1] = param_3D.plane[1];
plane[2] = param_3D.plane[2];
mutex1.unlock();
double fenmu = sqrt(powf(plane[0], 2) + powf(plane[1], 2) + 1);
double Wx, Wy, Wz;
vector<double>().swap(dis); //清楚dis中的内容
double t_c = (double)clock();
cvtColor(rectifyImageL, rectifyImageL, CV_GRAY2RGB);
int rowNumber = rectifyImageL.rows;
int colNumber = rectifyImageL.cols;
pcl::PointCloud<pcl::PointXYZRGB> cloud_a2,cloud_a3;
cloud_a2.height = rowNumber;
cloud_a2.width = colNumber;
cloud_a2.points.resize(colNumber* rowNumber);
for (unsigned int u = 0; u < rowNumber; ++u)
{
for (unsigned int v = 0; v < colNumber; ++v)
{
/*unsigned int num = rowNumber*colNumber-(u*colNumber + v)-1;*/
unsigned int num = u * colNumber + v;
cloud_a2.points[num].b = rectifyImageL.at<Vec3b>(u, v)[0];
cloud_a2.points[num].g = rectifyImageL.at<Vec3b>(u, v)[1];
cloud_a2.points[num].r = rectifyImageL.at<Vec3b>(u, v)[2];
double Xw = 0, Yw = 0, Zw = 0;
if (xyz.at<Vec3f>(u, v)[2] < 1000&& xyz.at<Vec3f>(u, v)[2]!=0) //使用OpenCV计算的结果,感觉更加准确
{
Wx= xyz.at<Vec3f>(u, v)[0];
Wy= xyz.at<Vec3f>(u, v)[1];
Wz= xyz.at<Vec3f>(u, v)[2];
cloud_a2.points[num].x = Wx/1000;
cloud_a2.points[num].y = Wy/1000;
cloud_a2.points[num].z = Wz/1000;
}
}
}
///滤波示例
创建pcl::StatisticalOutlierRemoval滤波器对象
//pcl::StatisticalOutlierRemoval<pcl::PointXYZRGB> sor;
//sor.setInputCloud(cloud_a2.makeShared());
//sor.setStddevMulThresh(1.0);
将标准差倍数设为1,意味着超过一点的距离超出平均距离一个标准差以上,
则该点被标记为离群点,将被移除。
//sor.filter(cloud_a3); //滤波结果存储于此
mutex1.lock();
param_3D.cloud_a = cloud_a2;
mutex1.unlock();
cloud_test.close();
}
可以直接使用PCL库
我是在QT界面上使用VTK显示的点云,这里涉及到更多的QT界面的基础知识,不再赘述
结果如图:
点云示例:
细心可以避免很多小错误
不怕自己做不好,就怕自己停止尝试
由于相机编号0,1和左右不一定是对应的,所以一定要搞清楚再运行算法,别问我是咋知道的~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。