c # Низкая производительность в цикле for при поиске пикселей на изображении - PullRequest
4 голосов
/ 28 февраля 2012

Когда я использую следующий код, цикл занимает около 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 { }
                }
            }
        }
    }
}

Ответы [ 5 ]

11 голосов
/ 28 февраля 2012

Проблема производительности вызвана GetPixels / SetPixels, который является чрезвычайно медленным способом доступа к данным в .NET Bitmap. Вместо этого я бы посмотрел на метод Bitmap.LockBits, чтобы получить указатель на растровое изображение и напрямую манипулировать данными. Это будет на порядок быстрее.

См. MSDN :

В следующем примере кода показано, как использовать свойства PixelFormat, Height, Width и Scan0; методы LockBits и UnlockBits; и перечисление ImageLockMode. Этот пример предназначен для использования с Windows Forms. Чтобы запустить этот пример, вставьте его в форму и обработайте событие Paint формы, вызвав метод LockUnlockBitsExample, передав 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 растрового изображения. В приведенном выше примере предполагается, что это 24 бит на пиксель BGR. На самом деле многие растровые изображения представляют собой BGRA (32 бита на пиксель), поэтому вам нужно будет изменить четыре байта для синего, зеленого, красного и альфа в этом порядке на пиксель.

2 голосов
/ 28 февраля 2012

используйте новый новый поток для обработки изображения, и ваша программа не будет зависать при обработке Threading

1 голос
/ 28 февраля 2012

Я ничего не знаю об обработке изображений, и ваша проблема кажется достаточно общей, чтобы кто-то, вероятно, разработал определенный алгоритм. Однако, если нет, вот мои два цента: неудивительно, что ваш алгоритм работает медленно, если два изображения имеют размер W1xH1 и W2xH2, ваше время выполнения в худшем случае составляет O(W1.H1.W2.H2). Средний случай намного меньше, но все еще недостаточно быстр.

Поиск подстроки внутри строки - это одномерный аналог вашей задачи, и это хорошо изученная проблема. Вы можете проверить алгоритм поиска строк Бойера-Мура и посмотреть, сможете ли вы адаптировать основную идею к вашей проблеме.

1 голос
/ 28 февраля 2012

Это похоже на голодный алгоритм.Подумайте об этом еще раз и определите точки, где вы можете безопасно покинуть функцию.например, добавить возврат после

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

Я полагаю, вы можете найти больше точек, где вы можете уйти, потому что вы можете быть уверены, что больше нечего найти, или почему вы заканчиваете свои циклы, даже если вы уже нашличто вы ищете?

Или, может быть, вы можете настроить его по-другому.Вы можете начать со сканирования изображения для первого пикселя, а после того, как найдете его, вы можете проверить второй, третий и т. Д., И, например, если третий пиксель не верен, вам нужно вернуться назад и продолжить.

Таким образом, если ни один пиксель не будет правильным, то вы пройдете изображение только один раз.

Другими словами, не пытайтесь сравнивать две x * y области, попробуйте сначала сравнить пиксели, областипотом.Вы должны быть в состоянии значительно сократить время.

0 голосов
/ 06 марта 2012

Я поместил некоторые учебники для начинающих на http://www.tmorley.net. Они объясняют обработку изображений с использованием C #.Первое, что нужно сделать, это то, что файл изображения (jpg, bmp и т. Д.) Немедленно содержит данные пикселей, извлеченные в массив.Обработка может быть выполнена на этом быстро, а затем результаты помещаются обратно в растровое изображение для отображения или сохранения на диск.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...