我正在尝试使用深度传感器为Oculus Rift开发套件添加位置跟踪。但是,我在生成可用结果的操作序列方面遇到了麻烦。
我从16位深度图像开始,其中值(但不是真的)对应于毫米。图像中未定义的值已设置为0。
首先,我通过更新遮罩图像来消除某些近距离和远距离以外的所有东西。
cv::Mat result = cv::Mat::zeros(depthImage.size(), CV_8UC3);
cv::Mat depthMask;
depthImage.convertTo(depthMask, CV_8U);
for_each_pixel<DepthImagePixel, uint8_t>(depthImage, depthMask,
[&](DepthImagePixel & depthPixel, uint8_t & maskPixel){
if (!maskPixel) {
return;
}
static const uint16_t depthMax = 1200;
static const uint16_t depthMin = 200;
if (depthPixel < depthMin || depthPixel > depthMax) {
maskPixel = 0;
}
});
接下来,由于我想要的功能可能比整个场景平均值更接近相机,我再次更新掩码以排除任何不在中值的特定范围内的内容:
const float depthAverage = cv::mean(depthImage, depthMask)[0];
const uint16_t depthMax = depthAverage * 1.0;
const uint16_t depthMin = depthAverage * 0.75;
for_each_pixel<DepthImagePixel, uint8_t>(depthImage, depthMask,
[&](DepthImagePixel & depthPixel, uint8_t & maskPixel){
if (!maskPixel) {
return;
}
if (depthPixel < depthMin || depthPixel > depthMax) {
maskPixel = 0;
}
});
最后,我将所有不在掩码中的内容归零,并将剩余值缩放到10和10之间。将图像格式转换为8位
之前的255 cv::Mat outsideMask;
cv::bitwise_not(depthMask, outsideMask);
// Zero out outside the mask
cv::subtract(depthImage, depthImage, depthImage, outsideMask);
// Within the mask, normalize to the range + X
cv::subtract(depthImage, depthMin, depthImage, depthMask);
double minVal, maxVal;
minMaxLoc(depthImage, &minVal, &maxVal);
float range = depthMax - depthMin;
float scale = (((float)(UINT8_MAX - 10) / range));
depthImage *= scale;
cv::add(depthImage, 10, depthImage, depthMask);
depthImage.convertTo(depthImage, CV_8U);
结果如下:
我对代码的这一部分感到非常满意,因为它产生了非常清晰的视觉特征。
然后我应用了几个平滑操作来消除深度相机中可怕的噪音:
cv::medianBlur(depthImage, depthImage, 9);
cv::Mat blurred;
cv::bilateralFilter(depthImage, blurred, 5, 250, 250);
depthImage = blurred;
cv::Mat result = cv::Mat::zeros(depthImage.size(), CV_8UC3);
cv::insertChannel(depthImage, result, 0);
同样,这些功能在视觉上看起来非常清晰,但我想知道它们是否能够以某种方式被削尖:
接下来我使用canny进行边缘检测:
cv::Mat canny_output;
{
cv::Canny(depthImage, canny_output, 20, 80, 3, true);
cv::insertChannel(canny_output, result, 1);
}
我正在寻找的线路在那里,但没有很好地代表角落:
最后我使用概率Hough来识别线条:
std::vector<cv::Vec4i> lines;
cv::HoughLinesP(canny_output, lines, pixelRes, degreeRes * CV_PI / 180, hughThreshold, hughMinLength, hughMaxGap);
for (size_t i = 0; i < lines.size(); i++)
{
cv::Vec4i l = lines[i];
glm::vec2 a((l[0], l[1]));
glm::vec2 b((l[2], l[3]));
float length = glm::length(a - b);
cv::line(result, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(0, 0, 255), 3, CV_AA);
}
这会生成此图像
此时我觉得自己已经脱轨了,因为我无法为Hough找到一套好的参数来产生合理数量的候选线来搜索我的形状,而我是不确定我是否应该摆弄Hough或者考虑改进前面步骤的输出。
是否有一种很好的方法可以在每个阶段客观地验证我的结果,而不是只是摆弄输入值,直到我觉得它“看起来不错”?给出起始图像是否有更好的方法来找到矩形(并且假设它不一定朝向特定方向?
答案 0 :(得分:2)
很酷的项目!
尽管如此,我觉得你的方法并没有使用你从深度图中获得的所有信息(例如3D点,法线等),这会有很大的帮助。
Point Cloud Library(PCL)是一个专门处理RGB-D数据的C ++库,使用RANSAC进行tutorial平面分割,可以激发您的灵感。由于存在大量依赖关系,您可能不希望在程序中使用PCL,但由于它是开源的,您可以在Github(PCL SAC segmentation)上找到算法实现。但是,根据场景的不同,RANSAC可能会很慢并产生不需要的结果。
您还可以尝试使用“实时平面分割”中提供的方法 使用RGB-D相机“by Holz,Holzer,Rusu和Behnke,2011(PDF),它建议使用积分图像进行快速正态估计,然后使用法线聚类进行平面检测。