フレーム間差分法 動画解析 (WPF+OpenCV)






::: note
・インメモリでやれば良いがc:\にテンポラリ吐いてます。
・特定の区域を検査する事によりドアを開けた、通過した等検知できます。
・Webカメラがないので動画ファイルの再生でやってますがカメラによる<BR>　リアルタイム検知が可能。
・差分1の画像で黒以外のドット数を調べてその数から大体の人数が出せる。
・カメラから遠い所は一人分の面積が小さくなるのでエリア別に計算すると<BR>　尚よい。
・差分1をよく見ると横線が出ているので風か何かで揺れている事がわかる。
・差分法など使わず独自のアルゴリズムで解析してみたい。(考案中)
:::

<font COLOR="blue" >

#### 1. 1フレーム目

</FONT>

![1st.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821882/3a26387c-e108-e4fb-ecc6-92e9e1414e6c.jpeg)

<font COLOR="blue" >

#### 2. 数フレーム先

</FONT>

![2nd.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821882/7eafb023-2694-6892-aab7-022789c85eb1.jpeg)

<font COLOR="blue" >

#### 3. 差分1

</FONT>

![diff_1st.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821882/13f9d8fc-e1e3-7030-94c2-5cbddadc20a7.jpeg)

<font COLOR="blue" >

#### 4. 差分2

</FONT>

![diff_2nd.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821882/c7c495f6-50af-91b0-82a8-4b4c98d0ff20.jpeg)

<font COLOR="blue" >

#### 5. 差分1と差分2の論理積
</FONT>

![AND.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821882/14d490b3-2faa-4a89-ec1d-84c1c5aed503.jpeg)

<font COLOR="blue" >

#### 6. WPF 
</FONT>

```c#
/////////////////////////////////////
// WPF+OpenCvSharp 動画解析 R1.00
// (c)inf102 2024.
// https://github.com/opencv/opencv/blob/master/samples/data/vtest.avi 
/////////////////////////////////////

using OpenCvSharp;
using OpenCvSharp.WpfExtensions;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;

namespace openCV4 {

    public partial class MainWindow : System.Windows.Window {
       
        static int ch=0;

        public MainWindow() {

            InitializeComponent();
        }

        private async void Window_Loaded(object sender, RoutedEventArgs e) {
            
            VideoCapture capture  = new VideoCapture("C:\\vtest.avi");
            var img = new Mat();

            // 再生フレームカウンタ
            int cnt=0;

            // フレーム間隔 管理用
            int cr=0; 
            while (capture.Read(img)){
          
                BitmapSource x=BitmapSourceConverter.ToBitmapSource(img);
                PngBitmapEncoder encoder = new PngBitmapEncoder();

                cr++;

                // フレーム保存
                if (cr==1){
                    using (Stream stream = new FileStream("c:\\1st.jpg", FileMode.Create)){
                        encoder.Frames.Add (BitmapFrame.Create(x));
                        encoder.Save(stream);
                    }
                }

                // +4フレーム目保存
                if (cr==5){
                    cr=0;

                    using (Stream stream = new FileStream("c:\\2nd.jpg", FileMode.Create)){
                        encoder.Frames.Add (BitmapFrame.Create(x));
                        encoder.Save(stream);
                    }

                    // 差分
                    Sa();
                }

                imageL.Source=x;
                await Task.Delay(100);
                cnt++;
            }
            imageL.Source=null;
        }

        // 差分+AND
        public void Sa() {

            Mat image1 = Cv2.ImRead("C:\\1st.jpg");
            Mat image2 = Cv2.ImRead("C:\\2nd.jpg");
            Mat diff = new Mat(new OpenCvSharp.Size(image1.Cols, image1.Rows), MatType.CV_8UC3);
            var dst = new Mat();

            Cv2.Absdiff(image1, image2, diff);

            var xx=BitmapSourceConverter.ToBitmapSource(diff);
         
            if (ch==0){
                using (Stream stream = new FileStream("c:\\diff_1st.jpg", FileMode.Create)){
                    PngBitmapEncoder encoder = new PngBitmapEncoder();
                    encoder.Frames.Add(BitmapFrame.Create(xx));
                    encoder.Save(stream);
                }
        
                ch=1;
            }
            else {         
                using (Stream stream = new FileStream("c:\\diff_2nd.jpg", FileMode.Create)){
                    PngBitmapEncoder encoder = new PngBitmapEncoder();
                    encoder.Frames.Add(BitmapFrame.Create(xx));
                    encoder.Save(stream);
                }
                ch=0;

                var file1 = "c:\\diff_1st.jpg";
                var file2 = "c:\\diff_2nd.jpg";

                var imgA = new Mat(file1);
                var imgB = new Mat(file2);

                Cv2.BitwiseAnd(imgA, imgB, dst);
                       
                var xx2=BitmapSourceConverter.ToBitmapSource(dst);
                imageR.Source=xx2;

                //////////////// 差分2枚のANDを算出保存
                using (Stream stream = new FileStream("c:\\AND.jpg", FileMode.Create)){
                    PngBitmapEncoder encoder = new PngBitmapEncoder();
                    encoder.Frames.Add(BitmapFrame.Create(xx2));
                    encoder.Save(stream);
                }

                //////////////////////////////////////////
                int c=0;
                int f=0;

                for (int y = 0; y < dst.Height; y++){
                    for (int x = 0; x < dst.Width; x++){
                        byte b = dst.At<byte>(y, x);
                        if ((int)b!=0) c++;
                        else f++;
                    }
                }

                double tt=c/8000;
                int cx=(int)tt;
                cx=cx/2;
            
                Title="["+cx.ToString()+"人] 人DAT"+c+" 以外F"+f.ToString();          
            }
        }
    }
}
```

#### 7.XAML
``` XML
<Window x:Class="openCV4.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:openCV4"
        mc:Ignorable="d"
        Title="MainWindow" Height="340" Width="830" Loaded="Window_Loaded">
    <Grid>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width= "407"/>
            <ColumnDefinition Width= "*"/>
        </Grid.ColumnDefinitions>

        <Image x:Name="imageL" Grid.Column="0"/>
        <Image x:Name="imageR" Grid.Column="1"/>
    </Grid>
</Window>
```