【问题标题】:C# - Finding the Boundaries of an Image (not the size)C# - 查找图像的边界(不是大小)
【发布时间】:2011-03-28 12:31:22
【问题描述】:

我正在开发一个应用程序来平均分割图像网格并将图像居中(基于它们的相似性)。到目前为止,我可以设法修复小尺寸的图像网格,但是每当我尝试更大的“精灵”尺寸(例如 100x100)时,我都会收到 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<MyPixel>) 的临时像素列表
  • imap 包含整个图像(List<MyPixel>)

我想这不是内存问题,因为我的应用只占用了大约 16mb 的顶部。

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

提前致谢!

【问题讨论】:

  • 我不太理解你想要完成的事情。您是否正在尝试拍摄一张大图像并将其拆分为多个相同大小的块?
  • @Lucas 我在一张图片中有不同图片的网格。例如,一个 100x100 的 png 包含 100 个大小为 10x10 像素的图像。假设我有一个图像网格,但它们没有对齐并且具有不同的大小:我试图使它们都具有相同的大小,并根据它们的像素相似度来对齐它们。

标签: c# recursion bitmap stack-overflow image-manipulation


【解决方案1】:

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

进程精灵(0,j) 进程精灵(1,j) 进程精灵(2,j) ... processSprite(1599, j)

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

编辑:

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

IEnumerable<Rectangle> FindImageTiles(Bitmap compositeImage)
{
    var result = new List<Rectangle>();

    // 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 => p.A != 0))
                    {
                        rect.Y--;
                        rect.Height++;
                    }
                    else if (rect.Bottom < compositeImage.Height
                        && xRange
                            .Select(bx => compositeImage.GetPixel(bx, rect.Bottom))
                            .Any(p => p.A != 0))
                    {
                        rect.Height++;
                    }
                    else if (rect.Left >= 0
                        && yRange
                            .Select(by => compositeImage.GetPixel(rect.Left, by))
                            .Any(p => p.A != 0))
                    {
                        rect.X--;
                        rect.Width++;
                    }
                    else if (rect.Right < compositeImage.Width
                        && yRange
                            .Select(by => compositeImage.GetPixel(rect.Right, by))
                            .Any(p => p.A != 0))
                    {
                        rect.Width++;
                    }
                    else
                    {
                        foundBounds = true;
                    }
                }
                result.Add(rect);
            }
        }
    }

    return result;
}

【讨论】:

  • "进行深度优先搜索的线性循环,如果您想找到最接近起点的像素,可能会采用螺旋模式。"好点...我不确定我如何知道何时需要中断循环,但我会看一下并尝试简化它。
  • 我相信我最初误解了您试图解决的问题。我添加了一些应该可以工作的代码(用来自互联网的一些图片进行了测试)
  • 感谢雅各布!像魅力一样工作!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-03
  • 1970-01-01
  • 1970-01-01
  • 2021-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多