如何使用OpenCV在深度图像中查找任意变换的矩形?

Jhe*_*ico 6 c++ opencv feature-detection oculus

我正在尝试使用深度传感器为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;
      }
  });
Run Code Online (Sandbox Code Playgroud)

接下来,由于我想要的功能可能比整个场景平均值更接近相机,我再次更新掩码以排除任何不在中值的特定范围内的内容:

  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;
      }
  });
Run Code Online (Sandbox Code Playgroud)

最后,我将所有不在掩码中的内容归零,并在将图像格式转换为8位之前将剩余值缩放到10到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);
Run Code Online (Sandbox Code Playgroud)

结果如下:

来源图片

我对代码的这一部分非常满意,因为它产生了非常清晰的视觉特性.

我正在应用几个平滑操作来摆脱深度相机的荒谬噪音:

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);
Run Code Online (Sandbox Code Playgroud)

再一次,这些功能在视觉上看起来非常清晰,但我想知道它们是否能够以某种方式被削尖:

在此输入图像描述

接下来我使用canny进行边缘检测:

  cv::Mat canny_output;
  {
    cv::Canny(depthImage, canny_output, 20, 80, 3, true);
    cv::insertChannel(canny_output, result, 1);
  }
Run Code Online (Sandbox Code Playgroud)

我正在寻找的线路在那里,但在角落处没有很好的表现:

在此输入图像描述

最后我使用概率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);
  }
Run Code Online (Sandbox Code Playgroud)

这导致该图像

在此输入图像描述

在这一点上,我觉得我已经离开了轨道,因为我找不到一个好的参数集,以便Hough产生合理数量的候选线来搜索我的形状,我不确定是否我应该摆弄霍夫或者考虑改进前面步骤的输出.

有没有一种方法在每个阶段客观地验证我的结果,而不是只是摆弄输入值,直到我认为它看起来很好?给定起始图像是否有更好的方法来找到矩形(并且假设它不一定朝向特定方向?

BCo*_*nic 2

非常酷的项目!

不过,我觉得你的方法并没有使用你可以从深度图中获得的所有信息(例如 3D 点、法线等),这会有很大帮助。

点云库(PCL)是一个专门用于处理 RGB-D 数据的 C++ 库,有一个关于使用 RANSAC 进行平面分割的教程,可以给您带来启发。由于存在大量依赖项,您可能不想在程序中使用 PCL,但是由于它是开源的,您可以在 Github 上找到算法实现(PCL SAC 分段)。然而,RANSAC 可能会很慢并且根据场景产生不需要的结果。

您还可以尝试使用 Holz、Holzer、Rusu 和 Behnke,2011 年的“Real-Time Plane Segmentation using RGB-D Cameras”(PDF)中提出的方法,该方法建议使用积分图像进行快速法线估计,然后使用聚类进行平面检测的法线。