OpenCV実戦 (33) - OpenCVとディープラーニングの衝突

0. 序文

ディープ ラーニングは機械学習のサブ分野であり、従来のニューラル ネットワークと畳み込みニューラル ネットワークに基づいて、音声認識、テキスト認識、画像分類などの分野で人間のレベルに近い、またはそれを超える精度を達成できます。OpenCV深層学習モジュールが基本モジュールとしてコア アルゴリズムに追加され、CPUとの助けを借りてGPUパフォーマンスが向上します。

1. ディープラーニングと畳み込みニューラルネットワーク

現実世界の問題に適用された場合の機械学習アルゴリズムの優れたパフォーマンスは、関連するアプリケーションに新しいアイデアを提供します。ディープ ラーニングはニューラル ネットワーク理論に基づいています。ディープ ラーニングの急速な発展は主に次の理由によるものです。まず、利用可能なコンピューティング能力により大規模なニューラル ネットワークの展開が可能になり、困難な問題の解決が可能になります。ニューラル ネットワーク (パーセプトロン) の生成 レイヤーは 1 つだけで、調整が必要な重みパラメーターはわずかしかありませんが、今日のネットワークには、最適化が必要なレイヤーが数百あり、パラメーターが数千万ある場合があります (そのため、ディープ ネットワークという名前が付けられています)。 ; 第二に、大量のデータによりニューラル ネットワークのトレーニングが可能になります。優れたパフォーマンスを得るために、ディープ ネットワークでは数千、さらには数百万のラベル付きサンプルが必要です (これは、最適化する必要があるパラメーターの数が非常に多いためです)。
ディープ ネットワークの最も重要な分野は、畳み込み演算に基づく畳み込みニューラル ネットワーク ( Convolutional Neural NetworksCNN) であり、学習されるパラメータは、ネットワークを構成するすべてのフィルター カーネルの値です。これらのフィルターは複数のネットワーク レイヤーに編成されており、初期のネットワーク レイヤーでは線や角などの基本的なオブジェクトの形状を抽出でき、後のレイヤーでは目、口、髪などのより複雑なパターンを徐々に検出できます。
OpenCVディープ ニューラル ネットワーク モジュールが含まれています。主に、他の機械学習ライブラリ ( TensorFlow、 、Caffeなど) を使用してTorchトレーニングされたディープ ネットワークをインポートするために使用されます。

2.顔検出にディープラーニングを使用する

OpenCVこのセクションでは、事前トレーニングされた深層学習の実行を使用して で顔検出を行う方法を学びます。事前トレーニングされた顔検出モデルをダウンロードし、OpenCVそのメソッドを使用してモデルをインポートし、入力画像または画像フレームを深層学習モデルに必要な構造に変換する方法を理解する必要があります。でのディープ ラーニング モデルの使用は非常に簡単です。必要なのは、事前トレーニングされたモデル ファイルをロードして、その基本構成を理解することだけです
まず、事前トレーニング済みのディープ ニューラル ネットワーク モデルをダウンロードするOpenCV必要があります。次に、顔検出を例として、でディープ ニューラル ネットワーク モデルを使用する方法を説明します。OpenCV

2.1 SSD の概要

このセクションでは、ワンショット検出器 ( Single-Shot DetectorSSD)DNNアルゴリズムを使用して画像内の顔を検出します。SSDこのアルゴリズムは、画像の処理中に境界ボックスとカテゴリを同時に予測します。SSD DNN構造は次のとおりです。

  • 300x300サイズの入力画像を使用する
  • 入力画像は複数の畳み込み層を通過して、さまざまなスケールでさまざまな特徴を取得します。
  • 特徴マップごとに、3x3畳み込みフィルターを使用してデフォルトの境界ボックスのセットを評価します。
  • 各デフォルトの境界ボックスを評価するときに、境界ボックスのオフセットとクラス確率を予測します。

モデルのアーキテクチャは次のとおりです。

SSDネットワークアーキテクチャ
SSDDNNは複数のカテゴリを分類するために使用できるアルゴリズムであり、修正されたネットワークを使用して顔検出を実行できます。OpenCVDNNモデルを定義および使用するための最も重要な関数はblobFomImagereadNetFromsetInput、およびですforward関数を
使用して入力画像を に変換します。呼び出し方法は次のとおりです。blobFromImageblob

blobFromImage(image, scaleFactor, size, mean, swapRB, crop);

blobFromImage関数内の各パラメータの意味は次のとおりです。

  • image:入力画像
  • size:出力画像のサイズ
  • mean: 画像から減算されるスカラー。平均減算が使用される場合、swapRB = True結果は次のようになります。(mean-R, mean-G, mean-B)
  • scalefactor: 画像値の倍率
  • swapRB3:チャネルイメージの最初と最後のチャネルを交換する必要があるかどうかを示すフラグビット
  • crop: サイズ変更後に画像をトリミングする必要があるかどうかを示すフラグビット

モデルをロードするには、readFrom[type]インポーターを使用して、次の機械学習ライブラリを使用してトレーニングされたモデルをインポートできます。

  • カフェ
  • テンソルフロー
  • パイトーチ
  • 難しい

深層学習モデルをインポートして input を作成した後、関数 classを使用して入力をニューラル ネットワークに入力blobできます。最初のパラメーターは入力、2 番目のパラメーターは入力層の名前です (入力層が複数ある場合)入力レイヤーの場合は、レイヤー名を入力して指定する必要があります)。最後に、関数が呼び出されて、入力に対して前方計算が実行され、予測結果が形式で返されます。顔検出アルゴリズムでは、検出されたオブジェクトの数と各検出の結果データ (バウンディング ボックス データと信頼度) が返されます。NetsetInputblobblobforwardblobcv::Mat
cv::Matdetection.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 NetworkDNN) を使用して、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構造体に変換する必要がありますblobOpenCVcv::Matblob

        //! [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()方法を学び、次に、コンピュータービジョン プロジェクトを構築するための実践的な学習を通じて、一般的な深層学習モデル アーキテクチャを物体検出タスクに適用しました。OpenCVblobOpenCV

シリーズリンク

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 と指向勾配ヒストグラムの使用 オブジェクトの実行検出

おすすめ

転載: blog.csdn.net/LOVEmy134611/article/details/133723366