私は、C++のOpenCVを使用して、移動する人々の追跡を機能させようとしています。カメラは通りを見て、人々はその周りを移動します。私が撮影して使用しているサンプルビデオについては、こちらを参照してください: http://akos.maroy.hu/~akos/eszesp/MVI_0778.MOV
私はこのトピックについて読み、次のような多くのことを試しました。
しかし、これらのどれも良い結果を提供しません。私のサンプルコードについては、以下を参照してください。上記のビデオに基づくコードの出力については、以下を参照してください: http://akos.maroy.hu/~akos/eszesp/ize.avi 。背景に対して検出された輪郭は赤で、輪郭の境界の長方形は緑で、HOG人物検出器の結果は青で表示されます。
私が抱えている具体的な問題は次のとおりです。
いくつかの誤検知がありますが、バックグラウンド検出とその後の輪郭の検出は正常に機能しているようです。しかし、主な欠点は、多くの場合、1人の人物が複数の輪郭に「切り刻まれる」ことです。想定される「理想的な」人のサイズ、またはその他の手段によって、これらを「結合」する簡単な方法はありますか?
hOG人物検出器に関しては、私の場合、画像上の実際の人物を識別することはめったにありません。私はそこで何が間違っているのでしょうか?
すべてのポインタ、アイデアを歓迎します!
したがって、これまでに使用しているコードは、あちこちで見つけたさまざまなサンプルのカストアンドペーストの栄光です。
#include<opencv2/opencv.hpp>
#include<iostream>
#include<vector>
int main(int argc, char *argv[])
{
if (argc < 3) {
std::cerr << "Usage: " << argv[0] << " in.file out.file" << std::endl;
return -1;
}
cv::Mat frame;
cv::Mat back;
cv::Mat fore;
std::cerr << "opening " << argv[1] << std::endl;
cv::VideoCapture cap(argv[1]);
cv::BackgroundSubtractorMOG2 bg;
//bg.nmixtures = 3;
//bg.bShadowDetection = false;
cv::VideoWriter output;
//int ex = static_cast<int>(cap.get(CV_CAP_PROP_FOURCC));
int ex = CV_FOURCC('P','I','M','1');
cv::Size size = cv::Size((int) cap.get(CV_CAP_PROP_FRAME_WIDTH),
(int) cap.get(CV_CAP_PROP_FRAME_HEIGHT));
std::cerr << "saving to " << argv[2] << std::endl;
output.open(argv[2], ex, cap.get(CV_CAP_PROP_FPS), size, true);
std::vector<std::vector<cv::Point> > contours;
cv::namedWindow("Frame");
cv::namedWindow("Fore");
cv::namedWindow("Background");
cv::SimpleBlobDetector::Params params;
params.minThreshold = 40;
params.maxThreshold = 60;
params.thresholdStep = 5;
params.minArea = 100;
params.minConvexity = 0.3;
params.minInertiaRatio = 0.01;
params.maxArea = 8000;
params.maxConvexity = 10;
params.filterByColor = false;
params.filterByCircularity = false;
cv::SimpleBlobDetector blobDtor(params);
blobDtor.create("SimpleBlob");
std::vector<std::vector<cv::Point> > blobContours;
std::vector<cv::KeyPoint> keyPoints;
cv::Mat out;
cv::HOGDescriptor hog;
hog.setSVMDetector(cv::HOGDescriptor::getDefaultPeopleDetector());
for(;;)
{
cap >> frame;
bg.operator ()(frame, fore);
bg.getBackgroundImage(back);
cv::erode(fore, fore, cv::Mat());
cv::dilate(fore, fore, cv::Mat());
blobDtor.detect(fore, keyPoints, cv::Mat());
//cv::imshow("Fore", fore);
cv::findContours(fore, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
cv::drawContours(frame, contours, -1, cv::Scalar(0,0,255), 2);
std::vector<std::vector<cv::Point> >::const_iterator it = contours.begin();
std::vector<std::vector<cv::Point> >::const_iterator end = contours.end();
while (it != end) {
cv::Rect bounds = cv::boundingRect(*it);
cv::rectangle(frame, bounds, cv::Scalar(0,255,0), 2);
++it;
}
cv::drawKeypoints(fore, keyPoints, out, CV_RGB(0,255,0), cv::DrawMatchesFlags::DEFAULT);
cv::imshow("Fore", out);
std::vector<cv::Rect> found, found_filtered;
hog.detectMultiScale(frame, found, 0, cv::Size(8,8), cv::Size(32,32), 1.05, 2);
for (int i = 0; i < found.size(); ++i) {
cv::Rect r = found[i];
int j = 0;
for (; j < found.size(); ++j) {
if (j != i && (r & found[j]) == r) {
break;
}
}
if (j == found.size()) {
found_filtered.Push_back(r);
}
}
for (int i = 0; i < found_filtered.size(); ++i) {
cv::Rect r = found_filtered[i];
cv::rectangle(frame, r.tl(), r.br(), cv::Scalar(255,0,0), 3);
}
output << frame;
cv::resize(frame, frame, cv::Size(1280, 720));
cv::imshow("Frame", frame);
cv::resize(back, back, cv::Size(1280, 720));
cv::imshow("Background", back);
if(cv::waitKey(30) >= 0) break;
}
return 0;
}
実際、それは非常に幅広いトピックです。この問題を攻撃しようとする科学論文はたくさんあります。あなたは前に何かを読むべきです。
簡単に言うと、背景の検出と輪郭が最も簡単な手法です。 OpenCVには非常に優れた実装があり、GPU用にも最適化されています。前景/背景のブロブをリファインするには、いくつかの モルフォロジー演算 ブロブの穴を閉じて、より良い結果を得ることができます。しかし、完璧な結果を期待しないでください。バックグラウンド減算は難しい操作です。特定のデータセットのパラメーターを微調整してから、実際のコードを試してみると、何も機能しません。関心のないオブジェクトによるライト、シャドウ、背景の変化..いくつかの問題について言及します。
つまり、いわゆる「blobフラグメンテーション」または「split-merge」問題を処理するための単純で標準的な手法はありません(1人の人がより多くのblobに分割される場合もあれば、より多くの人が1つのblobにマージされる場合もあります)。繰り返しになりますが、この議論に関する科学論文がたくさんあります。しかし、不完全な観察や乱雑な観察の追跡を処理するための手法があります。最も簡単な方法の1つは、カルマンフィルターを使用した不完全な観測を前提として、システムの実際の状態を推測することです。 Opencvにはその上に素晴らしい実装があります。繰り返しになりますが、「カルマンフィルター追跡」または「GNNデータの関連付け」で検索すると、多くのことがわかります。
人の身長の推定などの幾何学的情報を使用したい場合は、それを行うことができますが、カメラのキャリブレーションパラメーターが必要です。これは、それらを利用できるようにする(標準のiphoneカメラのMicrosoft kinectにパラメーターが利用できる)か、カメラのキャリブレーションプロセスを通じてそれらを計算することを意味します。これは、チェス盤の画像をダウンロードして紙に印刷し、写真を撮ることを意味します。次に、OpenCVにはキャリブレーションを行うためのすべての方法があります。その後、地面を推定し、いくつかの単純なレンダリングプロジェクト/非プロジェクトメソッドを使用して2D座標から3D座標に行き来し、3D標準人物の2D境界ボックスを推定する必要があります。
「歩行者追跡」に関する最新のアプローチは、いくつかの検出器を使用して観察を抽出します。バックグラウンド減算は、穴の画像を検索しないように検出を試みるマップを提供できますが、この場合、ブロブの検出は役に立ちません。 OpenCVでは、この場合によく使用される実装はHaar Adaboost検出器とHOG検出器です。HOG検出器は、場合によってはより良い結果をもたらすようです。 OpenCVにすでに実装されている分類器には、Haarの顔検出器と、HOGの検出器が含まれています。 OpenCVリポジトリのcppサンプルとpythonサンプルの両方に例があります。
標準の検出が失敗した場合(ビデオのサイズが異なる場合、または歩行者以外のオブジェクトを検出する必要がある場合)、独自の検出器をトレーニングする必要があります。つまり、検出したいオブジェクトの画像(ポジティブサンプル)と他の画像(ネガティブサンプル)を収集し、SVNなどの機械学習手法を使用して独自の分類器をトレーニングします。繰り返しますが、グーグルはあなたの友達です:)
幸運を!
私はこのような人間の追跡者を作成します:
まず、オブジェクトを初期化する必要があります。どうやって?物体の検出。適切なモデル(haarcascade_fullbody.xmlなど)でHOGまたはカスケード分類子を使用します(またはそれらをすべて一緒に使用します)。
次に、境界ボックス内で見つかったピクセルを追跡する必要があります。どうやって? 過去のテンプレートと一致 !アイデア:複数をvector<cv::Mat>
に蓄積し、mean template
を相関に使用します。
その他のアイデア:
結果を組み合わせる:最も信頼性の高い観測モデルとして検出器を使用し、失敗した場合はテンプレートマッチングに切り替えます。
バックグラウンドモデリングを使用して誤検知をファイリングします(FPはバックグラウンドと優れた相関関係があります)。
また、輪郭ベースの追跡が必要な場合は、opencvサンプルフォルダーにあるblobtrack_sample.cppを試してください。
読書人トラッカーを見たことがありますか。これは研究プロジェクトでしたが、オープンソースで非常に効果的です。 ここ を参照してください
おそらく現在はまったく最先端ではありませんが、ソースは入手可能であり、非常によく構成されています。
トラッキングの「モーションモデル」コンポーネントがありません。カルマン/粒子フィルターが役立つはずです。カルマンが好きです。