C#中的Alpha形状(凹壳)算法

时间:2013-05-18 13:57:14

标签: c# .net aforge

我需要使用凹算法从一组点来勾勒出形状,在AForge.NET中我是否可以使用我的实现,我已经读过某个地方AForge.NET已经实现了该算法但我找不到它在文档中。

非常感谢任何帮助,

致以最诚挚的问候,

丹尼尔

2 个答案:

答案 0 :(得分:0)

我知道AForge DONT有Concave Hull。 如果你想计算它,你需要使用EmguCV。

答案 1 :(得分:0)

我也在寻找一个简单的.NET实现来创建alpha形状,但找不到。所以我做了我自己的。至关重要的见解由苏黎世联邦理工学院的Kaspar Fischer在this文档中提供。

这个想法只是简单地用半径为alpha的圆形勺子吃掉了一个有限点的周围空间,而没有实际击中这些点。这是Kaspar论文的图片:

enter image description here

现在,每个在其边界上仅包含两个点但内部不包含任何点的圆都被称为暴露在外的(AEC),正是这些AEC赋予了您最终的alpha形状-只是用边替换定义AEC的两个点。

注意:如果您的Alpha形状看起来太像凸包,请将Alpha缩小。另一方面,如果您的Alpha形状不完整或有太多孔,请将Alpha增大。

这是最低要求的代码(它以O( n ³)运行,其中 n 包含点数):

public class Edge
{
    public PointF A { get; set; }
    public PointF B { get; set; }
}

public class AlphaShape
{
    public List<Edge> BorderEdges { get; private set; }

    public AlphaShape(List<PointF> points, float alpha)
    {
        // 0. error checking, init
        if (points == null || points.Count < 2) { throw new ArgumentException("AlphaShape needs at least 2 points"); }
        BorderEdges = new List<Edge>();           
        var alpha_2 = alpha * alpha;

        // 1. run through all pairs of points
        for (int i = 0; i < points.Count - 1; i++)
        {
            for (int j = i + 1; j < points.Count; j++)
            {
                if (points[i] == points[j]) { throw new ArgumentException("AlphaShape needs pairwise distinct points"); } // alternatively, continue
                var dist = Dist(points[i], points[j]);                    
                if (dist > 2 * alpha) { continue; } // circle fits between points ==> p_i, p_j can't be alpha-exposed                    

                float x1 = points[i].X, x2 = points[j].X, y1 = points[i].Y, y2 = points[j].Y; // for clarity & brevity

                var mid = new PointF((x1 + x2) / 2, (y1 + y2) / 2);

                // find two circles that contain p_i and p_j; note that center1 == center2 if dist == 2*alpha
                var center1 = new PointF(
                    mid.X + (float)Math.Sqrt(alpha_2 - (dist / 2) * (dist / 2)) * (y1 - y2) / dist,
                    mid.Y + (float)Math.Sqrt(alpha_2 - (dist / 2) * (dist / 2)) * (x2 - x1) / dist
                    );

                var center2 = new PointF(
                    mid.X - (float)Math.Sqrt(alpha_2 - (dist / 2) * (dist / 2)) * (y1 - y2) / dist,
                    mid.Y - (float)Math.Sqrt(alpha_2 - (dist / 2) * (dist / 2)) * (x2 - x1) / dist
                    );

                // check if one of the circles is alpha-exposed, i.e. no other point lies in it
                bool c1_empty = true, c2_empty = true;
                for (int k = 0; k < points.Count && (c1_empty || c2_empty); k++)
                {
                    if (points[k] == points[i] || points[k] == points[j]) { continue; }

                    if ((center1.X - points[k].X) * (center1.X - points[k].X) + (center1.Y - points[k].Y) * (center1.Y - points[k].Y) < alpha_2)
                    {
                        c1_empty = false;
                    }

                    if ((center2.X - points[k].X) * (center2.X - points[k].X) + (center2.Y - points[k].Y) * (center2.Y - points[k].Y) < alpha_2)
                    {
                        c2_empty = false;
                    }                                                
                }

                if (c1_empty || c2_empty)
                {                       
                    // yup!
                    BorderEdges.Add(new Edge() { A = points[i], B = points[j] });
                }
            }
        }
    }

    // Euclidian distance between A and B
    public static float Dist(PointF A, PointF B)
    {
        return (float)Math.Sqrt((A.X - B.X) * (A.X - B.X) + (A.Y - B.Y) * (A.Y - B.Y));
    }      
}

并且,作为概念证明,这是实际应用中使用的代码的输出:

enter image description here