【问题标题】:How can I use OpenCV to find an arbitrarily transformed rectangle in a depth image?如何使用 OpenCV 在深度图像中找到任意变换的矩形?
【发布时间】:2014-04-22 02:19:13
【问题描述】:

我正在尝试使用深度传感器向 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 和 255 之间,然后将图像格式转换为 8 位

  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);
  }

我正在寻找的线条在那里,但没有很好地呈现在角落:

最后我使用概率霍夫来识别线条:

  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 或考虑改进先前步骤的输出。

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

【问题讨论】:

  • 具体来说,这是一项具有挑战性的任务,需要一个来自深度图像的良好表面曲率估计器,专为光滑表面而设计,不需要太多预处理。预处理使一切顺利,但也使头盔窗口与上半身更难区分。
  • Curves and Surfaces 见 13.3.2、13.5.2 和 13.8
  • 此外,如果您找到一种方法来检测或从深度图中删除此部分,这将有所帮助。 See picture

标签: c++ opencv feature-detection oculus


【解决方案1】:

非常酷的项目!

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

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

您也可以尝试使用“实时平面分割”中介绍的方法 使用 RGB-D 相机”,Holz、Holzer、Rusu 和 Behnke,2011 年 (PDF),建议使用积分图像进行快速法线估计,然后使用法线聚类进行平面检测。

【讨论】:

  • 我最初尝试与 PCL 合作,但这是一个暴行。我正在使用 Visual Studio 2013,我的(相当大的)项目使用静态链接的运行时,PCL 都不支持。当我尝试制作一个动态链接的小项目并调整 PCL 以在 VC2013 中编译时,我遇到了在运行他们的一些演示代码时立即导致崩溃的错误。令我惊讶的是,使用 CMake 的项目对其编译器支持有如此严格的限制。
  • 我会看一下论文,虽然我想要一个库,而不是我必须实施的论文。通过使用 Sobel 计算图像的导数,我取得了一些进展。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-31
  • 2016-01-03
  • 2019-09-14
  • 2011-01-05
  • 1970-01-01
  • 1970-01-01
  • 2015-09-09
相关资源
最近更新 更多