【问题标题】:For Loops, Application Hanging对于循环,应用程序挂起
【发布时间】:2011-06-13 19:21:47
【问题描述】:

好吧,我正在创建一个应用程序,我使用 for 循环基本上读取图像的每个像素,寻找像素颜色的模式(简单的东西)无论如何,由于某种原因,我的应用程序只是锁定并且永远不会恢复正常.我一遍又一遍地循环代码,没有发现任何真正的问题。

我唯一注意到的是,ScanPixelsLater 中的 for 循环可能会提前退出。我已经尽可能多地注释了代码,

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

}

【问题讨论】:

    标签: c# loops for-loop nested-loops


    【解决方案1】:

    因此,要了解您的应用程序为何挂起,您需要了解一点 WinForm 应用程序的工作原理。

    运行 UI 的线程也有所谓的消息泵。此消息泵包含从操作系统(和其他来源)传递到所有 UI 元素的消息。他们告诉他们什么时候改变状态,什么时候重绘自己,等等。当你有一个像你这样长时间运行的循环时,消息泵无法处理消息。 get 排队,但从未处理,这就是应用程序“挂起”的意思。

    您的应用程序不可能永远恢复。您的循环最终将结束,您的 UI 将再次响应(假设我没有错过某个地方的无限循环,但我认为我没有错过)。但是,GDI+ GetPixel 方法确实非常慢,如果您的图像很大,那么这组循环将需要很长时间才能完成。您可能必须深入研究不安全的上下文并使用 LockBits 获取指向图像内存的指针。这里有很多关于如何做到这一点的例子。

    编辑:更仔细地查看您的代码后,很明显它的效率相对较低。您至少有 6 级嵌套 for 循环在那里进行,因此您实际上是在只需要一次扫描时多次扫描图像。

    图像处理是一个资源密集型过程。您需要注意尽可能高效地完成所有性能密集型工作。

    【讨论】:

    • 感谢您的回复,非常感谢,但我真的想不出另一种方法来完成上述操作。如果它对我想要实现的目标有效,这是“最有效”的方法。我讨厌所有的循环,我会继续思考:)
    【解决方案2】:

    从我的 cmets 迁移(现已删除)

    这不是给你带来麻烦的原因,因为它永远不会恢复正常,但你不应该在循环中调用GetPixel。使用它的速度非常慢。相反,您可以使用指针或像素数组使用 google 或 stackoverflow 搜索“getpixel slow”,并出现大量解决方案。

    更新:我现在看了一点代码...在嵌套 for 循环的主代码 (ScanPixels) 中,您调用 DetectNextWhiteLine 这也是一个嵌套的for循环,最后调用ScanPixelsLater,这也是一个嵌套的for循环。现在你可能得到了一个 6 级深度的嵌套 for 循环 O(n^6),它调用了一个相对昂贵的方法 (GetPixel)。您应该只对像素进行几次迭代。这可能就是它永远不会停止的原因,因为这可能是 1000^6*~100 条指令 :)

    【讨论】:

    • @lasseespeholt:C# 确实支持指针操作,并且确实在 Bitmap/Image 类中公开。
    • @leppie 我想我让自己不清楚(感谢我的英语技能)。我并不是说它不支持指针(它支持,在 stackoverflow 和谷歌上寻找解决方案),但上次我检查时,它没有像 WPF WriteableBitmap 那样暴露像素数组。所以只到了句子的最后一部分。
    • @lasseespeholt:还是不明白。该功能位于System.Drawing。查看Bitmap.LockBits 方法。 msdn2.microsoft.com/en-us/library/… 但是,您永远不会将代码用作 MSDN 上的示例,这只是愚蠢的开销。
    • @leppie +1 谢谢 :) 我不知道,因为我不使用 GDI+,我只说“想想”。
    • @lasseespeholt:请做 :) 我不使用 WPF。
    猜你喜欢
    • 2017-12-16
    • 1970-01-01
    • 2018-09-12
    • 1970-01-01
    • 1970-01-01
    • 2014-12-11
    • 1970-01-01
    • 2011-07-04
    • 2011-02-10
    相关资源
    最近更新 更多