Search code examples
c#motion-detection

How can I compare 2 images and delete the last one if they are the same?


I made a video recorder that takes screenshots each frame and saves it in a temporary folder. When I press the Stop recording button it puts it all together in one video. I now need to have my program check the latest 2 screenshots it has taken and compare them to find changes, if change wasn't found the last image should get deleted. In the other hand if the 2 images had any difference, both images would stay there.

Thank you for helping me out!

Forms1.cs[Design] Forms1.cs[Design]

Below is my Screen Recorder script

ScreenRecorder.cs

using System.IO;
using System.Drawing;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Accord.Video;
using Accord.Video.FFMPEG;
using GroupDocs.Comparison.Options;
using GroupDocs.Comparison;

namespace Timelapser_version_1._2
{
    class ScreenRecorder
    {
        //Video variables:
        private Rectangle bounds;
        private string outputPath = "";
        private string tempPath = "";
        private int fileCount = 1;
        private List<string> inputImageSequence = new List<string>();

        //File variables:
        private string audioName = "mic.wav";
        private string videoName = "video.mp4";
        private string finalName = "FinalVideo.mp4";

        //Time variable:
        Stopwatch watch = new Stopwatch();

        //Audio variables:
        public static class NativeMethods
        {
            [DllImport("winmm.dll", EntryPoint = "mciSendStringA", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)]
            public static extern int record(string lpstrCommand, string lpstrReturnString, int uReturnLength, int hwndCallback);
        }

        //ScreenRecorder Object:
        public ScreenRecorder(Rectangle b, string outPath)
        {
            //Create temporary folder for screenshots:
            CreateTempFolder("tempScreenCaps");

            //Set variables:
            bounds = b;
            outputPath = outPath;
        }

        //Create temporary folder:
        private void CreateTempFolder(string name)
        {
            //Check if a C or D drive exists:
            if (Directory.Exists("D://"))
            {
                string pathName = $"D://{name}";
                Directory.CreateDirectory(pathName);
                tempPath = pathName;
            }
            else
            {
                string pathName = $"C://Documents//{name}";
                Directory.CreateDirectory(pathName);
                tempPath = pathName;
            }
        }

        //Change final video name:
        public void setVideoName(string name)
        {
            finalName = name;
        }

        //Delete all files and directory:
        private void DeletePath(string targetDir)
        {
            string[] files = Directory.GetFiles(targetDir);
            string[] dirs = Directory.GetDirectories(targetDir);

            //Delete each file:
            foreach (string file in files)
            {
                File.SetAttributes(file, FileAttributes.Normal);
                File.Delete(file);
            }

            //Delete the path:
            foreach (string dir in dirs)
            {
                DeletePath(dir);
            }

            Directory.Delete(targetDir, false);
        }

        //Delete all files except the one specified:
        private void DeleteFilesExcept(string targetDir, string excDir)
        {
            string[] files = Directory.GetFiles(targetDir);

            //Delete each file except specified:
            foreach (string file in files)
            {
                if (file != excDir)
                {
                    File.SetAttributes(file, FileAttributes.Normal);
                    File.Delete(file);
                }
            }
        }

        //Clean up program on crash:
        public void cleanUp()
        {
            if (Directory.Exists(tempPath))
            {
                DeletePath(tempPath);
            }
        }

        //Return elapsed time:
        public string getElapsed()
        {
            return string.Format("{0:D2}:{1:D2}:{2:D2}", watch.Elapsed.Hours, watch.Elapsed.Minutes, watch.Elapsed.Seconds);
        }

        //Record video:
        public void RecordVideo()
        {
            //Keep track of time:
            watch.Start();
            int blac=0;
            string a ="screenshot-1+%d";
            using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
            {
                using (Graphics g = Graphics.FromImage(bitmap))
                {
                    //Add screen to bitmap:
                    g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
                }
                //Save screenshot:
                string name = tempPath + "//screenshot-" + fileCount + ".png";
                bitmap.Save(name, ImageFormat.Png);
                inputImageSequence.Add(name);
                fileCount++;

                //Dispose of bitmap:
                bitmap.Dispose();
            }
        }

        //Record audio:
        public void RecordAudio()
        {
            NativeMethods.record("open new Type waveaudio Alias recsound", "", 0, 0);
            NativeMethods.record("record recsound", "", 0, 0);
        }

        //Save audio file:
        private void SaveAudio()
        {
            string audioPath = "save recsound " + outputPath + "//" + audioName;
            NativeMethods.record(audioPath, "", 0, 0);
            NativeMethods.record("close recsound", "", 0, 0);
        }

        
        //Save video file:
        public void SaveVideo(int width, int height, int frameRate)
        {

            using (VideoFileWriter vFWriter = new VideoFileWriter())
            {
                
                vFWriter.Open(outputPath + "//"+ videoName, width, height, frameRate, VideoCodec.MPEG4);

                //Make each screenshot into a video frame:
                foreach (string imageLocation in inputImageSequence)
                {
                    Bitmap imageFrame = System.Drawing.Image.FromFile(imageLocation) as Bitmap;
                    vFWriter.WriteVideoFrame(imageFrame);
                    imageFrame.Dispose();
                }

                //Close:
                vFWriter.Close();
            }
        }

