HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Creating and combining rectangles based on a result of an edge detection

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
resultcombiningrectanglescreatingedgebaseddetectionand

Problem

Context

In an imageprocessing project of mine, I needed to do some edge detection and implemented the Theo Pavlidis algorithm which works quite well. A problem had been to get the next "good" position. Assuming an image like this (where the 1 represent the needed pixel value)

011110
111111
111111
011110

this would result in the following edges found

011110
100001 1111
100001 1111
011110

because the algorithm is searching for values which hadn't been visited and have the desired value 1 in it.

So I was in the need to find the enclosed area to exclude it from the algorithms position search area.

The algorithm returns a IEnumerable for each found value on the edge, where the Position struct looks like so

public struct Position
{
    public int X;
    public int Y;

    public Position(int x, int y)
    {
        X = x;
        Y = y;
    }

    private static readonly Position empty = new Position(-1, -1);
    public static Position Empty
    {
        get
        {
            return empty;
        }
    }

    public static bool  operator ==(Position p1, Position p2){

        return (p1.X == p2.X) && (p1.Y == p2.Y);
    }

    public static bool operator !=(Position p1, Position p2)
    {
        return !(p1.X == p2.X) && (p1.Y == p2.Y);
    }

}


So, nothing fancy at all.

My idea was to first create an IEnumerable out of the IEnumerable using the Min of Position.X of all positions with the same value of Position.Y to create the rectangles line by line form top to bottom.

The meat

The creation of the IEnumerable is done like so

```
private IEnumerable ToRectangles(IEnumerable positions)
{
foreach (Position position in positions.Distinct(new PositionComparerY()).OrderBy(p => p.Y))
{
IEnumerable positionsWithSameY = positions.Where(p => p.Y == position.Y);
int minX = positionsWithSameY.Min(x => x.X);
int maxX = positionsWithSameY.Max(x => x.X);

Solution

Life could be so easy if everything would work like expected. The shown code is good and work successful for the given array, but fails for e.g

000011110 without the 0 1111
001001000 1 1
011110000 1111
011110000 1111


So we still need to find the enclosed area to exclude it from the algorithm.

For this problem a IMHO good approach is to use the PathGeometry class which has the FillContains() method to examine if a given point is inside the "path".

So I have created one extension methods like so

public static bool Contains(this IEnumerable geometries, Position position)
{
    Point point = new Point(position.X, position.Y);
    return geometries.Any(g=> g.FillContains(point));
}


to check if a given Position is contained in an IEnumerable.

The ToPathGeometry() extension method allows me to create a PathGeometry object by providing a starting position and the found positions like so

public static PathGeometry ToPathGeometry(this Position startPosition, IEnumerable foundPositions)
{

    IList segements = new List();
    foreach (Position p in foundPositions.Where(p => p != startPosition))
    {
        segements.Add(new LineSegment(new Point(p.X, p.Y), true));
    }

    PathFigure figure = new PathFigure(new Point(startPosition.X, startPosition.Y), segements, true);
    IList figures = new List();
    figures.Add(figure);

    return new PathGeometry(figures);

}


Which leads me also to the conclusion that it would be better to skip the Position class and instead use the Point class.

Update

Profiling my application resulted that the shown extension method

public static bool Contains(this IEnumerable geometries, Position position)
{
    Point point = new Point(position.X, position.Y);
    return geometries.Any(g=> g.FillContains(point));
}


is a bottleneck. So I changed it to

public static bool Contains(this IEnumerable geometries, Position position)
{
    Point point = new Point(position.X, position.Y);
    return geometries.Any(g=> g.Bounds.Contains(point));
}


which reduced the execution time to 1/15 .

Code Snippets

public static bool Contains(this IEnumerable<PathGeometry> geometries, Position position)
{
    Point point = new Point(position.X, position.Y);
    return geometries.Any(g=> g.FillContains(point));
}
public static PathGeometry ToPathGeometry(this Position startPosition, IEnumerable<Position> foundPositions)
{

    IList<LineSegment> segements = new List<LineSegment>();
    foreach (Position p in foundPositions.Where(p => p != startPosition))
    {
        segements.Add(new LineSegment(new Point(p.X, p.Y), true));
    }

    PathFigure figure = new PathFigure(new Point(startPosition.X, startPosition.Y), segements, true);
    IList<PathFigure> figures = new List<PathFigure>();
    figures.Add(figure);

    return new PathGeometry(figures);

}
public static bool Contains(this IEnumerable<PathGeometry> geometries, Position position)
{
    Point point = new Point(position.X, position.Y);
    return geometries.Any(g=> g.FillContains(point));
}
public static bool Contains(this IEnumerable<PathGeometry> geometries, Position position)
{
    Point point = new Point(position.X, position.Y);
    return geometries.Any(g=> g.Bounds.Contains(point));
}

Context

StackExchange Code Review Q#93396, answer score: 3

Revisions (0)

No revisions yet.