For 循环、应用程序挂起
好吧,我正在创建一个应用程序,我使用 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
}
Well I'm creating an application, I'm using for loops to basically read every pixel of an image looking for patterns in pixel color (simple stuff) Anyway for some reason my application simply locks up and never reverts back to normal. I've loop through the code time and time again without seeing any real problems.
The only thing I've noticed, is the for loop in ScanPixelsLater may be exiting early. I've commented the code as much as possible,
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
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
![扫码二维码加入Web技术交流群](/public/img/jiaqun_03.jpg)
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
因此,要了解应用程序挂起的原因,您需要了解一点 WinForm 应用程序的工作原理。
运行 UI 的线程也有所谓的消息泵。该消息泵包含从操作系统(和其他源)传递到所有 UI 元素的消息。它们告诉它们何时更改状态、何时重绘自身等。当您有像您这样的长时间运行循环时,消息泵无法处理消息。该获取已排队,但从未被处理,这就是应用程序“挂起”的含义。
您的应用程序不太可能永远无法恢复。你的循环最终将结束,你的 UI 将再次变得响应(假设我没有错过某个地方的无限循环,但我不认为我错过了)。然而,GDI+ GetPixel 方法确实非常慢,如果您的图像很大,那么这组循环将需要很长时间才能完成。您可能必须深入研究不安全的上下文并使用 LockBits 获取指向图像内存的指针。这里有很多关于如何做到这一点的例子。
编辑:仔细查看您的代码后,很明显它的效率相对较低。那里至少有 6 层嵌套的 for 循环,因此当只需要一次扫描时,您实际上是在多次扫描图像。
图像处理是一个资源密集型过程。您需要小心谨慎,尽可能高效地完成所有性能密集型工作。
So, to understand why your application is hanging you need to know a little bit about how WinForm applications work.
The thread that your UI is running on also has what is called a message pump. This message pump contains messages that get passed from the operating system (and other sources) to all of the UI elements. They tell them when to change state, when to redraw themselves, etc. When you have a long running loop like yours the message pump cannot process messages. The get queued up, but never processed, and this is what it means for an application to 'hang'.
It is unlikely that your application will never recover. Your loop will eventually end and your UI will become responsive again (assuming I didn't miss an infinite loop somewhere, but I don't think that I did). However, the GDI+ GetPixel method is really very slow, and if your image is large at all that set of loops is going to take a long time to complete. You will likely have to delve into an unsafe context and obtain a pointer to the image's memory using LockBits. There are many examples of how to do that floating around here.
EDIT: After looking at your code a bit more closely it is also apparent that it is relatively inefficient. You have at least 6 levels of nested for loops going on there, so you are essentially scanning the image multiple times when only a single scan is needed.
Image processing is a resource intensive process. You need to be careful to do all of your performance intensive work as efficiently as possible.
从我的评论中迁移(现已删除)
这绝对不会给您带来麻烦,因为它永远不会恢复正常,但您永远不应该在循环中调用
GetPixel
。使用它的速度慢得令人难以置信。相反,您可以使用指针或像素数组通过 google 或 stackoverflow 搜索“getpixel Slow”,会出现大量解决方案。更新:我现在已经查看了一些代码...在嵌套 for 循环的主代码 (
ScanPixels
) 中,您调用DetectNextWhiteLine
这也是一个嵌套的 for 循环,最后调用ScanPixelsLater
这也是一个嵌套的 for 循环。现在您可能会得到一个 6 层深度的嵌套 for 循环 O(n^6),它调用一个相对昂贵的方法 (GetPixel
)。您应该只迭代像素几次。这可能就是为什么它永远不会停止的原因,因为这可能是 1000^6*~100 条指令:)migrated from my comments (now deleted)
It is properly not this which causes you trouble since it never comes to normal but you should never call
GetPixel
in a loop. It is incredible slow to use that. Instead you can use pointers or an pixel array Search for "getpixel slow" with google or stackoverflow and a large number of solutions come up.Updated: I have looked a little bit on the code now... In the main code (
ScanPixels
) which is a nested for-loop, you callDetectNextWhiteLine
which is also a nested for-loop and finally this callsScanPixelsLater
which is ALSO a nested for-loop. Now you potentially got a 6-level-deep nested for loop O(n^6) which calls an relatively expensive method (GetPixel
). You should only iterate over the pixels a few times. This may be why it never stops because this is potential something like 1000^6*~100 instructions :)