C # Slow performance for a loop when looking for pixels in an image

When I use the following code, it takes about 3-5 seconds before the loop is executed if the image I'm looking for on the image is not found. Although this is a search, the rest of the program is paused, my timers go out of sync and it looks like the program freezes for a few seconds. Images are not very large, "printscreen" is about 344x354, and "Ok" is about 15x7. I know this is because of for-loops, but is there a better way to do this, or can I somehow run its part of the program, except for the rest of the program, so the program does not freeze for a few seconds.

// Ok is the image I am searching for. // printscreen is the image I am searching in. Bitmap Ok = new Bitmap(Properties.Resources.popupok1); int Count = 0; for (int x = 0; x < printscreen.Width; x++) { for (int y = 0; y < printscreen.Height; y++) { Count = 0; if (printscreen.GetPixel(x, y) == Ok.GetPixel(0, 0) && printscreen.GetPixel(x + 1, y) == Ok.GetPixel(1, 0)) { for (int OkX = 0; OkX <= Ok.Width; OkX++) { for (int OkY = 0; OkY <= Ok.Height; OkY++) { try { if (printscreen.GetPixel(x + OkX, y + OkY) != Ok.GetPixel(OkX, OkY)) { OkX = Ok.Width; OkY = Ok.Height; } else { Count += 1; } if (Count == 105) { X = x; Y = y; OkX = Ok.Width; OkY = Ok.Height; x = printscreen.Width - 1; y = printscreen.Height - 1; Console.Add("Ok button found."); Console.Add(""); ConsoleUpdate(); } } catch { } } } } } } 
+4
source share
5 answers

The performance issue is caused by GetPixels / SetPixels, which is an extremely slow way to access data in .NET Bitmap. Instead, I would look into the Bitmap.LockBits method to get a pointer to a bitmap and manipulate the data directly. It will be an order of magnitude faster.

See MSDN :

The following code example shows how to use the PixelFormat, Height, Width, and Scan0 properties; LockBits and UnlockBits methods and the ImageLockMode enumeration. This example is for use with Windows Forms. To run this example, paste it into the form and handle the Paint form event by calling the LockUnlockBitsExample method, passing e as PaintEventArgs.

 private void LockUnlockBitsExample(PaintEventArgs e) { // Create a new bitmap. Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg"); // Lock the bitmap bits. Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat); // Get the address of the first line. IntPtr ptr = bmpData.Scan0; // Declare an array to hold the bytes of the bitmap. // This code is specific to a bitmap with 24 bits per pixels. int bytes = bmp.Width * bmp.Height * 3; byte[] rgbValues = new byte[bytes]; // Copy the RGB values into the array. System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); // Set every red value to 255. for (int counter = 2; counter < rgbValues.Length; counter+=3) rgbValues[counter] = 255; // Copy the RGB values back to the bitmap System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes); // Unlock the bits. bmp.UnlockBits(bmpData); // Draw the modified image. e.Graphics.DrawImage(bmp, 0, 150); } 

If you want to go even faster and not copy the array, manipulate it and copy it, you can work with the bitmap in place using an unsafe pointer. In this case, the inside will change as follows:

  // Lock the bitmap bits. Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat); // Get the address of the first line. IntPtr ptr = bmpData.Scan0; // Declare an array to hold the bytes of the bitmap. // This code is specific to a bitmap with 24 bits per pixels. int bytes = bmp.Width * bmp.Height * 3; unsafe { byte* rgbValues = (byte*)ptr; // Set every red value to 255. for (int counter = 2; counter < bytes counter+=3) rgbValues[counter] = 255; } // Unlock the bits. bmp.UnlockBits(bmpData); 

Just take care of the PixelFormat bitmap. The above example assumes its 24 bits per pixel BGR. There are actually a lot of bitmaps - BGRA (32 bits per pixel), so you will need to change the four bytes for Blue, Gree, Red, Alpha in that order by pixel.

+11
source

use a new new thread for image processing, and your program does not freeze, but Threading

+2
source

It looks like a hungry algorytm. Think about it again and identify points where you can safely leave this function. e.g. add refund after

 Console.Add("Ok button found."); Console.Add(""); ConsoleUpdate(); return; 

I believe that you can find more points that you can leave because you can be sure that you have nothing more to find, or why you end your cycles even if you have already found what you are looking for?

Or maybe you can configure it differently. You can start scanning the image for the first pixel, and after searching for it, you can check the second, third, etc., And, if, for example, the third pixel is incorrect, you will need to go back and continue.

So, even if one pixel is correct, you will view the image only once.

In other words, do not try to compare the two x * y regions, first try to compare the pixels, and then the regions. You should be able to significantly reduce the time.

+1
source

I don't know anything about image processing, and your problem seems to be common enough for someone to probably develop a specific algorithm. However, in the case of no, here are my two cents: it is not surprising that your algorithm runs slowly, if two images are W1xH1 and W2xH2, your runtime is O(W1.H1.W2.H2) in the worst case. The average case is much smaller, but still not fast enough.

Finding a substring inside a string is a one-dimensional analogue of your task, and it is a well-researched problem. You can check the Boyer-Moore string search algorithm and see if you can adapt the basic idea to your problem.

+1
source

I posted primers on http://www.tmorley.net . This explains image processing using C #. The first thing that is done is that the image file (jpg, bmp, etc.) immediately contains the pixel data extracted into the array. Processing can be done quickly, and then the results are returned to the bitmap for display or saving to disk.

0
source

Source: https://habr.com/ru/post/1398778/


All Articles