OpenCV 戦闘 (31) - カスケードされた Haar 機能に基づくターゲット検出
0. 序文
機械学習の基礎セクションでは、機械学習のいくつかの基本概念を紹介し、さまざまなクラスのサンプルを使用して分類器を構築しました。ただし、分類器をトレーニングするこのアプローチでは、すべてのサンプルの表現を保存し、最も近いラベル付き点 (最近傍点) を見て新しいインスタンスのラベルを予測する必要があります。ほとんどの機械学習手法では、トレーニングはサンプルをループして機械学習モデルを構築する反復プロセスです。より多くのサンプルを使用すると、結果として得られる分類器のパフォーマンスが徐々に向上します。モデルのパフォーマンスが事前に設定された値に達するか、現在のトレーニング データセットからそれ以上の改善が得られなくなると、学習プロセスは停止します。このセクションでは、上記のプロセスに従う機械学習アルゴリズムであるカスケード分類器を紹介します。
1.ハール特徴画像表現
この分類器に進む前に、まずHaar
特徴画像表現を紹介します。優れた画像表現が堅牢な分類器を生成するために不可欠な要素であることはすでにわかっています。
分類器を生成する最初のステップは、認識されるオブジェクト カテゴリのさまざまなインスタンスを含む画像サンプルの大規模なコレクションを取得することです。サンプルの表現方法は、サンプルで構築された分類器のパフォーマンスに重要な影響を与えます。 。ピクセルレベルの表現は通常、オブジェクトの各クラスの固有の特性を確実に記述するには低レベルすぎます。逆に、複数のスケールで画像内に存在する固有のパターンを説明できる表現は、画像の特徴をよりよく表現できます。これは、Haar
features( ) の基本的な考え方です。features( ) はすべて、変換された基底関数( )から派生するため、Haar features
クラスHaar
features( ) とも呼ばれます。特徴はピクセルの小さな長方形領域を定義し、後で単純な減算によって比較されます。通常、 、 、およびtraits という 3 つの異なる構成が利用可能です。Haar-like features
Haar
Haar transform basis functions
Haar
2-矩形
3-矩形
4-矩形
これらの特徴は任意のサイズにすることができ、表現される画像の任意の領域に適用できます。たとえば、以下の画像には、顔の画像に適用された2 つのHaar
特徴が含まれています。
表現の構築はHaar
、任意のタイプ、サイズ、位置の複数のHaar
特徴を選択し、それらを画像に適用することで構成されます。選択された一連のHaar
特徴から取得された特定の一連の値が画像表現を構成します。次に、どの機能セットを選択するかを決定することが課題となります。実際、これらのHaar
機能の一部は、さまざまなオブジェクトを区別するために他の機能よりも効果的である可能性があります。たとえば、顔画像サンプルでは、3-矩形
Haar
(上の図に示すように) 目の間に特徴を適用するとより効果的になる場合があります。もちろん、可能性のある機能は数十万あるHaar
ため、手動で選択することは困難です。機械学習手法を使用して、特定のオブジェクト クラスに最も関連性の高い機能を選択する必要があります。
2. カスケードされた Haar 特徴に基づくバイナリ分類分類器
このセクションでは、バイナリ分類器の生成に使用して拡張カスケード機能を構築する方法を学びますOpenCV
。バイナリ分類子は、特定のクラス (顔の画像など) のインスタンスを他のクラス (顔を含まない画像など) から識別できる分類子です。つまり、分類タスクには 2 つのクラスしかありません。この場合、ポジティブ サンプル (つまり、顔画像) とネガティブ サンプル (つまり、顔以外の画像) を使用して 2 つのカテゴリを表します。このセクションで使用される分類子は、一度に 1 つずつ適用される一連の単純な分類子で構成されています。カスケード分類器の各段階では、小さな特徴セットに対して取得された値に基づいて、オブジェクトを拒否するかどうかが迅速に決定されます。このカスケード構造により、各ステージでより正確な決定を行って前のステージの分類器のパフォーマンスを向上 (またはブースト) することにより、分類器のパフォーマンスが向上します。このアプローチの主な利点は、カスケードの初期段階が単純な検出で構成され、その後無関係なクラス インスタンスをすぐに拒否できるため、カスケード分類子の計算が高速化されることです。これは、画像をスキャンしてオブジェクトのクラスを検索するときに、ほとんどのテスト対象のサブウィンドウは、関心のあるカテゴリに属していません。このようにして、少数のウィンドウのみがすべての段階を経て、承認または拒否を決定する必要があります。
(1)ブースト カスケード分類器をトレーニングするために、OpenCV
これらの操作を実行するためのツールが提供されています。ライブラリがインストールされるとOpenCV
、2 つの実行可能ファイルが作成され、次bin
のディレクトリに配置されます。opencv_createsamples
opencv_traincascade
(2)分類器を学習させる場合、まずサンプルを収集する必要があります。陽性サンプルと偽サンプルは、ターゲット クラスのインスタンスの画像で構成されています。このセクションでは、交通標識を認識するように分類器をトレーニングすることを目的としています。使用される陽性サンプルは次のとおりです:
(3)使用される陽性サンプルのリストはsign.txt
、画像ファイル名と境界ボックスの座標を含む という名前のテキスト ファイルで指定されます。
1.png 1 0 0 64 64
2.png 1 0 0 64 64
3.png 1 0 0 64 64
4.png 1 0 0 64 64
5.png 1 0 0 64 64
6.png 1 0 0 64 64
7.png 1 0 0 64 64
8.png 1 0 0 64 64
9.png 1 0 0 64 64
(4)ファイル名の後の最初の数字は画像に含まれる陽性サンプルの数、次の 2 つの値は陽性サンプルを含む境界ボックスの左上隅の座標、最後の 2 つの値ははオブジェクトの幅と高さです。このセクションでは、元の画像からポジティブ サンプルを抽出しました。そのため、各ファイルにはサンプルが 1 つだけあり、左上隅の座標は です(0, 0)
。次に、抽出ツールを実行してポジティブ サンプル ファイルを作成します。
opencv_createsamples -info sign.txt -vec sign.vec -w 24 -h 24 -num 9
(5)上記のコードは、stop.vec
入力テキスト ファイルで指定されたすべての陽性サンプルを含む出力ファイルを作成します。サンプル サイズ ( 24×24
) を元のサイズ ( 64×64
) より小さくすることに注意してください。抽出ツールはすべてのサンプルのサイズを指定されたサイズに変更します。通常、Haar
機能はテンプレートが小さいほどうまく機能しますが、これはケースバイケースで検証する必要があります。
(6)ネガティブ サンプルは、対象カテゴリのインスタンスを含まない画像です (このセクションでは、交通標識を含まない画像)。とりわけ、これらの画像には、分類子が期待するあらゆる種類の画像が含まれている必要があります。トレーニング ツールはこれらの画像からランダムな負のサンプルを抽出できるため、これらの負のサンプルは任意のサイズにすることができます。
(7)陽性サンプルと陰性サンプルが準備されたら、カスケード分類器をトレーニングできます。
opencv_traincascade -data classifier -vec sign.vec -bg neg.txt -numPos 9 -numNeg 18 -numStages 20 -minHitRate 0.95 -maxFalseAlarmRate 0.5 -w 24 -h 24
数千のサンプルを含む複雑な分類器では、トレーニング プロセスに長い時間がかかり、場合によっては数日かかる場合があることに注意してください。hit rate
トレーニング プログラムを実行すると、カスケード分類器はトレーニングのステージが完了するたびにパフォーマンス レポートを出力し、分類器の現在のヒット率 ( 、 ) を示します。これは、カスケード分類器が現在受け入れている陽性サンプルの割合です (つまりHR
、 、これらは正の例 (真の例とも呼ばれます) として正しく識別され、この数値をできるだけ 1.0 に近づけたいと考えています。このレポートには、現在の偽陽性率 ( false alarm rate
、FAR
)、陽性と誤分類された検査陰性者 (偽陽性とも呼ばれる) の数も示されており、この数値をできる限り近づけたいと考えています0.0
。
分類器でのトレーニングには数秒しかかかりません。結果として得られる分類器の構造は、XML
トレーニング段階で生成されるファイルに記述されます。トレーニングが完了すると、分類器を使用できるようになり、任意のサンプルを入力することができ、分類器は予測結果を出力します。
(8)このセクションでは、24×24
の画像を使用してカスケード分類器を訓練しましたが、一般に、任意のサイズの画像内の任意の位置にクラス オブジェクトのインスタンスがあるかどうかを調べる必要があります。これを達成するには、入力画像をスキャンし、サンプル サイズの可能なすべてのウィンドウを抽出する必要があります。分類器が十分に正確であれば、ターゲット オブジェクトを含むウィンドウのみが肯定的な予測を返します。ただし、これはポジティブ サンプルが適切なサイズの場合にのみ機能するため、複数のスケールでオブジェクト インスタンスを検出するには、画像ピラミッドを構築して、元の画像のサイズを各レベルでスケーリングする必要があります。このようにして、ピラミッドを下に向かっていくと、ターゲットの例が大きくなり、最終的には適切なサイズにスケールアップされます。これは時間のかかるプロセスですが、OpenCV
このプロセスを実装するためのクラスが提供されています。XML
まず、適切なファイルをロードして分類子を構築する必要があります。
cv::CascadeClassifier cascade;
if (!cascade.load("classifier/cascade.xml")) {
std::cout << "Error when loading the cascade classfier!" << std::endl;
return -1;
}
(9)次に、入力画像を使用して検出メソッドを呼び出します。
// 预测图片标签
std::vector<cv::Rect> detections;
cascade.detectMultiScale(inputImage, // 输入图像
detections, // 检测结果
1.1, // 尺度缩放因子
1, // 所需邻居检测数
0, // 标志位
cv::Size(48, 48), // 要检测的最小对象大小
cv::Size(200, 200)); // 要检测的最大对象大小
std::cout << "detections= " << detections.size() << std::endl;
for (int i = 0; i < detections.size(); i++)
cv::rectangle(inputImage, detections[i], cv::Scalar(255, 255, 255), 2);
cv::imshow("Sign detection", inputImage);
(10)cv::Rect
はインスタンスのベクトルを返します。検出結果を視覚化するには、入力画像上にこれらの四角形を描画するだけです。
for (int i = 0; i < detections.size(); i++)
cv::rectangle(inputImage, detections[i], cv::Scalar(255, 255, 255), 2);
cv::imshow("Sign detection", inputImage);
分類子を使用して画像上でテストすると、次の結果が得られます。
3. カスケード分類器アルゴリズムのフロー
OpenCV
前のセクションでは、正のサンプルと負のサンプルを使用してカスケード分類器を構築する方法を紹介しましたが、次に、カスケード分類器をトレーニングする基本的な手順を紹介します。私たちが導入したカスケード分類器は、Haar
この機能を使用してトレーニングされましたが、他の画像特徴を使用してカスケード分類器を構築することもできます。
カスケード分類器の背後には 2 つの中心的な考え方があります。1 つ目は、より強力なパフォーマンスを持つ分類器は、より弱いパフォーマンスを持ついくつかの分類器 (つまり、単純な特徴に基づく分類器) を組み合わせることによって構築できること、2 つ目は、マシン ビジョンでは、ネガティブ サンプルが出現する確率がポジティブ サンプルよりも頻繁に出現することです。段階を分けることで効果的に分類することができます。初期の段階では明らかな否定的な例がすぐに拒否され、後の段階ではより複雑なサンプルに対してより洗練された決定が行われます。上記の考えに基づいて、リフティング カスケード学習アルゴリズム ( boosted cascade learning algorithm
) の導入を続けます。使用するアルゴリズムは、boosting
のバリアント アルゴリズムに基づいていますAdaBoost
。
このセクションでは、Haar
機能を使用して弱分類器を構築します。機能 (指定されたタイプ、サイズ、位置) が適用されると、値が取得されますHaar
。次に、その特徴値に基づいて否定的なインスタンスと肯定的なインスタンスを最もよく分類するしきい値を見つけることによって、単純な分類子が取得されます。最適なしきい値を見つけるために、いくつかの正および負のサンプルを使用できます (opencv_traincascade
使用される正および負のサンプルの数は-numPos
および-numNeg
パラメーターで指定されます)。考えられる特徴が多数あるためHaar
、すべてをチェックしてサンプル セットを分類できる最適な特徴を選択すると、明らかに、この基本的な分類子は間違い、つまり誤分類を犯す可能性があるため、そのような分類子をいくつか構築する必要があります。分類子は、新しい分類子を検索するたびに繰り返し追加されます。Haar
特徴を選択すると、最適な分類が与えられます。ただし、各反復では現在誤分類されているサンプルに焦点を当てたいため、誤分類されたサンプルに高い重みを与えることで分類パフォーマンスを測定します。したがって、最終的には単純な分類器のセットが得られ、次にこれらの弱分類器の重み付き合計から強力な分類器を構築します (つまり、よりパフォーマンスの高い分類器にはより高い重みが与えられます)。このアプローチを使用すると、数百の単純な特徴を組み合わせることで、優れたパフォーマンスを備えた強力な分類器を取得できます。
ただし、トレーニングの初期段階では、多数の弱分類器から強い分類器を形成することは望ましくありません。代わりに、すべての肯定的な例を保持しながら、明らかな否定的な例をすぐに拒否するために、少数の特徴のみを使用するHaar
単純な分類子を見つける必要があります。古典的な形式では、AdaBoost
偽陰性 (つまり、陰性サンプルとして誤って分類された陽性サンプル) と偽陽性 (つまり、陽性サンプルとして誤って分類された陰性サンプル) の数を数えることによって、合計の分類誤差を最小限に抑えることが目的です。AdaBoost
私たちの目標は、偽陽性率を最小限に抑えながら、ほとんどまたはすべての陽性例を正しく分類することです。偽陽性率は、真の例を予測するときにより高い報酬を与えるように修正できます。したがって、分類器のカスケードをトレーニングする各段階で、最小ヒット率と最大誤検知率という 2 つの基準を設定する必要があります。 では、 (デフォルト値 ) および(デフォルト値) パラメーターopencv_traincascade
を使用して指定されます。これら 2 つのパフォーマンス基準が満たされるまで、さまざまな段階で機能が追加されます。肯定的なインスタンスが次のステージに確実に進むことができるように、最小ヒット率を高く設定する必要があります。肯定的なインスタンスが特定のステージで拒否された場合、拒否された肯定的なインスタンスは回復できないことに注意してください。したがって、複雑さの低い分類器の生成を容易にするために、最大誤検知率を高く設定する必要があります。そうしないと、パフォーマンス基準を満たすためにより多くの機能が必要となり、弱分類器をトレーニングするという以前の考え方に矛盾します。したがって、優れたカスケード分類器は、初期段階ではより少ない特徴の構成を使用する必要があり、カスケードが増加するにつれて、各段階の特徴の数もそれに応じて増加します。では、各ステージのフィーチャの最大数は次を使用します。-minHitRate
0.995
-maxFalseAlarmRate
0.5
Haar
Haar
opencv_traincascade
-maxWeakCount
(デフォルトは100
) パラメータ設定、段数は-numStages
(デフォルトは20
) パラメータ設定を使用します。
トレーニングの新しい段階を開始するときは、新しいネガティブ サンプルを収集する必要があります。これは、提供されたネガティブ サンプル画像から抽出できます。難しいのは、これまでのすべての段階を通過した陰性サンプル (つまり、陽性として誤って分類されたサンプル) を見つけることです。トレーニングの段階が増えるほど、これらのネガティブ サンプルを収集することが難しくなります。そのため、分類器には多数のネガティブ サンプル画像を提供し、分類が難しい画像パッチからサンプルを抽出する必要があります (類似しているため)。陽性サンプルに)。さらに、特定の段階で新しい機能を追加せずに両方のパフォーマンス基準が満たされた場合、カスケード トレーニングは停止します。つまり、モデルをそのまま使用することも、より複雑なサンプルを供給することによって使用することもできます。逆に、パフォーマンス基準を満たせない場合、トレーニングは停止します。この場合、より単純なパフォーマンス基準を使用して新しいトレーニング手順を試行する必要があります。
N ステージで構成される分類器のカスケードでは、各ステージが前のカスケードされたステージの結果に基づいて構築されるため、分類n
器の全体的なパフォーマンスが少なくとも と よりも優れていることを簡単に示すことができます。たとえば、分類器の値が0.99 5 20 0.995^{20}であると想定して、 のデフォルト値を検討します。minHitRate
maxFalseAlarmRate
opencv_traincascade
0.99 520と 0の正解率 (命中率)。5 20 0.5^{20}0.5 _偽陽性率は20 。これは、90%
の陽性サンプルは正しく識別され、0.001%
の陰性サンプルは陽性サンプルとして誤分類されることを意味します。カスケードする場合、陽性サンプルの一部が失われるため、各段階で使用される指定されたサンプル数よりも多くの陽性サンプルを提供する必要があることに注意してください。上の例では、numPos
利用可能な陽性サンプルの数を に設定しました90%
。
重要な問題は、トレーニングに使用するサンプルの数はどれくらいかということです。これは特定のアプリケーションに応じて判断する必要がありますが、明らかに、ポジティブ サンプル セットはクラス インスタンスのほとんどをカバーするのに十分な大きさでなければなりません。ネガティブなイメージも関連性がある必要があり、通常の経験則は次のとおりですnumNeg = 2 * numPos
。このセクションでは、この機能を使用してカスケード分類器を構築する
方法を紹介しましたこのような特徴は、指向性勾配の特徴やヒストグラムなどHaar
の他の特徴を使用して構築することもできます。プログラムでは、パラメーターを使用してさまざまな特徴タイプを選択できます。ライブラリには、顔や顔の特徴などの検出に使用できる、事前にトレーニングされたカスケード分類器が多数含まれています。これらのファイルは、ソース ディレクトリのデータ ディレクトリにあります。LBP
opencv_traincascade
-featureType
OpenCV
XML
4. Haar カスケード検出器を使用した顔検出
事前トレーニングされた顔検出モデルはすでに で提供されており、必要なのは、適切なファイルOpenCV
を使用してクラスのインスタンスを作成することだけです。XML
cv::CascadeClassifier
cv::CascadeClassifier faceCascade;
if (!faceCascade.load("haarcascade_frontalface_default.xml")) {
std::cout << "Error when loading the face cascade classfier!" << std::endl;
return -1;
}
次に、Haar
この機能を使用して顔を検出するには:
faceCascade.detectMultiScale(picture, // 输入图像
detections, // 检测结果
1.1, // 尺度缩放因子
3, // 所需邻居检测数
0, // 标记位
cv::Size(48, 48), // 要检测的最小对象大小
cv::Size(200, 200)); // 要检测的最大对象大小
std::cout << "detections= " << detections.size() << std::endl;
同じプロセスを使用して人間の目を検出することもできます。
5. 完全なコード
完全なコードはdetectObjects.cpp
次のとおりです。
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>
int main() {
// 打开正样本
std::vector<cv::Mat> referenceImages;
referenceImages.push_back(cv::imread("1.png"));
referenceImages.push_back(cv::imread("2.png"));
referenceImages.push_back(cv::imread("3.png"));
referenceImages.push_back(cv::imread("4.png"));
referenceImages.push_back(cv::imread("5.png"));
referenceImages.push_back(cv::imread("6.png"));
referenceImages.push_back(cv::imread("7.png"));
referenceImages.push_back(cv::imread("8.png"));
referenceImages.push_back(cv::imread("9.png"));
// 组合图像
cv::Mat positveImages(2 * referenceImages[0].rows, 4 * referenceImages[0].cols, CV_8UC3);
for (int i = 0; i < 2; i++)
for (int j = 0; j < 4; j++) {
referenceImages[i * 2 + j].copyTo(positveImages(cv::Rect(j*referenceImages[i * 2 + j].cols, i*referenceImages[i * 2 + j].rows, referenceImages[i * 2 + j].cols, referenceImages[i * 2 + j].rows)));
}
cv::imshow("Positive samples", positveImages);
cv::Mat negative = cv::imread("n1.jpg");
cv::resize(negative, negative, cv::Size(), 0.33, 0.33);
cv::imshow("One negative sample", negative);
cv::Mat inputImage = cv::imread("sign.png");
cv::resize(inputImage, inputImage, cv::Size(), 0.5, 0.5);
cv::CascadeClassifier cascade;
if (!cascade.load("classifier/cascade.xml")) {
std::cout << "Error when loading the cascade classfier!" << std::endl;
return -1;
}
// 预测图片标签
std::vector<cv::Rect> detections;
cascade.detectMultiScale(inputImage, // 输入图像
detections, // 检测结果
1.1, // 尺度缩放因子
1, // 所需邻居检测数
0, // 标志位
cv::Size(48, 48), // 要检测的最小对象大小
cv::Size(200, 200)); // 要检测的最大对象大小
std::cout << "detections= " << detections.size() << std::endl;
for (int i = 0; i < detections.size(); i++)
cv::rectangle(inputImage, detections[i], cv::Scalar(255, 255, 255), 2);
cv::imshow("Sign detection", inputImage);
// 人脸检测
cv::Mat picture = cv::imread("girl.png");
cv::CascadeClassifier faceCascade;
if (!faceCascade.load("haarcascade_frontalface_default.xml")) {
std::cout << "Error when loading the face cascade classfier!" << std::endl;
return -1;
}
faceCascade.detectMultiScale(picture, // 输入图像
detections, // 检测结果
1.1, // 尺度缩放因子
3, // 所需邻居检测数
0, // 标记位
cv::Size(48, 48), // 要检测的最小对象大小
cv::Size(200, 200)); // 要检测的最大对象大小
std::cout << "detections= " << detections.size() << std::endl;
// 绘制检测到的对象边界框
for (int i = 0; i < detections.size(); i++)
cv::rectangle(picture, detections[i], cv::Scalar(255, 255, 255), 2);
// 检测眼睛
cv::CascadeClassifier eyeCascade;
if (!eyeCascade.load("haarcascade_eye.xml")) {
std::cout << "Error when loading the eye cascade classfier!" << std::endl;
return -1;
}
eyeCascade.detectMultiScale(picture, // 输入图像
detections, // 检测结果
1.1, // 尺度缩放因子
3, // 所需邻居检测数
0, // 标记位
cv::Size(24, 24), // 要检测的最小对象大小
cv::Size(36, 36)); // 要检测的最大对象大小
std::cout << "detections= " << detections.size() << std::endl;
// 绘制检测到的对象边界框
for (int i = 0; i < detections.size(); i++)
cv::rectangle(picture, detections[i], cv::Scalar(0, 0, 0), 2);
cv::imshow("Detection results", picture);
cv::waitKey();
return 0;
}
まとめ
Haar
特徴は、複数のスケールで画像に存在する固有のパターンの表現を記述し、画像の特徴をより適切に表現できます。カスケード分類器は、より強力なパフォーマンスの分類を取得するために、より弱いパフォーマンスの複数の分類器を組み合わせることによって構築されます。このセクションでは、関数の使用法を紹介しますcv::CascadeClassifier
。オブジェクト検出を実行するためのカスケードHaar
機能を実装します。
シリーズリンク
OpenCV実戦(1) - OpenCVと画像処理の基礎
OpenCV実戦(2) - OpenCVコアのデータ構造
OpenCV実戦(3) - 注目する画像領域
OpenCV実戦(4) - ピクセル操作
OpenCV実戦(5) ) - 画像操作詳細
OpenCV 実戦 (6) - OpenCV 戦略設計モード
OpenCV 実戦 (7) - OpenCV 色空間変換
OpenCV 実戦 (8) - ヒストグラム詳細
OpenCV 実戦 (9) - 逆投影ヒストグラムによる画像検出 内容
OpenCV実戦(10) - 積分画像の詳細説明
OpenCV実戦(11) - 形態変換の詳細説明
OpenCV実戦(12) - 画像フィルタリングの詳細説明
OpenCV実戦(13) - ハイパスフィルタとその応用
OpenCV 実戦 (14) —— イメージライン抽出
OpenCV 実戦 (15) —— 輪郭検出 詳細
OpenCV 実戦 (16) —— コーナー点検出 詳細
OpenCV 実戦 (17) —— FAST 特徴点検出
OpenCV 実戦 ( 18) —— 特徴マッチング
OpenCV 実戦 (19) ) —— 特徴記述子
OpenCV 実戦 (20) —— 画像投影関係
OpenCV 実戦 (21) — ランダムサンプルに基づく 一貫したマッチング画像
OpenCV 実戦 (22)——ホモグラフィーとその応用
OpenCV実戦(23)——カメラキャリブレーション
OpenCV実戦(24) - カメラ姿勢推定
OpenCV実戦(25) - 3Dシーン再構築
OpenCV実戦(26) - 映像シーケンス処理
OpenCV実戦(27) - 映像中の特徴点追跡
OpenCV実戦(28) - オプティカルフロー推定
OpenCV実戦(29) - 映像オブジェクト追跡
OpenCV実戦(30) - OpenCV と機械学習の出会い