OpenCV実戦 (33) - OpenCVとディープラーニングの衝突
0. 序文
ディープ ラーニングは機械学習のサブ分野であり、従来のニューラル ネットワークと畳み込みニューラル ネットワークに基づいて、音声認識、テキスト認識、画像分類などの分野で人間のレベルに近い、またはそれを超える精度を達成できます。OpenCV
深層学習モジュールが基本モジュールとしてコア アルゴリズムに追加され、CPU
との助けを借りてGPU
パフォーマンスが向上します。
1. ディープラーニングと畳み込みニューラルネットワーク
現実世界の問題に適用された場合の機械学習アルゴリズムの優れたパフォーマンスは、関連するアプリケーションに新しいアイデアを提供します。ディープ ラーニングはニューラル ネットワーク理論に基づいています。ディープ ラーニングの急速な発展は主に次の理由によるものです。まず、利用可能なコンピューティング能力により大規模なニューラル ネットワークの展開が可能になり、困難な問題の解決が可能になります。ニューラル ネットワーク (パーセプトロン) の生成 レイヤーは 1 つだけで、調整が必要な重みパラメーターはわずかしかありませんが、今日のネットワークには、最適化が必要なレイヤーが数百あり、パラメーターが数千万ある場合があります (そのため、ディープ ネットワークという名前が付けられています)。 ; 第二に、大量のデータによりニューラル ネットワークのトレーニングが可能になります。優れたパフォーマンスを得るために、ディープ ネットワークでは数千、さらには数百万のラベル付きサンプルが必要です (これは、最適化する必要があるパラメーターの数が非常に多いためです)。
ディープ ネットワークの最も重要な分野は、畳み込み演算に基づく畳み込みニューラル ネットワーク ( Convolutional Neural Networks
、CNN
) であり、学習されるパラメータは、ネットワークを構成するすべてのフィルター カーネルの値です。これらのフィルターは複数のネットワーク レイヤーに編成されており、初期のネットワーク レイヤーでは線や角などの基本的なオブジェクトの形状を抽出でき、後のレイヤーでは目、口、髪などのより複雑なパターンを徐々に検出できます。
OpenCV
ディープ ニューラル ネットワーク モジュールが含まれています。主に、他の機械学習ライブラリ ( TensorFlow
、 、Caffe
など) を使用してTorch
トレーニングされたディープ ネットワークをインポートするために使用されます。
2.顔検出にディープラーニングを使用する
OpenCV
このセクションでは、事前トレーニングされた深層学習の実行を使用して で顔検出を行う方法を学びます。事前トレーニングされた顔検出モデルをダウンロードし、OpenCV
そのメソッドを使用してモデルをインポートし、入力画像または画像フレームを深層学習モデルに必要な構造に変換する方法を理解する必要があります。でのディープ ラーニング モデルの使用は非常に簡単です。必要なのは、事前トレーニングされたモデル ファイルをロードして、その基本構成を理解することだけです
。まず、事前トレーニング済みのディープ ニューラル ネットワーク モデルをダウンロードするOpenCV
必要があります。次に、顔検出を例として、でディープ ニューラル ネットワーク モデルを使用する方法を説明します。OpenCV
2.1 SSD の概要
このセクションでは、ワンショット検出器 ( Single-Shot Detector
、SSD
)DNN
アルゴリズムを使用して画像内の顔を検出します。SSD
このアルゴリズムは、画像の処理中に境界ボックスとカテゴリを同時に予測します。SSD DNN
構造は次のとおりです。
300x300
サイズの入力画像を使用する- 入力画像は複数の畳み込み層を通過して、さまざまなスケールでさまざまな特徴を取得します。
- 特徴マップごとに、
3x3
畳み込みフィルターを使用してデフォルトの境界ボックスのセットを評価します。 - 各デフォルトの境界ボックスを評価するときに、境界ボックスのオフセットとクラス確率を予測します。
モデルのアーキテクチャは次のとおりです。
SSD
DNN
は複数のカテゴリを分類するために使用できるアルゴリズムであり、修正されたネットワークを使用して顔検出を実行できます。でOpenCV
、DNN
モデルを定義および使用するための最も重要な関数はblobFomImage
、readNetFrom
、setInput
、およびですforward
。関数を
使用して入力画像を に変換します。呼び出し方法は次のとおりです。blobFromImage
blob
blobFromImage(image, scaleFactor, size, mean, swapRB, crop);
blobFromImage
関数内の各パラメータの意味は次のとおりです。
image
:入力画像size
:出力画像のサイズmean
: 画像から減算されるスカラー。平均減算が使用される場合、swapRB = True
結果は次のようになります。(mean-R, mean-G, mean-B)
scalefactor
: 画像値の倍率swapRB
3
:チャネルイメージの最初と最後のチャネルを交換する必要があるかどうかを示すフラグビットcrop
: サイズ変更後に画像をトリミングする必要があるかどうかを示すフラグビット
モデルをロードするには、readFrom[type]
インポーターを使用して、次の機械学習ライブラリを使用してトレーニングされたモデルをインポートできます。
- カフェ
- テンソルフロー
- パイトーチ
- 難しい
深層学習モデルをインポートして input を作成した後、関数 classを使用して入力をニューラル ネットワークに入力blob
できます。最初のパラメーターは入力、2 番目のパラメーターは入力層の名前です (入力層が複数ある場合)入力レイヤーの場合は、レイヤー名を入力して指定する必要があります)。最後に、関数が呼び出されて、入力に対して前方計算が実行され、予測結果が形式で返されます。顔検出アルゴリズムでは、検出されたオブジェクトの数と各検出の結果データ (バウンディング ボックス データと信頼度) が返されます。Net
setInput
blob
blob
forward
blob
cv::Mat
cv::Mat
detection.size[2]
detect.size[3]
Column 0
:物体の存在に対する確信Column 1
: 境界ボックスの信頼度Column 2
:検出された顔の信頼度Column 3
:左下の境界ボックスのX
座標Column 4
:左下の境界ボックスのY
座標Column 5
:右上隅のバウンディングボックスX
の座標Column 6
:右上隅のバウンディングボックスY
の座標
境界ボックスは画像のサイズに関連しており、画像内に境界ボックスの四角形を描画したい場合は、画像のサイズを掛ける必要があります。
2.2 SSD を使用して顔検出を実行する
(1) 顔検出器モデルをダウンロードしdata
、フォルダーに保存します 通常、重み付けファイルdeploy.prototxt
とネットワーク構造ファイルの2 つのファイルが必要ですres10_300x300_ssd_iter_140000.caffemodel
。顔検出アルゴリズムを使用するには、deploy.prototxt
ネットワーク構造を定義するファイルとネットワークの重みを含むファイルをダウンロードしますres10_300x300_ssd_iter_140000.caffemodel
。
(2)事前トレーニングされたディープ ニューラル ネットワーク ( Deep Neural Network
、DNN
) を使用して、face_detection.cpp
ファイルを作成し、必要なライブラリをインポートします。
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
using namespace cv::dnn;
(3)DNN
アルゴリズムで使用する必要があるグローバル変数を宣言します。これは、入力ネットワーク、前処理されたデータ、ロードされるファイル名を定義します。
float confidenceThreshold = 0.5;
String modelConfiguration = "deploy.prototxt";
String modelBinary = "res10_300x300_ssd_iter_140000.caffemodel";
const size_t inWidth = 300;
const size_t inHeight = 300;
const double inScaleFactor = 1.0;
const Scalar meanVal(104.0, 177.0, 123.0);
(4)main
関数を作成し、OpenCV dnn::Net
クラスにモデルをロードします。
int main(int argc, char **argv) {
dnn::Net net = readNetFromCaffe(modelConfiguration, modelBinary);
(5)empty()
関数を呼び出して、DNN
正しくロードされたかどうかを確認します。
if (net.empty()) {
cerr << "Can't load network by using the following files: " << endl;
cerr << "prototxt: " << modelConfiguration << endl;
cerr << "caffemodel: " << modelBinary << endl;
cerr << "Models are available here:" << endl;
cerr << "<OPENCV_SRC_DIR>/samples/dnn/face_detector" << endl;
cerr << "or here:" << endl;
cerr << "https://github.com/opencv/opencv/tree/master/samples/dnn/face_detector" << endl;
exit(-1);
}
(6)正しくロードされていればDNN
、画像フレームのキャプチャを開始できます。アプリケーションの入力パラメータの数を確認して、デフォルト値をロードする必要があるか、ビデオ ファイルを処理する必要があるかを判断します。
VideoCapture cap;
if (argc==1) {
cap = VideoCapture(0);
if(!cap.isOpened()) {
cout << "Couldn't find default camera" << endl;
return -1;
}
} else {
cap.open(argv[1]);
if(!cap.isOpened()) {
cout << "Couldn't open image or video: " << argv[1] << endl;
return -1;
}
}
(7)ビデオ キャプチャ オブジェクトが正しく開かれている場合は、メイン ループを開始して各ビデオ フレームを取得できます。
for(;;)
{
Mat frame;
cap >> frame; // 获取新帧
if (frame.empty()) {
waitKey();
break;
}
(8)DNN
アルゴリズムで画像を処理します。DNN
アルゴリズムに入力する画像を準備するには、blobFromImage
関数を使用して、格納するクラスを使用してOpenCV Mat
構造体をDNN
構造体に変換する必要があります。blob
OpenCV
cv::Mat
blob
//! [Prepare blob]
Mat inputBlob = blobFromImage(frame, inScaleFactor,
Size(inWidth, inHeight), meanVal, false, false); //Convert Mat to batch of images
(9)ビデオ フレームを に変換した後blob
、それを に入力しDNN
、順伝播関数を使用してforward
検出します。
//! [Set input blob]
net.setInput(inputBlob, "data"); // 设定网络输入
//! [Make forward pass]
Mat detection = net.forward("detection_out"); // 计算输出
Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>());
(10)画像内で検出された顔ごとに長方形のボックスを描画し、その信頼度を与えます。
for(int i = 0; i < detectionMat.rows; i++)
{
float confidence = detectionMat.at<float>(i, 2);
if(confidence > confidenceThreshold)
{
int xLeftBottom = static_cast<int>(detectionMat.at<float>(i, 3) * frame.cols);
int yLeftBottom = static_cast<int>(detectionMat.at<float>(i, 4) * frame.rows);
int xRightTop = static_cast<int>(detectionMat.at<float>(i, 5) * frame.cols);
int yRightTop = static_cast<int>(detectionMat.at<float>(i, 6) * frame.rows);
Rect object((int)xLeftBottom, (int)yLeftBottom,
(int)(xRightTop - xLeftBottom),
(int)(yRightTop - yLeftBottom));
rectangle(frame, object, Scalar(0, 255, 0));
stringstream ss;
ss.str("");
ss << confidence;
String conf(ss.str());
String label = "Face: " + conf;
int baseLine = 0;
Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
rectangle(frame, Rect(Point(xLeftBottom, yLeftBottom - labelSize.height),
Size(labelSize.width, labelSize.height + baseLine)),
Scalar(255, 255, 255), FILLED);
putText(frame, label, Point(xLeftBottom, yLeftBottom),
FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0,0,0));
}
}
上記のコードを実行すると、以下のような検出結果が得られます。
3. 完全なコード
完全なコードはface_detection.cpp
次のとおりです。
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
using namespace cv::dnn;
float confidenceThreshold = 0.5;
String modelConfiguration = "deploy.prototxt";
String modelBinary = "res10_300x300_ssd_iter_140000.caffemodel";
const size_t inWidth = 300;
const size_t inHeight = 300;
const double inScaleFactor = 1.0;
const Scalar meanVal(104.0, 177.0, 123.0);
int main(int argc, char **argv) {
dnn::Net net = readNetFromCaffe(modelConfiguration, modelBinary);
if (net.empty()) {
cerr << "Can't load network by using the following files: " << endl;
cerr << "prototxt: " << modelConfiguration << endl;
cerr << "caffemodel: " << modelBinary << endl;
cerr << "Models are available here:" << endl;
cerr << "<OPENCV_SRC_DIR>/samples/dnn/face_detector" << endl;
cerr << "or here:" << endl;
cerr << "https://github.com/opencv/opencv/tree/master/samples/dnn/face_detector" << endl;
exit(-1);
}
VideoCapture cap;
if (argc==1) {
cap = VideoCapture(0);
if(!cap.isOpened()) {
cout << "Couldn't find default camera" << endl;
return -1;
}
} else {
cap.open(argv[1]);
if(!cap.isOpened()) {
cout << "Couldn't open image or video: " << argv[1] << endl;
return -1;
}
}
for(;;)
{
Mat frame;
cap >> frame; // 获取新帧
if (frame.empty()) {
waitKey();
break;
}
//! [Prepare blob]
Mat inputBlob = blobFromImage(frame, inScaleFactor,
Size(inWidth, inHeight), meanVal, false, false); //Convert Mat to batch of images
//! [Set input blob]
net.setInput(inputBlob, "data"); // 设定网络输入
//! [Make forward pass]
Mat detection = net.forward("detection_out"); // 计算输出
Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>());
for(int i = 0; i < detectionMat.rows; i++)
{
float confidence = detectionMat.at<float>(i, 2);
if(confidence > confidenceThreshold)
{
int xLeftBottom = static_cast<int>(detectionMat.at<float>(i, 3) * frame.cols);
int yLeftBottom = static_cast<int>(detectionMat.at<float>(i, 4) * frame.rows);
int xRightTop = static_cast<int>(detectionMat.at<float>(i, 5) * frame.cols);
int yRightTop = static_cast<int>(detectionMat.at<float>(i, 6) * frame.rows);
Rect object((int)xLeftBottom, (int)yLeftBottom,
(int)(xRightTop - xLeftBottom),
(int)(yRightTop - yLeftBottom));
rectangle(frame, object, Scalar(0, 255, 0));
stringstream ss;
ss.str("");
ss << confidence;
String conf(ss.str());
String label = "Face: " + conf;
int baseLine = 0;
Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
rectangle(frame, Rect(Point(xLeftBottom, yLeftBottom - labelSize.height),
Size(labelSize.width, labelSize.height + baseLine)),
Scalar(255, 255, 255), FILLED);
putText(frame, label, Point(xLeftBottom, yLeftBottom),
FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0,0,0));
}
}
imshow("detections", frame);
if (waitKey(1) >= 0) break;
}
return 0;
}
まとめ
この記事では、まず とcv2::dnn::blobFromImage()
の関数を介して入力されるネットワークを構築するcv2::dnn::blobFromImages()
方法を学び、次に、コンピュータービジョン プロジェクトを構築するための実践的な学習を通じて、一般的な深層学習モデル アーキテクチャを物体検出タスクに適用しました。OpenCV
blob
OpenCV
シリーズリンク
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) — 高速な特徴点検出
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 と機械学習の衝突
OpenCV の実践 (31) — カスケード Haar 特徴に基づくターゲット検出
OpenCV の実践 (32) — SVM と指向勾配ヒストグラムの使用 オブジェクトの実行検出