6

深度センサーを使用して、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);

繰り返しますが、機能は視覚的にはかなり明確に見えますが、どうにかしてシャープにすることができなかったのだろうかと思います:

ここに画像の説明を入力

次に、エッジ検出にキャニーを使用しています。

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

これにより、この画像が得られます

ここに画像の説明を入力

この時点で、私は軌道から外れてしまったように感じます。なぜなら、ハフが自分の形状を検索するための合理的な数の候補線を生成するための適切なパラメーターのセットを見つけることができないからです。ハフをいじるか、前のステップの出力を改善することを検討する必要があります。

「見た目が良い」と思うまで入力値をいじるのではなく、各段階で結果を客観的に検証する良い方法はありますか? 開始画像が与えられた場合 (そして、必ずしも特定の方向に向いているとは限らないことを考えると)、長方形を見つけるためのより良い方法はありますか?

4

1 に答える 1

2

とてもクールなプロジェクト!

ただし、あなたのアプローチは、デプスマップから取得できるすべての情報 (3D ポイント、法線など) を使用していないように感じます。これは非常に役立ちます。

RGB-D データの処理専用の C++ ライブラリである Point Cloud Library (PCL) には、RANSAC を使用した平面セグメンテーションに関するチュートリアルがあり、刺激を受けることができます。多数の依存関係があるため、プログラムで PCL を使用したくない場合もありますが、PCL はオープンソースであるため、Github でアルゴリズムの実装を見つけることができます ( PCL SAC セグメンテーション)。ただし、RANSAC は速度が遅く、シーンによっては望ましくない結果が生じる場合があります。

Holz、Holzer、Rusu、および Behnke、2011 年 ( PDF ) による「RGB-D カメラを使用したリアルタイム平面セグメンテーション」で提示されたアプローチを使用することもできます。法線の。

于 2014-03-17T08:21:35.417 に答える