简体   繁体   English

c#搜索图像中的像素时,for循环性能低下

[英]c# Low performance in for-loop while searching pixels in image

When I use the following code, it takes around 3-5 seconds before the loop is done if the image I am searching for in the image is not found. 当我使用下面的代码时,如果找不到我在图像中搜索的图像,则在完成循环之前大约需要3-5秒。 While this is searching the rest of the program is paused, my timers gets out of sync and it looks like the program freezes for a few seconds. 当这是搜索时,程序的其余部分暂停,我的计时器不同步,看起来程序冻结了几秒钟。 The images are not very big, "printscreen" is around 344x354 and "Ok" is around 15x7. 图像不是很大,“printscreen”约为344x354,“Ok”约为15x7。 I know it is because of the for-loops, but is there a better way to do this or can I runt his part of the program besides the rest of the program in some way, so the program won't freeze for a few seconds. 我知道这是因为for循环,但有没有更好的方法来做到这一点,或者我可以以某种方式除了程序的其余部分之外,他的程序部分,所以程序不会冻结几秒钟。

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

The performance issue is caused by GetPixels/SetPixels, which is an extraordinarily slow way to access the data in an .NET Bitmap. 性能问题是由GetPixels / SetPixels引起的,这是访问.NET位图中数据的一种非常慢的方式。 Instead, I would look into the Bitmap.LockBits method to get a pointer to the bitmap and manipulate data directly. 相反,我会研究Bitmap.LockBits方法来获取指向位图的指针并直接操作数据。 It will be an order of magnitude faster. 它会快一个数量级。

See MSDN : 请参阅MSDN

The following code example demonstrates how to use the PixelFormat, Height, Width, and Scan0 properties; 以下代码示例演示如何使用PixelFormat,Height,Width和Scan0属性; the LockBits and UnlockBits methods; LockBits和UnlockBits方法; and the ImageLockMode enumeration. 和ImageLockMode枚举。 This example is designed to be used with Windows Forms. 此示例旨在与Windows窗体一起使用。 To run this example, paste it into a form and handle the form's Paint event by calling the LockUnlockBitsExample method, passing e as PaintEventArgs. 要运行此示例,请将其粘贴到表单中,并通过调用LockUnlockBitsExample方法处理表单的Paint事件,并将e作为PaintEventArgs传递。

private void LockUnlockBitsExample(PaintEventArgs e)
{

    // Create a new bitmap.
    Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");

    // Lock the bitmap's 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 rather than copy the array out, manipulate it and copy it back, you can operate on the bitmap in place using an unsafe pointer. 如果你想要更快,而不是复制数组,操纵它并将其复制回来,你可以使用不安全的指针对位图进行操作。 In this case, the inner part would change as follows: 在这种情况下,内部部分将更改如下:

    // Lock the bitmap's 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 to note the PixelFormat of the bitmap. 请注意注意位图的PixelFormat。 The example above assumes its 24 bits per pixel BGR. 上面的例子假设它的每像素BGR为24位。 In actual fact many bitmaps are BGRA (32bits per pixel) so you will need to modify four bytes for Blue, Gree, Red, Alpha in that order per pixel. 实际上,许多位图都是BGRA(每像素32位),因此您需要按照每个像素的顺序修改蓝色,格力,红色,Alpha四个字节。

使用新的新线程来处理图像,你的程序在处理线程时不会冻结

This looks like hungry algorytm. 这看起来像饥饿的algorytm。 Think about it once more and define points, where you can safely leave the function. 再考虑一下并定义点,您可以放心地离开该功能。 eg add return after 例如,之后添加返回

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

I believe you can find more points where you can leave, because you can be sure, there is nothing more to find, or why you're finishing your cycles even you already found what you are looking for? 我相信你可以找到更多可以离开的地方,因为你可以肯定,没有什么可以找到的,或者你为什么要完成你的周期,即使你已经找到了你想要的东西?

Or maybe you can set it up in a different way. 或者也许你可以用不同的方式进行设置。 You can start with scanning the picture for the first pixel, and after you find it, you can check second one, third, etc and if eg third pixel is not correct you'll need to return back and continue. 您可以从第一个像素扫描图片开始,找到后,您可以检查第二个,第三个像素等,如果第三个像素不正确,则需要返回并继续。

So if not even one pixel will be correct, then you'll go through the picture only once. 因此,如果连一个像素都不正确,那么您只需要浏览一次。

In other words, do not try to compare two x*y areas, try to compare pixels first, areas later. 换句话说,不要试图比较两个x * y区域,尝试先比较像素,稍后再区域。 you should be able to reduce the time significantly. 你应该能够显着减少时间。

I don't know anything about image processing and your problem seems general enough that someone probably must have developed a specific algorithm. 我对图像处理一无所知,你的问题似乎很普遍,有人可能已经开发了一个特定的算法。 However, in case not, here is my two cents: It is not surprising that your algorithm runs slowly, if two images are of size W1xH1 and W2xH2 your run time is O(W1.H1.W2.H2) at the worst case. 但是,如果没有,这是我的两分钱:你的算法运行缓慢并不奇怪,如果两个图像的大小为W1xH1和W2xH2,那么在最坏的情况下你的运行时间是O(W1.H1.W2.H2) The average case is much less but still not fast enough. 平均情况要少得多,但仍然不够快。

Searching a substring inside a string is the one dimensional analogue of your task and it is well researched problem. 在字符串中搜索子字符串是您的任务的一维模拟,这是一个很好的研究问题。 You may want to check Boyer-Moore string searching algorithm and see if you can adapt the basic idea to your problem. 您可能需要检查Boyer-Moore字符串搜索算法,看看是否可以根据您的问题调整基本思路。

I have put some beginners' tutorials on http://www.tmorley.net . 我在http://www.tmorley.net上放了一些初学者的教程。 These explain image processing using C#. 这些解释了使用C#的图像处理。 The first thing that is done is that the image file (jpg, bmp, etc) immediately has the pixel data extracted into an array. 首先要做的是图像文件(jpg,bmp等)立即将像素数据提取到数组中。 Processing can be done on this quickly and then the results are put back into a Bitmap for display or saving to disc. 可以快速完成处理,然后将结果放回到位图中以显示或保存到光盘。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM