OpenCV 戦闘 - ビデオの前景オブジェクトを抽出します

0. 序文

固定カメラでシーンを観察すると、背景はほとんど変化しません。この場合、本当に関心のあるオブジェクトは、シーン内の動くオブジェクトです。これらの前景オブジェクトを抽出するには、背景モデルを構築し、その背景モデルと現在のフレームを比較して前景オブジェクトを検出する必要があります。前景の抽出は、インテリジェントな監視アプリケーションの基本的な手順です。

1. ビデオの前景オブジェクトを抽出する

シーンの背景画像 (つまり、前景オブジェクトを含まないフレーム) が利用可能な場合は、単純な画像の差分によって現在のフレームの前景を抽出します。

cv::absdiff(backgroundImage,currentImage,foreground);

差が十分に大きいピクセルを前景ピクセルとみなします。ただし、ほとんどの場合、背景画像はすぐには入手できません。実際、特定の画像内に前景オブジェクトが存在しないことを保証することは困難です。さらに、背景シーンは、照明条件が変化したり、背景にオブジェクトが追加されたり、背景から削除されたりするなど、時間の経過とともに変化することがよくあります。
したがって、背景シーンのモデルを動的に構築する必要があります。これは、シーンを一定期間観察することによって実行できます。ほとんどの場合、背景のすべてのピクセル位置が表示されると想定しているため、すべての観測値の平均を単純に計算するのが良い戦略である可能性があります。ただし、これはさまざまな理由から実現できません。第一に、背景を計算する前に多数の画像を保存する必要があること、第二に、平均画像を計算するために画像を蓄積する場合、前景の抽出ができないこと、同時に、いつ、どれだけの画像を保存すべきかを決定することが不可能であることです。使用可能な背景モデルを計算するために累積され、さらに、前景オブジェクトの画像は平均背景の計算に影響します。
より良い戦略は、動的な背景をモデル化することです。これは、moving average移動平均 ( ) を計算することで実現できます。pt p_tの場合、最新の受信値の時間信号平均を計算する方法は次のとおりです。p指定された時間ですtttのピクセル値μ t − 1 μ_{t-1}メートルt 1は現在の平均値であり、次の式を使用して平均値を更新します:
ut = ( 1 − α ) μ t − 1 + α pt u_t=(1-\alpha)\mu_{t-1}+\alpha p_tあなた=( 1a ) mt 1+p
ああαは学習率を表し、現在の推定平均に対する現在の値の影響を定義します。値が大きいほど、移動平均は観測値の変化に速く適応します。背景モデルを構築するには、受信フレームの各ピクセルの移動平均を計算するだけです。ピクセルが前景ピクセルであるかどうかは、現在の画像と背景モデルの違いによって決まります。

(1)移動平均を使用して背景モデルを学習し、減算によって前景オブジェクトを抽出するクラスBGFGSegmentorを構築します。必要なプロパティは次のとおりです。

class BGFGSegmentor : public FrameProcessor {
    
    
    cv::Mat gray;           // 灰度图像
    cv::Mat background;     // 累积背景
    cv::Mat backImage;      // 当前背景图像
    cv::Mat foreground;     // 前景图像
    double learningRate;    // 学习率
    int threshold;          // 阈值

(2)現在のフレームと背景モデルを比較し、モデルを更新します。

    public:
        BGFGSegmentor() : threshold(10), learningRate(0.01) {
    
    }
        // 设置阈值
        void setThreshold(int t) {
    
    
            threshold= t;
        }
        // 设置学习率
        void setLearningRate(double r) {
    
    
            learningRate= r;
        }
        // processing method
        void process(cv:: Mat &frame, cv:: Mat &output) {
    
    
            // 转换为灰度图像
            cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY); 
            // 初始化背景图像
            if (background.empty())
                gray.convertTo(background, CV_32F);
            background.convertTo(backImage,CV_8U);
            // 计算当前图像与背景图像间的差异
            cv::absdiff(backImage,gray,foreground);
            // 对前景图像应用阈值
            cv::threshold(foreground,output,threshold,255,cv::THRESH_BINARY_INV);
            // 累积背景
            cv::accumulateWeighted(gray, background, 
                                // alpha*gray + (1-alpha)*background
                                learningRate,       // alpha 
                                output);            // mask
        }

(3)ビデオ処理フレームワークを使用して前景抽出プログラムを構築します。

int main () {
    
    
    // 创建视频处理实例
    VideoProcessor processor;
    BGFGSegmentor segmentor;
    segmentor.setThreshold(25);
    // 打开视频文件
    processor.setInput("example.avi");
    processor.setFrameProcessor(&segmentor);
    // 显示视频
    processor.displayOutput("Extracted Foreground");
    processor.setDelay(1000./processor.getFrameRate());
    processor.run();
    cv::waitKey();
}

