深度センサーを使用して、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);
}
これにより、この画像が得られます
この時点で、私は軌道から外れてしまったように感じます。なぜなら、ハフが自分の形状を検索するための合理的な数の候補線を生成するための適切なパラメーターのセットを見つけることができないからです。ハフをいじるか、前のステップの出力を改善することを検討する必要があります。
「見た目が良い」と思うまで入力値をいじるのではなく、各段階で結果を客観的に検証する良い方法はありますか? 開始画像が与えられた場合 (そして、必ずしも特定の方向に向いているとは限らないことを考えると)、長方形を見つけるためのより良い方法はありますか?