-1

I am making a video recorder, The app works by taking a lot of screenshots and putting them together into one video. Also, I am trying to make something like screen motion detection. I need the app to take screenshots only when a difference in the screen is detected. I was thinking about how to do that, and I believe I need to make it still take screenshots while comparing them to the previous one. Is there a way to do that?

The code:

        //Record video:
        public void RecordVideo()
        {
            //Keep track of time:
            watch.Start();

            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();
            }
        }
Kostas G
  • 140
  • 7

1 Answers1

2

I have something that may be useful for you. The idea is save only the differences between the images and, with that, recreate later all images from starting image and saved changes.

To do this, you only need make a XOR operation in the image bytes. This method allow you get the difference (the array parameter) between two images:

protected void ApplyXor(Bitmap img1, Bitmap img2, byte[] array)
{
    const ImageLockMode rw = ImageLockMode.ReadWrite;
    const PixelFormat argb = PixelFormat.Format32bppArgb;

    var locked1 = img1.LockBits(new Rectangle(0, 0, img1.Width, img1.Height), rw, argb);
    var locked2 = img2.LockBits(new Rectangle(0, 0, img2.Width, img2.Height), rw, argb);

    try
    {
        ApplyXor(locked2, locked1, array);
    }
    finally
    {
        img1.UnlockBits(locked1);
        img2.UnlockBits(locked2);
    }
}

With the previous img1 bitmap and the array returned, you can get the img2 with this method:

protected void ApplyXor(Bitmap img1, byte[] array, Bitmap img2)
{
    const ImageLockMode rw = ImageLockMode.ReadWrite;
    const PixelFormat argb = PixelFormat.Format32bppArgb;

    var locked1 = img1.LockBits(new Rectangle(0, 0, img1.Width, img1.Height), rw, argb);
    var locked2 = img2.LockBits(new Rectangle(0, 0, img2.Width, img2.Height), rw, argb);

    try
    {
        ApplyXor(locked1, array, locked2);
    }
    finally
    {
        img1.UnlockBits(locked1);
        img2.UnlockBits(locked2);
    }
}

And here the other required methods:

private unsafe void ApplyXor(BitmapData img1, BitmapData img2, byte[] array)
{
    byte* prev0 = (byte*)img1.Scan0.ToPointer();
    byte* cur0 = (byte*)img2.Scan0.ToPointer();

    int height = img1.Height;
    int width = img1.Width;
    int halfwidth = width / 2;

    fixed (byte* target = array)
    {
        ulong* dst = (ulong*)target;

        for (int y = 0; y < height; ++y)
        {
            ulong* prevRow = (ulong*)(prev0 + img1.Stride * y);
            ulong* curRow = (ulong*)(cur0 + img2.Stride * y);

            for (int x = 0; x < halfwidth; ++x)
            {
                if (curRow[x] != prevRow[x])
                {
                    int a = 0;
                }

                *(dst++) = curRow[x] ^ prevRow[x];
            }
        }
    }
}

private unsafe void ApplyXor(BitmapData img1, byte[] array, BitmapData img2)
{
    byte* prev0 = (byte*)img1.Scan0.ToPointer();
    byte* cur0 = (byte*)img2.Scan0.ToPointer();

    int height = img1.Height;
    int width = img1.Width;
    int halfwidth = width / 2;

    fixed (byte* target = array)
    {
        ulong* dst = (ulong*)target;

        for (int y = 0; y < height; ++y)
        {
            ulong* prevRow = (ulong*)(prev0 + img1.Stride * y);
            ulong* curRow = (ulong*)(cur0 + img2.Stride * y);

            for (int x = 0; x < halfwidth; ++x)
            {
                curRow[x] = *(dst++) ^ prevRow[x];
            }
        }
    }
}

NOTE: You must configure your project to allow unsafe.

With previous methods, you can do:

  • Save a img1 bitmap
  • Get img2 bitmap, do XOR and get the array (array2, for example)
  • With img3, get the XOR with img2 (array3, for example). Now, img2 isn't needed
  • With img4, get the XOR with img3 (array4). Now, img3 isn't needed
  • ...

You have img1 and array2, array3, array4... and you can recreate all images:

  • Make XOR between img1 and array2 to get img2
  • Make XOR between img2 and array3 to get img3
  • ...

If you need send video over TCP, you can send the images sending one image and the XOR arrays (the differences). Or better yet, compress the XOR arrays using K4os.Compression.LZ4.

Victor
  • 2,313
  • 2
  • 5
  • 13
  • 1
    But how does this actually detect a change? – ProgrammingLlama Jun 28 '22 at 00:03
  • @DiplomacyNotWar the xor give you the changes. If array contains any 1 value, the image has changes. The code is interesting because similar images give you an array with lots of 0s that you can compress a lot and send very few information via sockets. May be not very clear in my answer but the key is in the compression factor that you get using the xor array – Victor Jun 28 '22 at 07:48
  • OP has a camera that they only want to record video from when there is motion in the scene. Sure XOR can help you cut down the information you need to send, but even the slow transition of sunrise to sunset will cause changes in an image from a camera. How do you differentiate that from motion using XOR? – ProgrammingLlama Jun 28 '22 at 08:07
  • @DiplomacyNotWar my answer is not for that case. I think the best option is use a library (DRY) like in this answer https://stackoverflow.com/a/17626650/18452174. – Victor Jun 28 '22 at 08:27
  • In RecordVideo you can use previous image and new image and make XOR to get an array with the differences (the 1s in the array are differences). You can get the percentage of 1s in the array and, below of some threshold, consider it like without changes. But changes in illumination, for example, give you different images. It's not the best way to check if image has changes. I use this in one application like remote desktop, to send image differences over TCP. It's a different problem. I recomend you the use of some library of motion detection. – Victor Jun 28 '22 at 11:36
  • My code hasn't sense for motion detection. You can use this code instead: https://www.codeproject.com/Articles/10248/Motion-Detection-Algorithms. For motion detection there are concrete algorithms better than my code. My code is good to send only the part of images that has changes (the xor array compressed). – Victor Jul 01 '22 at 14:05