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

本文关键字:for 循环 性能 像素 搜索 搜索图 图像 | 更新日期: 2023-09-27 18:26:04

当我使用以下代码时,如果在图像中找不到我要搜索的图像,大约需要3-5秒才能完成循环。在搜索过程中,程序的其余部分暂停,我的计时器不同步,看起来程序冻结了几秒钟。图像不是很大,"printscreen"大约是344x354,"Ok"大约是15x7。我知道这是因为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 { }
                }
            }
        }
    }
}

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

性能问题是由GetPixels/SetPixels引起的,这是访问.NET位图中数据的一种非常缓慢的方式。相反,我会研究Bitmap.LockBits方法来获得指向位图的指针并直接操作数据。它将更快一个数量级。

请参阅MSDN:

以下代码示例演示如何使用PixelFormat、Height、Width和Scan0属性;LockBits和UnlockBits方法;和ImageLockMode枚举。此示例设计用于Windows窗体。要运行此示例,请将其粘贴到表单中,并通过调用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);
}

如果您想更快地执行操作,而不是将数组复制出去、操作它并将其复制回,则可以使用不安全的指针对位图进行操作。在这种情况下,内部会发生如下变化:

    // 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);

只需注意位图的PixelFormat即可。上面的示例假设其每像素BGR为24位。事实上,许多位图都是BGRA(每像素32位),因此您需要按每个像素的顺序修改Blue、Gree、Red和Alpha的四个字节。

使用新的线程来处理映像,并且在处理线程时程序不会冻结

这看起来像饥饿算法。再次思考并定义点,在这些点上可以安全地离开函数。例如,在之后添加退货

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

我相信你可以找到更多可以离开的地方,因为你可以肯定,没有什么可以找到的了,或者为什么你已经找到了你想要的东西,却还在完成你的周期?

或者你可以用不同的方式设置它。你可以从扫描图片的第一个像素开始,找到后,你可以检查第二个、第三个等,如果第三个像素不正确,你需要返回并继续。

因此,如果没有一个像素是正确的,那么你只需要浏览一次图片。

换句话说,不要尝试比较两个x*y区域,先尝试比较像素,然后再尝试比较区域。你应该能够显著减少时间。

我对图像处理一无所知,你的问题似乎很普遍,可能有人开发了一种特定的算法。然而,如果不是这样的话,这是我的两分钱:你的算法运行缓慢并不奇怪,如果两个图像的大小是W1xH1和W2xH2,那么在最坏的情况下,你的运行时间是O(W1.H1.W2.H2)。平均病例要少得多,但仍然不够快。

在字符串中搜索子字符串是你任务的一维模拟,这是一个经过充分研究的问题。您可能需要检查Boyer-Moore字符串搜索算法,看看是否可以将基本思想应用于您的问题。

我在上面放了一些初学者教程http://www.tmorley.net.这些解释了使用C#的图像处理。首先要做的是,图像文件(jpg、bmp等)立即将像素数据提取到数组中。可以快速对此进行处理,然后将结果放回位图中进行显示或保存到光盘。