次のようにバイナリの前景イメージを表示します。

バイナリの前景画像

cv::accumulateWeighted画像の移動移動平均は、画像の各ピクセルに移動平均公式を適用する関数を使用して計算できます。結果の画像は浮動小数点画像でなければならないため、現在のフレームと比較する前に、背景モデルを背景画像に変換する必要があります。前景イメージは、単純なしきい値絶対差分 ( cv::absdiffafter。cv::threshold次に、前景画像cv::accumulateWeightedは前景ピクセルfalse(つまり0) で定義されているため、前景ピクセルの更新を避けるためのマスクとして使用され、結果の画像では前景オブジェクトが黒いピクセルとして表示されます。
最後に、簡単にするために、プログラムで構築された背景モデルは、抽出されたグレースケール画像フレームに基づいています。色の背景を計算するには、色空間内の移動平均を計算する必要があります。主な困難は、改善するための適切なしきい値を決定することにあります。結果。
シーン内の前景オブジェクトを抽出する上記の方法は、比較的安定した背景を持つ単純なシーンに適しています。ただし、多くの場合、背景のシーンが一部の領域で変動する可能性があり、移動する背景オブジェクト (風に揺れる葉など) やグレア効果 (水面での太陽光の反射など) などの前景の誤検出が頻繁に発生します。影は移動する物体の一部として検出されることが多いため、影も問題となる可能性があります。これらの問題を解決するには、ガウス法の混合など、より複雑な背景モデリング法を導入する必要があります。

2. 混合ガウス法

混合ガウス法は、移動平均に基づいて改良されたアルゴリズムです。まず、この方法ではピクセルごとに複数の移動平均モデルが維持されます。このようにして、背景ピクセルが 2 つの値の間で変動する場合、2 つの移動平均が保存されます。新しいピクセル値は、一般的に観察されるモデルのいずれにも属さない場合にのみ、フォアグラウンドとして宣言されます。モデルの数はパラメータを使用して決定でき、一般的に使用されるモデルの数は です5
次に、各モデルの移動平均だけでなく、分散も維持されます。
σ t 2 = ( 1 − α ) σ t − 1 2 + α ( pt − μ t ) 2 \sigma_t^2=(1- \ alpha)\sigma_{t-1}^2+\alpha(p_t-\mu_t)^2pt2=( 1a ) pt 12+a ( pメートル)2
計算された平均と分散を使用してガウス モデルを構築すると、特定のピクセル値が背景に属する確率を推定できます。したがって、閾値は差の絶対値ではなく確率を表すため、適切な閾値を決定するのが容易になります。したがって、背景値の変動が大きい領域の前景オブジェクトを識別するには、より大きな差が必要です。
特定のガウス モデルが十分な頻度で一致しない場合、そのモデルは背景モデルの一部とみなされません。代わりに、ピクセル値が現在維持されている背景モデル (つまり、前景ピクセル) の外側にある場合、新しいガウス モデルが作成され、新しいモデルが最も頻度の高いモデルになると、背景に関連付けられます。
このアルゴリズムは、単純な背景/前景スプリッターよりも実装が明らかに複雑です。ただし、実装OpenCVcv::BackgroundSubtractorMOGジェネリック クラスcv::BackgroundSubtractor

int main () {
    
    
    // 打开视频
    cv::VideoCapture capture("example.avi");
    if (!capture.isOpened()) return 0;
    // 当前视频帧
    cv::Mat frame;
    // 前景二值图像
    cv::Mat foreground;
    // 背景图像
    cv::Mat background;
    cv::namedWindow("Extracted Foreground");
    cv::Ptr<cv::BackgroundSubtractor> ptrMOG = cv::bgsegm::createBackgroundSubtractorMOG();
    bool stop(false);
    while (!stop) {
    
    
        if (!capture.read(frame)) break;
        // 升级背景并返回前景
        ptrMOG->apply(frame, foreground, 0.01);
        cv::threshold(foreground, foreground, 128, 255, cv::THRESH_BINARY_INV);
        cv::imshow("Extracted Foreground", foreground);
        if (cv::waitKey(10) >= 0) stop = true;
    }
    cv::waitKey();
}

クラスのインスタンスを作成して呼び出すだけで、アルゴリズムは同時に背景を更新し、前景画像を返します。なお、ここでの背景モデルは色に応じて計算される。ではOpenCV、観測されたピクセルの変化が局所的な明るさの変化のみによって引き起こされているのか (もしそうであれば、影による可能性があります)、それとも色差の変化も含まれているのかをチェックすることで影を識別するための別のメソッドも実装されています。これは、クラスcv::BackgroundSubtractorMOG2を呼び出してアルゴリズム。このアルゴリズムは、使用するピクセルあたりの適切なガウス モデルの数を動的に決定します。上記の方法を複数のビデオで試して、さまざまなアルゴリズムのパフォーマンスを観察できます。

3. 完全なコード

ヘッダー ファイル ( ) のvideoprocessor.h完全なコードについては、ビデオ シーケンス処理セクションを参照してください。BGFGSegmentor.hヘッダー ファイル ( ) の完全なコードは次のとおりです。

#if !defined BGFGSeg
#define BGFGSeg

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include "videoprocessor.h"

class BGFGSegmentor : public FrameProcessor {
    
    
    
    cv::Mat gray;           // 灰度图像
    cv::Mat background;     // 累积背景
    cv::Mat backImage;      // 当前背景图像
    cv::Mat foreground;     // 前景图像
    double learningRate;    // 学习率
    int threshold;          // 阈值

    public:

        BGFGSegmentor() : threshold(10), learningRate(0.01) {
    
    }
        // 设置阈值
        void setThreshold(int t) {
    
    
            threshold= t;
        }
        // 设置学习率
        void setLearningRate(double r) {
    
    
            learningRate= r;
        }
        // processing method
        void process(cv:: Mat &frame, cv:: Mat &output) {
    
    
            // 转换为灰度图像
            cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY); 
            // 初始化背景图像
            if (background.empty())
                gray.convertTo(background, CV_32F);
            background.convertTo(backImage,CV_8U);
            // 计算当前图像与背景图像间的差异
            cv::absdiff(backImage,gray,foreground);
            // 对前景图像应用阈值
            cv::threshold(foreground,output,threshold,255,cv::THRESH_BINARY_INV);
            // 累积背景
            cv::accumulateWeighted(gray, background, 
                                // alpha*gray + (1-alpha)*background
                                learningRate,       // alpha 
                                output);            // mask
        }
};

