Для циклов, приложение висит - PullRequest
3 голосов
/ 22 января 2011

Что ж, я создаю приложение, я использую циклы for, чтобы в основном прочитать каждый пиксель изображения в поисках шаблонов в цвете пикселя (простые вещи). В любом случае, по какой-то причине мое приложение просто блокируется и никогда не возвращается к нормальному , Я перебираю код снова и снова, не видя никаких реальных проблем.

Единственное, что я заметил, это цикл for в ScanPixelsLater , возможно завершается рано. Я прокомментировал код как можно больше,

private Point topLeftc, bottomLeftc, topRightc, bottomRightc;

/// <summary>
/// Starts the initial looping process, designed only to loop through ONCE, ScanPixelsLater takes over
/// </summary>
/// <param name="img">Image to scan</param>
public void ScanPixels(Bitmap img)
{
    int whitePixel = 0;

    for (int y = 100; y < img.Height; y++)
    {

        for (int x = 100; x < img.Width; x++)
        {

            if (img.GetPixel(x, y) == Color.FromArgb(255, 255, 255, 255))
            {
                // img.SetPixel(x, y, Color.Green);
                whitePixel++;

            }
            else { whitePixel = 0; }


            if (whitePixel == 18)
            {
                whitePixel = 0;
                topLeftc = new Point(x - 18, y);
                DetectNextWhiteLine(topLeftc, img);

            }
        }
    }


}


/// <summary>
/// First creates the TopRight value via using the last pixel in x range, and using the current Y value
/// Then a Y loop is started 10 pixels down, within this loop is another X loop which scans the X range
/// If 10 consecutive white pixels are found, the TopLeft X value and the current Y value are used to map the 
/// BottomLeft and BottomRight coordinates. Finally a new Point is created which starts (x = 1) and (y = currentYValue + 2)
/// The ScanPixelsLater method is then called, passing the new Point (newLocation).
/// 
/// </summary>
/// <param name="p">The x and y value of where the pixels were found</param>
/// <param name="img">Image being used</param>
private void DetectNextWhiteLine(Point p, Bitmap img)
{

    int whitePixel = 0;
    topRightc = new Point(img.Width, topLeftc.Y);

    for (int y = p.Y + 10; y < img.Height; y++)
    {

        for (int x = p.X - 5; x < img.Width; x++)
        {
            if (img.GetPixel(x, y) == Color.FromArgb(255, 255, 255, 255))
            {
                whitePixel++;
            }
            else
            {
                whitePixel = 0;
            }

            if (whitePixel == 10)
            {
                bottomLeftc = new Point(topLeftc.X, y);
                bottomRightc = new Point(img.Width, y);
                Cords.Add(new Coordinates(topLeftc, topRightc, bottomLeftc, bottomRightc));

                Point newLocation = new Point(1, y + 2);
                calls++;
                ScanPixelsLater(newLocation, img); //rescan the image from new y axis 
            }
        }
    }

}



/// <summary>
/// Loops through the pixels based on the p parameter, if 15 white pixels are found, the location (x & y) is
/// passed to the DetectNextWhiteLine method which fixes the next line with a similar number of white pixels.
/// </summary>
/// <param name="p">The Point(x,y) at which to start scanning</param>
/// <param name="img"></param>
private void ScanPixelsLater(Point p, Bitmap img)
{

    int whitePixel = 0;

    for (int y = p.Y; y < img.Height; y++)
    {

        for (int x = p.X; x < img.Width; x++)
        {
            if (img.GetPixel(x, y) == Color.FromArgb(255, 255, 255, 255))
            {
                whitePixel++;
            }
            else
            {
                whitePixel = 0;
            }

            if (whitePixel == 15)
            {
                bottomLeftc = new Point(topLeftc.X, y);
                topLeftc = new Point(x - 15, y);
                calls++;
                DetectNextWhiteLine(topLeftc, img);
            }

        }
    }

    // only want this to execute after all the pixels within the entire img have been read
    // possibly executing early.

    DrawWhiteLines(img);
    AppArgs aa = new AppArgs(true);
    Change(this, aa); // custom event handler, fired behind form to update GUI

}

Ответы [ 2 ]

2 голосов
/ 22 января 2011

перенесено из моих комментариев (теперь удалено)

Это не то, что вызывает у вас проблемы, так как это никогда не приходит в норму, но вы никогда не должны вызывать GetPixel в цикле. Это невероятно медленно, чтобы использовать это. Вместо этого вы можете использовать указатели или массив пикселей. Выполните поиск «getpixel slow» с помощью google или stackoverflow, и появится большое количество решений.

Обновлено: Я немного посмотрел на код сейчас ... В основном коде (ScanPixels), который является вложенным циклом for, вы вызываете DetectNextWhiteLine, который также является вложенный цикл for и, наконец, это вызывает ScanPixelsLater, который также является вложенным циклом for. Теперь у вас потенциально есть 6-уровневый вложенный цикл O (n ^ 6), который вызывает относительно дорогой метод (GetPixel). Вам следует перебирать пиксели только несколько раз. Возможно, поэтому он никогда не останавливается, потому что это потенциально что-то вроде 1000 ^ 6 * ~ 100 инструкций:)

2 голосов
/ 22 января 2011

Итак, чтобы понять, почему ваше приложение зависает, вам нужно немного узнать о том, как работают приложения WinForm.

Поток, на котором работает ваш пользовательский интерфейс, также имеет так называемый насос сообщений. Этот насос сообщений содержит сообщения, которые передаются из операционной системы (и других источников) всем элементам пользовательского интерфейса. Они сообщают им, когда менять состояние, когда перерисовывать себя и т. Д. Когда у вас такой длинный цикл, как у вас, насос сообщений не может обрабатывать сообщения. Get ставится в очередь, но никогда не обрабатывается, и это означает, что приложение «зависает».

Вряд ли ваше приложение никогда не восстановится. Ваш цикл в конечном итоге закончится, и ваш пользовательский интерфейс снова станет отзывчивым (при условии, что я где-то не пропустил бесконечный цикл, но я не думаю, что это сделал). Тем не менее, метод GDI + GetPixel действительно очень медленный, и если ваше изображение вообще большое, этот набор циклов займет много времени. Скорее всего, вам придется углубиться в небезопасный контекст и получить указатель на память изображения с помощью LockBits. Есть много примеров того, как это можно сделать здесь.

РЕДАКТИРОВАТЬ: После более тщательного изучения кода также становится очевидным, что он относительно неэффективен. У вас есть как минимум 6 уровней вложенных циклов, поэтому вы сканируете изображение несколько раз, когда требуется только одно сканирование.

Обработка изображений является ресурсоемким процессом. Вы должны быть осторожны, чтобы выполнять всю интенсивную работу с максимальной эффективностью.

...