代码之家  ›  专栏  ›  技术社区  ›  George Johnston

在画布上查找绘制区域的算法

  •  6
  • George Johnston  · 技术社区  · 14 年前

    更新:我正试图从这篇文章中去掉一些杂乱的内容,并更简明地总结出来。如果需要,请参阅原始编辑。

    我目前正试图在位图画布上跟踪一系列单色斑点。

    例如,我试图跟踪的位图示例如下: alt text http://www.refuctored.com/polygons.bmp

    在成功跟踪了图像上3个斑点的轮廓之后,我将拥有一个类,该类将斑点的颜色绑定到表示斑点轮廓的点列表(而不是斑点内的所有像素)。

    我遇到的问题是逻辑上的,在这种情况下,相邻的像素除了前一个像素没有周围的像素。

    例如,上面的例子可以很好地跟踪,但是第二个会失败,因为像素已经被使用了,所以它没有位置。

    alt text http://www.refuctored.com/error.jpg

    我从左到右,从上到下追踪,喜欢对角线而不是直角。我必须能够根据提取的数据重新绘制区域的精确副本,因此列表中的像素必须按正确的顺序进行复制。

    到目前为止,我的尝试已经失败了,几天来我一直在努力重写算法,每次都有点不同,以解决这个问题。到目前为止,我还没有成功。有没有其他人也有类似的问题,比如我的,谁有一个好的算法来找到边缘?

    6 回复  |  直到 14 年前
        1
  •  2
  •   Quasimondo    14 年前

    避免这些死胡同的一个简单技巧是在跟踪之前使用最近的邻居缩放算法将要跟踪的图像的大小增加一倍。那样的话,你就永远不会得到一条带子。

    另一种方法是使用行进平方算法,但它似乎仍有一两个失败的案例: http://www.sakri.net/blog/2009/05/28/detecting-edge-pixels-with-marching-squares-algorithm/

        2
  •  1
  •   Eric Perko    14 年前

    你看过斑点检测算法吗?例如, http://opencv.willowgarage.com/wiki/cvBlobsLib 如果您可以将OpenCV集成到您的应用程序中。再加上阈值技术,可以为图像中的每种颜色(或颜色范围)创建二进制图像,您可以很容易地找到相同颜色的斑点。对图像中的每种颜色重复此操作,您将得到按颜色排序的斑点列表。

    如果不能直接使用opencv,可能该库引用的论文(“使用轮廓跟踪技术的线性时间分量标记算法”,F.Chang等人)将提供一种查找斑点的好方法。

        3
  •  0
  •   Ben S    14 年前

    不要使用递归,而是使用堆栈。

    伪代码:

    Add initial pixel to polygon
    Add initial pixel to stack
    while(stack is not empty) {
        pop pixel off the stack
        foreach (neighbor n of popped pixel) {
            if (n is close enough in color to initial pixel) {
                Add n to polygon
                Add n to stack
            }
        }
    }
    

    这将比使用递归的相同解决方案使用更少的内存。

        4
  •  0
  •   Ahmet Kakıcı    14 年前

    只需将“图像”发送到buildPixelaray函数,然后调用findRegion。 之后,“颜色”变量将在每个列表成员中保存颜色列表和像素坐标。

    我已经从我的一个项目中复制了源代码,可能有一些未定义的变量或语法错误。

        public class ImageProcessing{
        private int[,] pixelArray;
        private int imageWidth;
        private int imageHeight;
        List<MyColor> colors;
    
        public void BuildPixelArray(ref Image myImage)
        {
            imageHeight = myImage.Height;
            imageWidth = myImage.Width;
            pixelArray = new int[imageWidth, imageHeight];
            Rectangle rect = new Rectangle(0, 0, myImage.Width, myImage.Height);
            Bitmap temp = new Bitmap(myImage);
            BitmapData bmpData = temp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            int remain = bmpData.Stride - bmpData.Width * 3;
            unsafe
            {
                byte* ptr = (byte*)bmpData.Scan0;
                for (int j = 15; j < bmpData.Height; j++)
                {
                    for (int i = 0; i < bmpData.Width; i++)
                    {
                        pixelArray[i, j] = ptr[0] + ptr[1] * 256 + ptr[2] * 256 * 256;
                        ptr += 3;
                    }
                    ptr += remain;
                }
            }
            temp.UnlockBits(bmpData);
        }
    
        public void FindRegions()
        {
            colors = new List<MyColor>();
    
            for (int i = 0; i < imageWidth; i++)
            {
                for (int j = 0; j < imageHeight; j++)
                {
                    int tmpColorValue = pixelArray[i, j];
                    MyColor tmp = new MyColor(tmpColorValue);
                    if (colors.Contains(tmp))
                    {
                        MyColor tmpColor = (from p in colors
                                            where p.colorValue == tmpColorValue
                                            select p).First();
    
                        tmpColor.pointList.Add(new MyPoint(i, j));
                    }
                    else
                    {
                        tmp.pointList.Add(new MyPoint(i, j));
                        colors.Add(tmp);
                    }
                }
            }
        }
    }
    
    public class MyColor : IEquatable<MyColor>
    {
        public int colorValue { get; set; }
        public List<MyPoint> pointList = new List<MyPoint>();
        public MyColor(int _colorValue)
        {
            colorValue = _colorValue;
        }
        public bool Equals(MyColor other)
        {
            if (this.colorValue == other.colorValue)
            {
                return true;
            }
            return false;
        }
    }
    public class MyPoint
    {
        public int xCoord { get; set; }
        public int yCoord { get; set; }
    
        public MyPoint(int _xCoord, int _yCoord)
        {
            xCoord = _xCoord;
            yCoord = _yCoord;
        }
    }
    
        5
  •  0
  •   Loren Pechtel    14 年前

    如果您得到一个堆栈溢出,我想您不会排除已经检查的像素。参观广场的第一项检查应该是你以前是否来过这里。

    另外,不久前我还在研究一个相关的问题,我想出了一种使用更少内存的不同方法:

    排队:

    AddPointToQueue(x, y);
    repeat
       x, y = HeadItem;
       AddMaybe(x - 1, y); x + 1, y; x, y - 1; x, y + 1;
    until QueueIsEmpty;
    
    AddMaybe(x, y):
    if Visited[x, y] return;
    Visited[x, y] = true;
    AddPointToQueue(x, y);
    

    这种方法的要点是,您最终得到的队列基本上包含了一条环绕映射区域的线。这比堆栈更好地限制了内存使用。

    如果相关的话,它也可以进行细微的修改,以产生到任何一个正方形的移动距离。

        6
  •  0
  •   Jazz.    14 年前

    尝试使用aforge.net。我将按颜色、阈值进行过滤,然后您可以做一些形态学的工作来减少黑白区域,以减少对象之间的接触。然后你就可以去找那些混蛋了。