Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slow image change analysis with solution #16

Open
ez2X8pk61DHkdztgFT6O opened this issue Jan 2, 2022 · 3 comments
Open

Slow image change analysis with solution #16

ez2X8pk61DHkdztgFT6O opened this issue Jan 2, 2022 · 3 comments
Assignees
Labels
investigation Investigation in progress

Comments

@ez2X8pk61DHkdztgFT6O
Copy link

Thanks for your code! I noticed that checking for image changes was the slowest part due to the use of GetPixel and SetPixel. The code below passes your tests and is much faster due to the use of 'LockBits' and direct access to the data. Requires the build properties to be set to 'allow unsafe code', though.

using System.Drawing;

namespace CMK
{
internal class ImageChangeAnalyser
{
private Bitmap oldImage = null;

    public unsafe Image BlackoutImage(Image newImage, out bool equal)
    {
        equal = false;
        if (newImage == null)
        {
            return null;
        }
        // Make sure we have a bitmap
        var newImageBmp = new Bitmap(newImage);
        // Copy of new image
        var copy_new_image = new Bitmap(newImageBmp);
        // Dimensions
        var x = newImage.Width;            
        var y = newImage.Height;
        var x4 = 4 * x; // argb format

        // Something to do?
        if (oldImage != null)
        {
            // Lock bits of old and new image
            var o_img = oldImage.LockBits(new Rectangle(0, 0, x, y), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            var n_img = newImageBmp.LockBits(new Rectangle(0, 0, x, y), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            // To track equality
            bool is_equal = true;

            for (int j = 0; j < y; j++)
            {
                byte* o_row = ((byte *) o_img.Scan0) + o_img.Stride * j;
                byte* n_row = ((byte * )n_img.Scan0) + n_img.Stride * j;
                for (int i = 0; i < x4; i+=4)
                {                    
                    // Compare b, g, and r values (+0, +1, +2)
                    if (o_row[i+0] == n_row[i+0] && o_row[i+1] == n_row[i+1] && o_row[i+2] == n_row[i+2])
                    {   
                        // For compatibility with tests
                        n_row[i + 0] = 255;
                        n_row[i + 1] = 255;
                        n_row[i + 2] = 255;
                        // Set 'A' value(+3) of new image to 0: fully transparent (whatever the other colors!)
                        n_row[i + 3] = 0;
                    }
                    else
                    {
                        is_equal = false;
                    }
                }
            }
            // Unlock bits
            oldImage.UnlockBits(o_img);
            newImageBmp.UnlockBits(n_img);
            // Update equality
            equal = is_equal;
        }
        // old image is current new image (but without our transparancy changes, so use copy0
        oldImage = copy_new_image;            
        // Return bitmap with transparance changes
        return newImageBmp;
    }

}

}

@ez2X8pk61DHkdztgFT6O
Copy link
Author

ez2X8pk61DHkdztgFT6O commented Jan 2, 2022

As an afterthought: although the above code always 'runs', the transparency is only changed when the original image had an alpha channel to start with (not sure how your code handles this, maybe GDI+ automagically adds alpha?). Otherwise the alpha channel changes will do nothing as the 'unlockbits' will write back all data except the alpha channel values. To make things full-proof one would need to check whether the original image has an alpha channel. And if not, do something like this:

    /// <summary>
    /// Convert image to 32bpp argb. Disposes of the existing images!
    /// </summary>
    /// <param name="a"></param>
    /// <returns></returns>
    private Image ToARGB(Image a)
    {
        Bitmap clone = new Bitmap(a.Width, a.Height, PixelFormat.Format32bppPArgb);
        using (Graphics gr = Graphics.FromImage(clone))
        {
            gr.DrawImage(a, new Rectangle(0, 0, clone.Width, clone.Height));
        }
        a.Dispose();
        return clone;
    }

@cmk1988
Copy link
Member

cmk1988 commented Jan 6, 2022

Thank you! I will review it on weekend. Do you want to create a pull request for the changes?

@cmk1988 cmk1988 added the investigation Investigation in progress label Jan 6, 2022
@cmk1988 cmk1988 self-assigned this Jan 6, 2022
@ez2X8pk61DHkdztgFT6O
Copy link
Author

ez2X8pk61DHkdztgFT6O commented Jan 6, 2022 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
investigation Investigation in progress
Projects
None yet
Development

No branches or pull requests

2 participants