        //Combine video and audio files:
        private void CombineVideoAndAudio(string video, string audio)
        {
            //FFMPEG command to combine video and audio:
            string args = $"/c ffmpeg -i \"{video}\" -i \"{audio}\" -shortest {finalName}";
            ProcessStartInfo startInfo = new ProcessStartInfo
            {
                CreateNoWindow = false,
                FileName = "cmd.exe",
                WorkingDirectory = outputPath,
                Arguments = args
            };

            //Execute command:
            using (Process exeProcess = Process.Start(startInfo))
            {
                exeProcess.WaitForExit();
            }
        }
       
        public void Stop()
        {
            //Stop watch:
            watch.Stop();

            //Video variables:
            int width = bounds.Width;
            int height = bounds.Height;
            int frameRate = 10;

            //Save audio:
            SaveAudio();

            //Save video:
            SaveVideo(width, height, frameRate);

            //Combine audio and video files:
            CombineVideoAndAudio(videoName, audioName);

            //Delete the screenshots and temporary folder:
            DeletePath(tempPath);

            //Delete separated video and audio files:
            DeleteFilesExcept(outputPath, outputPath + "\\" + finalName);
        }
    }
}

forms1.cs

using System;
using System.Drawing;
using System.Windows.Forms;
namespace Timelapser_version_1._2
{
    public partial class Timelapser : Form
    {
        string outputPath = "";
        bool folderSelected = false;
        string finalVidName = "FinalVideo.mp4";
        // Screen recorder object:
        ScreenRecorder screenRec = new ScreenRecorder(new Rectangle(), " ");
        public Timelapser()
        {
            InitializeComponent();
        }
       

        private void Start_Click(object sender, EventArgs e)
        {
            
            bool containsMP4 = finalVidName.Contains(".mp4");
            if (folderSelected && containsMP4)
            {
                screenRec.setVideoName(finalVidName);
                timer1.Start();
                
            }
            else if(!folderSelected && containsMP4)
            {
                MessageBox.Show("You must select an output Folder before recording","Error");
            }
            else if (folderSelected && !containsMP4)
            {
                MessageBox.Show("You must select video name that ends in '.mp4'", "Error");
                finalVidName = "FinalVideo.mp4";
            }
        }

        private void Stop_Click(object sender, EventArgs e)
        {
            timer1.Stop();
            screenRec.Stop();
            Application.Restart();
        }
       
        private void timer1_Tick(object sender, EventArgs e)
        {
            
            screenRec.RecordVideo();
            screenRec.RecordAudio();
            Timelabel.Text = screenRec.getElapsed();
        }

        private void SelectFolder_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog folderBrowser = new FolderBrowserDialog();
            folderBrowser.Description = "Select an Output Folder";
            if (folderBrowser.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                outputPath = @folderBrowser.SelectedPath;
                folderSelected = true;

                Rectangle bounds = Screen.FromControl(this).Bounds;
                screenRec = new ScreenRecorder(bounds,outputPath);
            }
            else
            {
                MessageBox.Show("Please select a folder");
            }
        }

Solution

  • I found the solution, to be able to check each 2 screenshots we need the function below.

    public static List<bool> GetHash(Bitmap bmpSource)
            {
                List<bool> lResult = new List<bool>();
                //create new image with 128x128 pixel
                Bitmap bmpMin = new Bitmap(bmpSource, new System.Drawing.Size(128, 128));
                for (int j = 0; j < bmpMin.Height; j++)
                {
                    for (int i = 0; i < bmpMin.Width; i++)
                    {
                        //reduce colors to true / false                
                        lResult.Add(bmpMin.GetPixel(i, j).GetBrightness() < 0.1f);
                    }
                }
                return lResult;
            }
    

    This function gets the screenshots and creates a 128x128 version of them. Also it changes the colour to black and white to get a true or false.

    Then we need to call the function for each 2 screenshots as shown below.

                    List<bool> iHash1 = GetHash(new Bitmap(tempPath + "//screenshot-" + k + ".png"));
                    List<bool> iHash2 = GetHash(new Bitmap(tempPath + "//screenshot-" + (k + 1) + ".png"));
                    //determine the number of equal pixel (x of 256)
                    long equalElements = iHash1.Zip(iHash2, (i, j) => i == j).Count(eq => eq);
                    Debug.Write(equalElements);
                    if (equalElements > 16300 )
                    {
                        var filePath = tempPath + "//screenshot-" + (k+1) + ".png";
                            File.Delete(filePath);
                        
                    }