LoginSignup
1
1

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

Posted at

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

1. 1フレーム目

1st.jpg

2. 数フレーム先

2nd.jpg

3. 差分1

diff_1st.jpg

4. 差分2

diff_2nd.jpg

5. 差分1と差分2の論理積

AND.jpg

6. WPF

/////////////////////////////////////
// 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

<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>
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1