如何分割对象c#

时间:2014-11-13 07:51:20

标签: c# image-segmentation edge-detection bounding-box

我使用此代码进行分割,我试图逐个检测像素,因为我的对象是二进制,而不是灰度。当我运行程序时,它绘制了2个对象。第一个对象被成功绘制(对象仍然有黑色和红色矩形),但第二个对象失败被绘制。屏幕截图为here。请帮帮我,为什么会这样?

#region Edge Detection

private void btnSegmentasi_Click(object sender, EventArgs e)
{
    Segments = new List<ImageSegment>();
    Bitmap bmp = (Bitmap)pb2.Image;
    imageArea = new Rectangle(0, 0, pb2.Image.Width - 1, pb2.Image.Height - 1);
    for (int y = 0; y < pb2.Image.Height; y++)
    {
        for (int x = 0; x < pb2.Image.Width; x++)
        {
            bool skip = false;
            foreach (ImageSegment segment in Segments)
            {
                if (pointIsInRect(x, y, segment.Rect))
                {
                    skip = true;
                    break;
                }
            }
            if (skip) continue;
            Color warna = bmp.GetPixel(x, y);
            if (warna.G == 0)
                startEdgeDetection(x, y, ref bmp);
        }
    } 
    DGVProses.DataSource = Segments;

    if (Segments.Count > 0)
    {
        Graphics g = pb2.CreateGraphics();
        Rectangle[] rects = (from theSegment in Segments select theSegment.Rect).ToArray();
        g.DrawRectangles(new Pen(Brushes.Red), rects);
        g.Dispose();
    }
}

private void startEdgeDetection(int x, int y, ref Bitmap bmp)
{
    Point startPoint = new Point(x, y);
    Point currPoint = new Point(x, y);

    int sudut = 180;
    int xMin = x, yMin = y, xMax = x, yMax = y;
    do
    {
        sudut -= 45;
        Point offset = angleToPoint(ref sudut);
        Point trialPoint = new Point(currPoint.X + offset.X, currPoint.Y + offset.Y);
        if (!pointIsInRect(trialPoint.X, trialPoint.Y, imageArea))
            continue;
        Color theColor = bmp.GetPixel(trialPoint.X, trialPoint.Y);
        if (theColor.G == 0)
        {
            currPoint = trialPoint;
            sudut -= 180;
            if (currPoint.X > xMax)
                xMax = currPoint.X;
            else if (currPoint.X < xMin)
                xMin = currPoint.X;
            if (currPoint.Y > yMax)
                yMax = currPoint.Y;
            else if (currPoint.Y < yMin)
                yMin = currPoint.Y;
            if (sudut < 0)
                sudut += 360;
            if (currPoint == startPoint && sudut == 180)
                break;
        }
    }
    while (!(currPoint == startPoint && sudut == 180));
    Rectangle r = new Rectangle(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1);
    Bitmap newImage = new Bitmap(r.Width + 2, r.Height + 2);
    using (Graphics g = Graphics.FromImage(newImage))
    {
        g.FillRectangle(Brushes.White, 0, 0, newImage.Width, newImage.Height);
        g.DrawImage(bmp, new Rectangle(1, 1, r.Width, r.Height), r, GraphicsUnit.Pixel);
        g.Dispose();
    }
    Segments.Add(new ImageSegment(r, newImage));
}

private Point angleToPoint(ref int sudut)
{
    if (sudut < 0)
        sudut += 360;
    switch (sudut)
    {
        case 135: return new Point(-1, -1);
        case 90: return new Point(0, -1);
        case 45: return new Point(1, -1);
        case 0: return new Point(1, 0);
        case 315: return new Point(1, 1);
        case 270: return new Point(0, 1);
        case 225: return new Point(-1, 1);
        default: return new Point(-1, 0);
    }
}

private bool pointIsInRect(int x, int y, Rectangle rect)
{
    if (x < rect.X)
        return false;
    if (x > rect.X + rect.Width)
        return false;
    if (x < rect.Y)
        return false;
    if (x > rect.Y + rect.Height)
        return false;
    return true;

}
#endregion

2 个答案:

答案 0 :(得分:1)

好的,我想我现在已经知道你的算法应该如何工作了。我猜你在对象内绕圈跑来跑去。我真的不知道为什么它不会发生在第一个对象上,但这是另一个故事。

当您输入startEdgeDetection时,您会从某个点开始,检查它是否为黑色,移动一个角度并重复整个过程。当前点到达起点时停止。关键是,这个算法不能保证走完整个对象,但可能只是做了以下(我不知道它是完全像这样,但很多):

OOOOOO
O####O
O####O
OOOOOO

OOOOOO
O*###O
O####O
OOOOOO

OOOOOO
O**##O
O####O
OOOOOO

OOOOOO
O**##O
O#*##O
OOOOOO

OOOOOO
O**##O
O**##O
OOOOOO

O = pixels filled with white
# = pixels filled with black
* = pixels you stepped through

您已经再次到达起点并且算法停止,但是边界框不包含整个对象,而只包含一部分。如果所有对象边界框的宽度或高度均为1,则使用边界框填充整个对象,因此它显示为红色。

