如何使用 OpenCV 3.0 StereoSGBM 和 PCL 生成一对立体图像的有效点云表示
How to generate a valid point cloud representation of a pair of stereo images using OpenCV 3.0 StereoSGBM and PCL
我最近开始使用 OpenCV 3.0,我的目标是从一组立体相机捕获一对立体图像,创建合适的视差图,将视差图转换为 3D 点云,最后显示使用 PCL.
在点云查看器中生成的点云
我已经进行了相机校准,得到的校准 RMS 为 0.4
您可以在下面的链接中找到我的图像对(左图)1 and (Right Image)2。我正在使用 StereoSGBM 来创建视差图像。我还使用轨迹条调整 StereoSGBM 函数参数以获得更好的视差图像。不幸的是,我无法 post 我的视差图像,因为我是 Whosebug 的新手并且没有足够的声誉来 post 超过两个图像链接!
得到视差图后(下面代码中的"disp"),我使用reprojectImageTo3D()函数将视差图信息转换为XYZ 3D坐标,然后我将结果转换成一个数组"pcl::PointXYZRGB" 点,以便它们可以显示在 PCL 点云查看器中。执行所需的转换后,我得到的点云是一个愚蠢的金字塔形状点云,没有任何意义。我已经阅读并尝试了以下链接中所有建议的方法:
1- http://blog.martinperis.com/2012/01/3d-reconstruction-with-opencv-and-point.html
2-
3-
但没有一个有效!!!
下面我提供了我的代码的转换部分,如果你能告诉我我遗漏了什么,将不胜感激:
pcl::PointCloud<pcl::PointXYZRGB>::Ptr pointcloud(new pcl::PointCloud<pcl::PointXYZRGB>());
Mat xyz;
reprojectImageTo3D(disp, xyz, Q, false, CV_32F);
pointcloud->width = static_cast<uint32_t>(disp.cols);
pointcloud->height = static_cast<uint32_t>(disp.rows);
pointcloud->is_dense = false;
pcl::PointXYZRGB point;
for (int i = 0; i < disp.rows; ++i)
{
uchar* rgb_ptr = Frame_RGBRight.ptr<uchar>(i);
uchar* disp_ptr = disp.ptr<uchar>(i);
double* xyz_ptr = xyz.ptr<double>(i);
for (int j = 0; j < disp.cols; ++j)
{
uchar d = disp_ptr[j];
if (d == 0) continue;
Point3f p = xyz.at<Point3f>(i, j);
point.z = p.z; // I have also tried p.z/16
point.x = p.x;
point.y = p.y;
point.b = rgb_ptr[3 * j];
point.g = rgb_ptr[3 * j + 1];
point.r = rgb_ptr[3 * j + 2];
pointcloud->points.push_back(point);
}
}
viewer.showCloud(pointcloud);
经过一些工作和研究后,我找到了我的答案,我在这里分享它,以便其他读者可以使用。
从视差图像到 3D XYZ(并最终到点云)的转换算法没有任何问题。问题是物体(我拍摄的物体)与相机的距离以及 StereoBM 或 StereoSGBM 算法可用于检测两个图像(图像对)之间相似性的信息量。为了获得正确的 3D 点云,需要有良好的视差图像,并且为了获得良好的视差图像(假设您已执行良好的校准),请确保以下内容:
1- 两个帧(右帧和左帧)之间应该有足够的可检测和可区分的共同特征。原因是 StereoBM 或 StereoSGBM 算法寻找两个帧之间的共同特征,它们很容易被两个帧中可能不一定属于相同对象的相似事物所愚弄。我个人认为这两种匹配算法还有很大的改进空间。所以当心你用你的相机在看什么。
2- 感兴趣的对象(您有兴趣拥有其 3D 点云模型的对象)应与您的相机保持一定距离。基线越大(基线是两个相机之间的距离),您感兴趣的物体(目标)可以越远。
嘈杂和扭曲的视差图像永远不会生成良好的 3D 点云。改善视差图像可以做的一件事是在应用程序中使用跟踪条,这样您就可以调整 StereoSBM 或 StereoSGBM 参数,直到看到好的结果(清晰平滑的视差图像)。下面的代码是一个关于如何生成轨迹条的小而简单的例子(我写得尽可能简单)。按需使用:
int PreFilterType = 0, PreFilterCap = 0, MinDisparity = 0, UniqnessRatio = 0, TextureThreshold = 0,
SpeckleRange = 0, SADWindowSize = 5, SpackleWindowSize = 0, numDisparities = 0, numDisparities2 = 0, PreFilterSize = 5;
Ptr<StereoBM> sbm = StereoBM::create(numDisparities, SADWindowSize);
while(1)
{
sbm->setPreFilterType(PreFilterType);
sbm->setPreFilterSize(PreFilterSize);
sbm->setPreFilterCap(PreFilterCap + 1);
sbm->setMinDisparity(MinDisparity-100);
sbm->setTextureThreshold(TextureThreshold*0.0001);
sbm->setSpeckleRange(SpeckleRange);
sbm->setSpeckleWindowSize(SpackleWindowSize);
sbm->setUniquenessRatio(0.01*UniqnessRatio);
sbm->setSmallerBlockSize(15);
sbm->setDisp12MaxDiff(32);
namedWindow("Track Bar Window", CV_WINDOW_NORMAL);
cvCreateTrackbar("Number of Disparities", "Track Bar Window", &PreFilterType, 1, 0);
cvCreateTrackbar("Pre Filter Size", "Track Bar Window", &PreFilterSize, 100);
cvCreateTrackbar("Pre Filter Cap", "Track Bar Window", &PreFilterCap, 61);
cvCreateTrackbar("Minimum Disparity", "Track Bar Window", &MinDisparity, 200);
cvCreateTrackbar("Uniqueness Ratio", "Track Bar Window", &UniqnessRatio, 2500);
cvCreateTrackbar("Texture Threshold", "Track Bar Window", &TextureThreshold, 10000);
cvCreateTrackbar("Speckle Range", "Track Bar Window", &SpeckleRange, 500);
cvCreateTrackbar("Block Size", "Track Bar Window", &SADWindowSize, 100);
cvCreateTrackbar("Speckle Window Size", "Track Bar Window", &SpackleWindowSize, 200);
cvCreateTrackbar("Number of Disparity", "Track Bar Window", &numDisparities, 500);
if (PreFilterSize % 2 == 0)
{
PreFilterSize = PreFilterSize + 1;
}
if (PreFilterSize2 < 5)
{
PreFilterSize = 5;
}
if (SADWindowSize % 2 == 0)
{
SADWindowSize = SADWindowSize + 1;
}
if (SADWindowSize < 5)
{
SADWindowSize = 5;
}
if (numDisparities % 16 != 0)
{
numDisparities = numDisparities + (16 - numDisparities % 16);
}
}
}
如果您没有得到正确的结果和平滑的视差图像,请不要失望。尝试将 OpenCV 示例图像(其中带有橙色桌子 lamp 的图像)与您的算法一起使用,以确保您拥有正确的管道,然后尝试从不同距离拍摄照片并使用 StereoBM/StereoSGBM参数,直到你能得到有用的东西。为此,我使用了自己的脸,因为我的基线非常小,所以我非常靠近我的相机(这是我的 3D 人脸点云图片的 link,嘿,你敢不敢laughing!!!)1.经过一周的努力,我很高兴看到自己处于 3D 点云形式。我以前从未如此高兴见到自己! ;)
我最近开始使用 OpenCV 3.0,我的目标是从一组立体相机捕获一对立体图像,创建合适的视差图,将视差图转换为 3D 点云,最后显示使用 PCL.
在点云查看器中生成的点云我已经进行了相机校准,得到的校准 RMS 为 0.4
您可以在下面的链接中找到我的图像对(左图)1 and (Right Image)2。我正在使用 StereoSGBM 来创建视差图像。我还使用轨迹条调整 StereoSGBM 函数参数以获得更好的视差图像。不幸的是,我无法 post 我的视差图像,因为我是 Whosebug 的新手并且没有足够的声誉来 post 超过两个图像链接!
得到视差图后(下面代码中的"disp"),我使用reprojectImageTo3D()函数将视差图信息转换为XYZ 3D坐标,然后我将结果转换成一个数组"pcl::PointXYZRGB" 点,以便它们可以显示在 PCL 点云查看器中。执行所需的转换后,我得到的点云是一个愚蠢的金字塔形状点云,没有任何意义。我已经阅读并尝试了以下链接中所有建议的方法:
1- http://blog.martinperis.com/2012/01/3d-reconstruction-with-opencv-and-point.html
2-
3-
但没有一个有效!!!
下面我提供了我的代码的转换部分,如果你能告诉我我遗漏了什么,将不胜感激:
pcl::PointCloud<pcl::PointXYZRGB>::Ptr pointcloud(new pcl::PointCloud<pcl::PointXYZRGB>());
Mat xyz;
reprojectImageTo3D(disp, xyz, Q, false, CV_32F);
pointcloud->width = static_cast<uint32_t>(disp.cols);
pointcloud->height = static_cast<uint32_t>(disp.rows);
pointcloud->is_dense = false;
pcl::PointXYZRGB point;
for (int i = 0; i < disp.rows; ++i)
{
uchar* rgb_ptr = Frame_RGBRight.ptr<uchar>(i);
uchar* disp_ptr = disp.ptr<uchar>(i);
double* xyz_ptr = xyz.ptr<double>(i);
for (int j = 0; j < disp.cols; ++j)
{
uchar d = disp_ptr[j];
if (d == 0) continue;
Point3f p = xyz.at<Point3f>(i, j);
point.z = p.z; // I have also tried p.z/16
point.x = p.x;
point.y = p.y;
point.b = rgb_ptr[3 * j];
point.g = rgb_ptr[3 * j + 1];
point.r = rgb_ptr[3 * j + 2];
pointcloud->points.push_back(point);
}
}
viewer.showCloud(pointcloud);
经过一些工作和研究后,我找到了我的答案,我在这里分享它,以便其他读者可以使用。
从视差图像到 3D XYZ(并最终到点云)的转换算法没有任何问题。问题是物体(我拍摄的物体)与相机的距离以及 StereoBM 或 StereoSGBM 算法可用于检测两个图像(图像对)之间相似性的信息量。为了获得正确的 3D 点云,需要有良好的视差图像,并且为了获得良好的视差图像(假设您已执行良好的校准),请确保以下内容:
1- 两个帧(右帧和左帧)之间应该有足够的可检测和可区分的共同特征。原因是 StereoBM 或 StereoSGBM 算法寻找两个帧之间的共同特征,它们很容易被两个帧中可能不一定属于相同对象的相似事物所愚弄。我个人认为这两种匹配算法还有很大的改进空间。所以当心你用你的相机在看什么。
2- 感兴趣的对象(您有兴趣拥有其 3D 点云模型的对象)应与您的相机保持一定距离。基线越大(基线是两个相机之间的距离),您感兴趣的物体(目标)可以越远。
嘈杂和扭曲的视差图像永远不会生成良好的 3D 点云。改善视差图像可以做的一件事是在应用程序中使用跟踪条,这样您就可以调整 StereoSBM 或 StereoSGBM 参数,直到看到好的结果(清晰平滑的视差图像)。下面的代码是一个关于如何生成轨迹条的小而简单的例子(我写得尽可能简单)。按需使用:
int PreFilterType = 0, PreFilterCap = 0, MinDisparity = 0, UniqnessRatio = 0, TextureThreshold = 0,
SpeckleRange = 0, SADWindowSize = 5, SpackleWindowSize = 0, numDisparities = 0, numDisparities2 = 0, PreFilterSize = 5;
Ptr<StereoBM> sbm = StereoBM::create(numDisparities, SADWindowSize);
while(1)
{
sbm->setPreFilterType(PreFilterType);
sbm->setPreFilterSize(PreFilterSize);
sbm->setPreFilterCap(PreFilterCap + 1);
sbm->setMinDisparity(MinDisparity-100);
sbm->setTextureThreshold(TextureThreshold*0.0001);
sbm->setSpeckleRange(SpeckleRange);
sbm->setSpeckleWindowSize(SpackleWindowSize);
sbm->setUniquenessRatio(0.01*UniqnessRatio);
sbm->setSmallerBlockSize(15);
sbm->setDisp12MaxDiff(32);
namedWindow("Track Bar Window", CV_WINDOW_NORMAL);
cvCreateTrackbar("Number of Disparities", "Track Bar Window", &PreFilterType, 1, 0);
cvCreateTrackbar("Pre Filter Size", "Track Bar Window", &PreFilterSize, 100);
cvCreateTrackbar("Pre Filter Cap", "Track Bar Window", &PreFilterCap, 61);
cvCreateTrackbar("Minimum Disparity", "Track Bar Window", &MinDisparity, 200);
cvCreateTrackbar("Uniqueness Ratio", "Track Bar Window", &UniqnessRatio, 2500);
cvCreateTrackbar("Texture Threshold", "Track Bar Window", &TextureThreshold, 10000);
cvCreateTrackbar("Speckle Range", "Track Bar Window", &SpeckleRange, 500);
cvCreateTrackbar("Block Size", "Track Bar Window", &SADWindowSize, 100);
cvCreateTrackbar("Speckle Window Size", "Track Bar Window", &SpackleWindowSize, 200);
cvCreateTrackbar("Number of Disparity", "Track Bar Window", &numDisparities, 500);
if (PreFilterSize % 2 == 0)
{
PreFilterSize = PreFilterSize + 1;
}
if (PreFilterSize2 < 5)
{
PreFilterSize = 5;
}
if (SADWindowSize % 2 == 0)
{
SADWindowSize = SADWindowSize + 1;
}
if (SADWindowSize < 5)
{
SADWindowSize = 5;
}
if (numDisparities % 16 != 0)
{
numDisparities = numDisparities + (16 - numDisparities % 16);
}
}
}
如果您没有得到正确的结果和平滑的视差图像,请不要失望。尝试将 OpenCV 示例图像(其中带有橙色桌子 lamp 的图像)与您的算法一起使用,以确保您拥有正确的管道,然后尝试从不同距离拍摄照片并使用 StereoBM/StereoSGBM参数,直到你能得到有用的东西。为此,我使用了自己的脸,因为我的基线非常小,所以我非常靠近我的相机(这是我的 3D 人脸点云图片的 link,嘿,你敢不敢laughing!!!)1.经过一周的努力,我很高兴看到自己处于 3D 点云形式。我以前从未如此高兴见到自己! ;)