C# – 查找图像的边界(不是大小)

我正在开发一个应用程序来均匀地分割图像网格并使图像居中(基于它们的相似性)。 到目前为止,我可以设法修复一个小尺寸的图像网格,但每当我尝试更大的“精灵”大小(例如100×100)时,我就会出现Stack Overflow错误。

是的我正在使用递归,但每当检查一个像素时,我设置一个布尔值来停用它,将它复制到一个列表并继续检查其他(在所有方向),直到列表填满了来自网格。 我不确定这是否是最佳方式,因为对于每次调用,我调用相同的方法7次(假设有7个相邻的像素尚未检查)…直到没有剩余的像素来检查,并且我可以转到网格中的下一个图像。

我尝试跟踪错误开始发生的地方,检查了大约1600像素并将它们添加到列表中。 MyPixel是一个包含4个变量的类:x(int),y(int),color(Color)和checked(bool)

public void processSprite(int i, int j) { //OOO //OXO //OOO pixeltemp.Add(new MyPixel(imap.pixels[i, j].x, imap.pixels[i, j].y, imap.pixels[i, j].color)); imap.pixels[i, j].read = true; //OOO //OOX //OOO try { if (!imap.pixels[i + 1, j].read) { if (imap.pixels[i + 1, j].color.A == 0) //Found a Border { imap.pixels[i + 1, j].read = true; } else { processSprite(i + 1, j); } } } //... (code goes on) } 
  • pixeltemp是保存图像的临时像素列表(List)
  • imap包含整个图像(List)

我想这不是内存问题,因为我的应用程序只需要大约16mb的顶部。

我的问题是,如果不是无限递归,为什么我会出现这种“堆栈溢出”错误? 有更简单的方法吗? 我认为我的代码看起来很难看,我只是不知道如何让它变得更好。

提前致谢 !

堆栈溢出不是由无限递归引起的,而是由进程可以处理的更多递归(或者更确切地说,调用堆栈)引起的。 在您的情况下,每次对processSprite递归调用都将对processSprite执行相同数量的递归调用。 因此,在没有找到边界的1600像素的最坏情况下,您的调用树将如下所示:

   processSprite(0,j)
     processSprite(1,j)
       processSprite(2,j)
         ...
           processSprite(1599,j)< - 这是1600个调用帧,
                                     足以溢出。

您需要将算法重新组织为执行深度优先搜索的线性循环,如果您想要找到最接近起点的像素,则可能采用螺旋模式。 我确信已经有其他已经开发出的其他spiffy算法来解决这个问题。

编辑:

我想我现在更好地理解你要解决的问题。 听起来你的图像可能包含由0-alpha像素包围的多个图像切片,并且您希望找到每个切片的边界矩形。 这看起来像一个有趣的问题需要解决,所以我实现了它:

 IEnumerable FindImageTiles(Bitmap compositeImage) { var result = new List(); // Scan for a non-empty region that hasn't already been "captured" for (var x = 0; x < compositeImage.Width; x++) { for (var y = 0; y < compositeImage.Height; y++) { // Only process the pixel if we don't have a rectangle that // already contains this and if it's not empty if (!result.Any(r => r.Contains(x, y)) && compositeImage.GetPixel(x, y).A != 0) { // Now that we've found a point, create a rectangle // surrounding that point, then expand outward until // we have a bounding rectangle that doesn't intersect // with the tile var rect = new Rectangle(x - 1, y - 1, 2, 2); bool foundBounds = false; while (!foundBounds) { var xRange = Enumerable.Range(rect.Left, rect.Right) .Where(px => px >= 0 && px < compositeImage.Width); var yRange = Enumerable.Range(rect.Top, rect.Bottom) .Where(py => py >= 0 && py < compositeImage.Height); // Adjust the top if (rect.Top >= 0 && xRange .Select(bx => compositeImage.GetPixel(bx, rect.Top)) .Any(p => pA != 0)) { rect.Y--; rect.Height++; } else if (rect.Bottom < compositeImage.Height && xRange .Select(bx => compositeImage.GetPixel(bx, rect.Bottom)) .Any(p => pA != 0)) { rect.Height++; } else if (rect.Left >= 0 && yRange .Select(by => compositeImage.GetPixel(rect.Left, by)) .Any(p => pA != 0)) { rect.X--; rect.Width++; } else if (rect.Right < compositeImage.Width && yRange .Select(by => compositeImage.GetPixel(rect.Right, by)) .Any(p => pA != 0)) { rect.Width++; } else { foundBounds = true; } } result.Add(rect); } } } return result; }