SKProgramLab

Let's Enjoy Programming! ~画像処理/IoT/機械学習など~

C#/VB.NETで画像処理⑭<背景差分>

こんにちは、SKです。
f:id:SKProgramLab:20200310203357p:plain:w100

C#/VB.NETで画像処理シリーズの第14弾。
今回は、画像処理の分野で良く使われる背景差分を行う方法を紹介します。

背景画像と現在画像を重ね合わせ、画像の中で変化のあった領域を抽出できます。
動体検知に使われたりします。

ボタン押下時に背景画像を登録し、背景差分をリアルタイムで行った結果の二値化画像を表示してみます。

OpenCVSharpとは?

skprogramlab.hatenablog.com

(前回)テンプレートマッチング

skprogramlab.hatenablog.com

動画手順

youtu.be

①背景画像登録ボタンを配置する

フォーム上にボタンを配置します。
f:id:SKProgramLab:20200507222624p:plain

前回同様、メインのProcess関数内でカメラ画像を常に変数picImageに格納しつつ、このボタンのクリックイベントで背景画像の変数backImageに格納します。

private Mat picImage;
private Mat backImage;

private void Process(Mat img)        
{
    //カメラ画像格納
    if(picImage == null)
    {
        picImage = img.Clone();
    }
    else
    {
        img.CopyTo(picImage);
    }
}

private void button1_Click(object sender, EventArgs e)
{
    //背景画像格納
    backImage = picImage.Clone();
}


②背景差分を行う

AbsDiff関数を使用し、背景画像とカメラ画像の差分画像を作成します。
出力画像もBGRの3チャンネルですが、結果が分かりやすいように二値化して表示します。

AbsDiff関数

Cv2.Absdiff Method
・第1引数:入力画像1
・第2引数:入力画像2
・第3引数:出力画像

//背景差分
if(backImage != null)
{               
    using(Mat dst = new Mat())
    {
        Cv2.Absdiff(img, backImage, dst);
        using(Mat gray = dst.CvtColor(ColorConversionCodes.BGR2GRAY))
        {
            Cv2.Threshold(gray, gray, 120, 255, ThresholdTypes.Binary);
            Cv2.ImShow("diff", gray);
            Cv2.WaitKey(1);
        }
    }
}



実行します。
背景画像を登録すると、最初は真っ黒 f:id:SKProgramLab:20200507225256p:plain

カップを入れるとこんな感じ f:id:SKProgramLab:20200507225342p:plain

最後に、コード全文を載せておきます。
C#

using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Task.Run(() =>
            {
                using (VideoCapture v = new VideoCapture(0))
                using (Mat img = new Mat())
                {
                    while (true)
                    {
                        v.Read(img);
                        Process(img);
                        pictureBox1.Image = BitmapConverter.ToBitmap(img);
                    }
                }
            });
        }

        private Mat picImage;
        private Mat backImage;

        private void Process(Mat img)        
        {
            //カメラ画像格納
            if(picImage == null)
            {
                picImage = img.Clone();
            }
            else
            {
                img.CopyTo(picImage);
            }

            //背景差分
            if(backImage != null)
            {               
                using(Mat dst = new Mat())
                {
                    Cv2.Absdiff(img, backImage, dst);
                    using(Mat gray = dst.CvtColor(ColorConversionCodes.BGR2GRAY))
                    {
                        Cv2.Threshold(gray, gray, 120, 255, ThresholdTypes.Binary);
                        Cv2.ImShow("diff", gray);
                        Cv2.WaitKey(1);
                    }
                }
            }

        }

        private void button1_Click(object sender, EventArgs e)
        {
            //背景画像格納
            backImage = picImage.Clone();
        }
    }
}


VB.NET

Imports OpenCvSharp
Imports OpenCvSharp.Extensions

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Task.Run(Sub()
                     Using v As New VideoCapture(0)
                         Using img As New Mat
                             Do
                                 v.Read(img)
                                 Process(img)
                                 PictureBox1.Image = img.ToBitmap
                             Loop
                         End Using
                     End Using
                 End Sub)
    End Sub

    Private picImage As Mat
    Private backImage As Mat

    Private Sub Process(ByVal img As Mat)

        'カメラ画像格納
        If picImage Is Nothing Then
            picImage = img.Clone
        Else
            img.CopyTo(picImage)
        End If

        '背景差分
        If backImage IsNot Nothing Then
            Using dst As New Mat
                Cv2.Absdiff(img, backImage, dst)
                Using gray As Mat = dst.CvtColor(ColorConversionCodes.BGR2GRAY)
                    Cv2.Threshold(gray, gray, 120, 255, ThresholdTypes.Binary)
                    Cv2.ImShow("diff", gray)
                    Cv2.WaitKey(1)
                End Using
            End Using
        End If

    End Sub

    '背景画像格納
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        backImage = picImage.Clone
    End Sub

End Class