#endif

メイン関数ファイル ( foreground.cpp) の完全なコードは次のとおりです。

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/bgsegm.hpp>

#include "videoprocessor.h"
#include "BGFGSegmentor.h"

int main () {
    
    
    // 打开视频
    cv::VideoCapture capture("r3.mp4");
    if (!capture.isOpened()) return 0;
    // 当前视频帧
    cv::Mat frame;
    // 前景二值图像
    cv::Mat foreground;
    // 背景图像
    cv::Mat background;
    cv::namedWindow("Extracted Foreground");
    cv::Ptr<cv::BackgroundSubtractor> ptrMOG = cv::bgsegm::createBackgroundSubtractorMOG();
    bool stop(false);
    while (!stop) {
    
    
        if (!capture.read(frame)) break;
        // 升级背景并返回前景
        ptrMOG->apply(frame, foreground, 0.01);
        cv::threshold(foreground, foreground, 128, 255, cv::THRESH_BINARY_INV);
        cv::imshow("Extracted Foreground", foreground);
        if (cv::waitKey(10) >= 0) stop = true;
    }
    cv::waitKey();
    // 创建视频处理实例#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/bgsegm.hpp>

#include "videoprocessor.h"
#include "BGFGSegmentor.h"

int main () {
    
    
    // 打开视频
    cv::VideoCapture capture("r3.mp4");
    if (!capture.isOpened()) return 0;
    // 当前视频帧
    cv::Mat frame;
    // 前景二值图像
    cv::Mat foreground;
    // 背景图像
    cv::Mat background;
    cv::namedWindow("Extracted Foreground");
    cv::Ptr<cv::BackgroundSubtractor> ptrMOG = cv::bgsegm::createBackgroundSubtractorMOG();
    bool stop(false);
    while (!stop) {
    
    
        if (!capture.read(frame)) break;
        // 升级背景并返回前景
        ptrMOG->apply(frame, foreground, 0.01);
        cv::threshold(foreground, foreground, 128, 255, cv::THRESH_BINARY_INV);
        cv::imshow("Extracted Foreground", foreground);
        if (cv::waitKey(10) >= 0) stop = true;
    }
    cv::waitKey();
    // 创建视频处理实例
    VideoProcessor processor;
    BGFGSegmentor segmentor;
    segmentor.setThreshold(25);
    // 打开视频文件
    processor.setInput("example.avi");
    processor.setFrameProcessor(&segmentor);
    // 显示视频
    processor.displayOutput("Extracted Foreground");
    processor.setDelay(1000./processor.getFrameRate());
    processor.run();
    cv::waitKey();
}
    VideoProcessor processor;
    BGFGSegmentor segmentor;
    segmentor.setThreshold(25);
    // 打开视频文件
    processor.setInput("example.avi");
    processor.setFrameProcessor(&segmentor);
    // 显示视频
    processor.displayOutput("Extracted Foreground");
    processor.setDelay(1000./processor.getFrameRate());
    processor.run();
    cv::waitKey();
}

関連リンク

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) - ビデオ シーケンス処理

おすすめ

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