您必须修复startEdgeDetection以避免描述的情况并确保您确实检测到边缘。

答案 1 :(得分:0)

我编写了一个简单的类来查找对象的边界框。应该很容易将它应用到你的问题中。

public class BoundingBoxCalculator
{
    Bitmap bitmapToCalculateBoundingBoxFor;
    Point startingPoint;
    Point[] neighborOffsets = 
    {new Point(-1,-1),
    new Point(0,-1),
    new Point(1,-1),
    new Point(-1, 0),
    new Point(1, 0),
    new Point(-1,1), 
    new Point(0,1),
    new Point(1,1)};

    public BoundingBoxCalculator(Bitmap bitmapContainingObject, Point borderPoint)
    {
        this.bitmapToCalculateBoundingBoxFor = bitmapContainingObject;
        this.startingPoint = borderPoint;
    }

    public Rectangle CalculateBoundingBox()
    {
        List<Point> edgePoints = CalculateEdge();

        int minX = edgePoints.Min(p => p.X);
        int maxX = edgePoints.Max(p => p.X);
        int minY = edgePoints.Min(p => p.Y);
        int maxY = edgePoints.Max(p => p.Y);

        return new Rectangle(minX, minY, maxX - minX, maxY - minY);
    }

    List<Point> CalculateEdge()
    {
        List<Point> edgePoints = new List<Point>();
        Point currentPoint = startingPoint;


        do
        {
            IEnumerable<Point> neighboringEdgePoints = GetNeighboringEdgePoints(currentPoint);
            IEnumerable<Point> neighboringEdgePointsNotVisited = from p in neighboringEdgePoints where !edgePoints.Contains(p) select p;

            edgePoints.Add(currentPoint);

            if(neighboringEdgePointsNotVisited.Count() == 0
               && neighboringEdgePoints.Contains(startingPoint))
            {
                currentPoint = startingPoint;
            }
            else if(neighboringEdgePointsNotVisited.Count() == 1)
            {
                Point nextPoint = neighboringEdgePointsNotVisited.First();
                currentPoint = nextPoint;
            }
            else if(neighboringEdgePointsNotVisited.Count() > 1)
            {
                Point nextPoint = GetPointWithMinDistance(currentPoint, neighboringEdgePointsNotVisited);
                currentPoint = nextPoint;
            }
            else
            {
                throw new Exception();
            }


        } while(currentPoint != startingPoint);

        return edgePoints;
    }

    Point GetPointWithMinDistance(Point origin, IEnumerable<Point> pointsToTest)
    {
        double minDistance = double.MaxValue;
        Point pointWithMinDistance = new Point(0,0);

        foreach(Point pointToTest in pointsToTest)
        {
            double currentDistance = GetPointsDistance(origin, pointToTest);

            if(currentDistance < minDistance)
            {
                minDistance = currentDistance;
                pointWithMinDistance = pointToTest;
            }
        }

        return pointWithMinDistance;
    }

    double GetPointsDistance(Point p1, Point p2)
    {
        return Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
    }

    IEnumerable<Point> GetNeighboringEdgePoints(Point currentPoint)
    {
        IEnumerable<Point> neighboringPoints = GetNeighboringPoints(currentPoint);
        List<Point> neighboringEdgePoints = new List<Point>();

        foreach(Point pointToTest in neighboringPoints)
        {
            if(GetNeighboringPoints(pointToTest).Count() < 8)
            {
                neighboringEdgePoints.Add(pointToTest);
            }
        }

        return neighboringEdgePoints;
    }

    IEnumerable<Point> GetNeighboringPoints(Point currentPoint)
    {
        List<Point> neighbors = new List<Point>();

        for(int offsetsCount = 0; offsetsCount < neighborOffsets.Length; offsetsCount++)
        {
            Point currentPointWithOffset = AddPointOffset(currentPoint, neighborOffsets[offsetsCount]);
            if(IsInImage(currentPointWithOffset) &&
              IsInObject(currentPointWithOffset))
            {
                neighbors.Add(currentPointWithOffset);
            }
        }

        return neighbors;
    }

    bool IsInImage(Point pointToTest)
    {
        return pointToTest.X >= 0
            && pointToTest.X < bitmapToCalculateBoundingBoxFor.Width
            && pointToTest.Y >= 0
            && pointToTest.Y < bitmapToCalculateBoundingBoxFor.Height;
    }

    bool IsInObject(Point pointToTest)
    {
        Color colorInPointPosition = bitmapToCalculateBoundingBoxFor.GetPixel(pointToTest.X, pointToTest.Y);

        //assume object is color is not white
        return colorInPointPosition.R != 255
            || colorInPointPosition.G != 255
            || colorInPointPosition.B != 255;
    }

    Point AddPointOffset(Point point, Point offset)
    {
        return new Point(point.X + offset.X, point.Y + offset.Y);
    }
}

在以下位置找到示例: https://dotnetfiddle.net/49bnTV

我刚刚测试了一个矩形,但我猜它应该适用于任何形状。试试吧。