より単純なポリゴンで輪郭を表現し、暗黙的にノイズ除去を行うために、輪郭にcv :: approxPolyDPメソッド(cv :: findContours)を正常に適用しました。
RGBDカメラ(一般的に非常にノイズが多い)から取得したエッジマップでも同じことをしたいのですが、これまであまり成功しておらず、オンラインで相対的な例を見つけることができません。これが必要な理由は、エッジマップを使用して、指の間のエッジ、指のオクルージョンによって作成されたエッジ、またはPalmで作成されたエッジを使用することもできるためです。
この方法は、等高線以外の一般的なエッジマップに適用できますか?
誰かが私に例を教えてもらえますか?
添付の画像:
輪郭の成功例:
エッジマップの問題のあるケース:
おそらく私は間違った方法で物事を描きますが、メソッドによって返されたピクセルだけを描くと、おそらく大きな領域が最終結果に表されないことがわかります(これはイプシロンパラメーターによってあまり変化しません)。
上で説明した実験パイプラインで使用したものと同様の深度画像も添付します。この深度画像は深度カメラでは取得されませんでしたが、OpenGLを使用してGPUの深度バッファーを読み取ることによって合成的に生成されました。
参考までに、これは深度カメラから直接取得された深度画像のエッジマップでもあります(生画像を使用し、スムージングなどは適用されません)
(深度カメラから見た手、手のひらを上に向け、指を手のひらに向かって「閉じる」)
approxPolyDP
に関する問題は、approxPolyDP
への入力のフォーマットが原因です。
approxPolyDP
は、入力がPoint
sのベクトルであることを期待しています。これらの点は、approxPolyDP
によって処理される多角形の曲線を定義します。曲線は開いていても閉じていてもよく、フラグで制御できます。
リスト内のポイントの順序は重要です。手作業でポリゴンをトレースするのと同じように、ベクトル内の後続の各ポイントは、時計回りまたは反時計回りのポリゴンの次の頂点である必要があります。
ポイントのリストがラスター順(Y、Xの順に並べ替え)で保存されている場合、point[k]
およびpoint[k+1]
は必ずしも同じ曲線に属しているとは限りません。これが問題の原因です。
この問題は、 OpenCV-キャニー関数の結果からエッジを抽出する方法は? のイラストで説明されています。 Mikhail からの引用:「キャニーはピクセルをチェーンやセグメントに接続しません。」
Canny
によって生成される「ラスター順序」の図。approxPolyDP
が期待する「等高線順」のイラスト必要なのは「エッジピクセルのチェーン」のリストです。各チェーンには、鉛筆の先が紙から離れることなく、誰かが鉛筆でオブジェクトの輪郭をトレースするように、互いに隣接するエッジピクセルが含まれている必要があります。
これは、Canny
などのエッジ検出メソッドから返されるものではありません。エッジマップを隣接する(連続した)エッジピクセルのチェーンに変換するには、さらに処理が必要です。
(1)threshold
への入力として、エッジ検出の代わりにバイナリfindContours
を使用します
これは、手を背景から分離するしきい値が存在し、この値が(手の一部だけでなく)手全体で機能する場合に適用されます。
(2)エッジマップをスキャンし、各エッジピクセルの隣接ピクセルを調べて、隣接ピクセルのリストを作成します。
これは連結成分アルゴリズムに似ていますが、blob(各ピクセルのメンバーシップのみを知る必要がある)を見つける代わりに、チェーンに沿って前と次のエッジピクセルを区別できるようにピクセルのチェーンを見つけようとする点が異なります。 。
(3)エッジ描画などの代替エッジ検出アルゴリズムを使用します。
詳細は http://ceng.anadolu.edu.tr/cv/EdgeDrawing/ にあります。
残念ながら、これはOpenCVからすぐに提供されるものではないため、他の場所で実装を見つける必要がある場合があります。
#include <stdint.h>
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat matInput = imread("~/Data/mA9EE.png", false);
// ---- Preprocessing of depth map. (Optional.) ----
GaussianBlur(matInput, matInput, cv::Size(9, 9), 4.0);
// ---- Here, we use cv::threshold instead of cv::Canny as explained above ----
Mat matEdge;
//Canny(matInput, matEdge, 0.1, 1.0);
threshold(matInput, matEdge, 192.0, 255.0, THRESH_BINARY_INV);
// ---- Use findContours to find chains of consecutive Edge pixels ----
vector<vector<Point> > contours;
findContours(matEdge, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
// ---- Code below is only used for visualizing the result. ----
Mat matContour(matEdge.size(), CV_8UC1);
for (size_t k = 0; k < contours.size(); ++k)
{
const vector<Point>& contour = contours[k];
for (size_t k2 = 0; k2 < contour.size(); ++k2)
{
const Point& p = contour[k2];
matContour.at<uint8_t>(p) = 255;
}
}
imwrite("~/Data/output.png", matContour);
cout << "Done!" << endl;
return 0;
}