From 3a0eb13166a58eb72373a1042d7d4dbfaeaf033f Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Sun, 8 May 2016 16:22:14 -0700 Subject: [PATCH] Refactored some code used in multiple methods into AdjacentPixelMatrix and NeighboringPointFinder --- OSCADSharp/OSCADSharp/OSCADSharp.csproj | 2 + .../Solids/Imported/AdjacentPixelMatrix.cs | 75 +++++++++++ .../Solids/Imported/NeighboringPointFinder.cs | 90 +++++++++++++ .../Imported/PolygonalImageProcessor.cs | 120 +++++++----------- 4 files changed, 216 insertions(+), 71 deletions(-) create mode 100644 OSCADSharp/OSCADSharp/Solids/Imported/AdjacentPixelMatrix.cs create mode 100644 OSCADSharp/OSCADSharp/Solids/Imported/NeighboringPointFinder.cs diff --git a/OSCADSharp/OSCADSharp/OSCADSharp.csproj b/OSCADSharp/OSCADSharp/OSCADSharp.csproj index 41a2b7c..6508a65 100644 --- a/OSCADSharp/OSCADSharp/OSCADSharp.csproj +++ b/OSCADSharp/OSCADSharp/OSCADSharp.csproj @@ -48,11 +48,13 @@ + + diff --git a/OSCADSharp/OSCADSharp/Solids/Imported/AdjacentPixelMatrix.cs b/OSCADSharp/OSCADSharp/Solids/Imported/AdjacentPixelMatrix.cs new file mode 100644 index 0000000..917d9c7 --- /dev/null +++ b/OSCADSharp/OSCADSharp/Solids/Imported/AdjacentPixelMatrix.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OSCADSharp.Solids.Imported +{ + /// + /// A matrix for finding neighbors in regions of pixels + /// + internal class AdjacentPixelMatrix + { + private Point topLeft; + private Point bottomRight; + private KeyValuePair?[,] grid; + + public Point TopLeft { get { return this.topLeft; } } + public Point BottomRight { get { return this.bottomRight; } } + + internal AdjacentPixelMatrix(List> pixelGrouping) + { + this.createGrid(pixelGrouping); + } + + public bool IsOutOfBounds(Point pt) + { + if (pt.X < topLeft.X || pt.X > bottomRight.X || pt.Y < topLeft.Y || pt.Y > bottomRight.Y) + { + return true; + } + + return false; + } + + public KeyValuePair? At(int x, int y) + { + return this.grid[x, y]; + } + + private KeyValuePair?[,] createGrid(List> colorGrouping) + { + this.topLeft = new Point(int.MaxValue, int.MaxValue); + this.bottomRight = new Point(int.MinValue, int.MinValue); + + foreach (var pair in colorGrouping) + { + if (pair.Key.X < topLeft.X) + topLeft.X = pair.Key.X; + + if (pair.Key.Y < topLeft.Y) + topLeft.Y = pair.Key.Y; + + if (pair.Key.X > bottomRight.X) + bottomRight.X = pair.Key.X; + + if (pair.Key.Y > bottomRight.Y) + bottomRight.Y = pair.Key.Y; + } + + int width = bottomRight.X - topLeft.X + 1; + int height = bottomRight.Y - topLeft.Y + 1; + this.grid = new KeyValuePair?[width, height]; + + foreach (var pair in colorGrouping) + { + var pt = pair.Key; + grid[pt.X - topLeft.X, pt.Y - topLeft.Y] = pair; + } + + return grid; + } + } +} diff --git a/OSCADSharp/OSCADSharp/Solids/Imported/NeighboringPointFinder.cs b/OSCADSharp/OSCADSharp/Solids/Imported/NeighboringPointFinder.cs new file mode 100644 index 0000000..aee7f3c --- /dev/null +++ b/OSCADSharp/OSCADSharp/Solids/Imported/NeighboringPointFinder.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OSCADSharp.Solids.Imported +{ + /// + /// Helper class to reuse code in image processing for finding adjacent pixels + /// + internal class NeighboringPointFinder + { + bool cardinalOnly = false; + + internal NeighboringPointFinder(bool cardinalDirectionsOnly = false) + { + this.cardinalOnly = cardinalDirectionsOnly; + } + + internal Point Above(Point origin) + { + return new Point(origin.X, origin.Y + 1); + } + + internal Point Below(Point origin) + { + return new Point(origin.X, origin.Y - 1); + } + + internal Point Left(Point origin) + { + return new Point(origin.X - 1, origin.Y); + } + + internal Point Right(Point origin) + { + return new Point(origin.X + 1, origin.Y); + } + + internal Point UpperLeft(Point origin) + { + return new Point(origin.X - 1, origin.Y + 1); + } + + internal Point UpperRight(Point origin) + { + return new Point(origin.X + 1, origin.Y + 1); + } + + internal Point LowerLeft(Point origin) + { + return new Point(origin.X - 1, origin.Y - 1); + } + + internal Point LowerRight(Point origin) + { + return new Point(origin.X + 1, origin.Y - 1); + } + + internal List GetNeighbors(Point origin) + { + if (this.cardinalOnly) + { + return new List() + { + this.Above(origin), + this.Below(origin), + this.Left(origin), + this.Right(origin) + }; + } + else + { + return new List() + { + this.Above(origin), + this.Below(origin), + this.Left(origin), + this.Right(origin), + this.UpperLeft(origin), + this.UpperRight(origin), + this.LowerLeft(origin), + this.LowerRight(origin) + }; + } + } + } +} diff --git a/OSCADSharp/OSCADSharp/Solids/Imported/PolygonalImageProcessor.cs b/OSCADSharp/OSCADSharp/Solids/Imported/PolygonalImageProcessor.cs index ec41126..485d826 100644 --- a/OSCADSharp/OSCADSharp/Solids/Imported/PolygonalImageProcessor.cs +++ b/OSCADSharp/OSCADSharp/Solids/Imported/PolygonalImageProcessor.cs @@ -146,6 +146,7 @@ namespace OSCADSharp.Solids.Imported //TODO: Reorder sections for correct polygon winding //var color = section[0].Value; + //var orderedSection = this.getOrderedPoints(section); //OSCADObject pgon = new Polygon(section.Select(sec => sec.Key).ToList()); //pgon = pgon.Color(String.Format("[{0}, {1}, {2}]", color.R == 0 ? 0 : color.R / 255, color.G == 0 ? 0 : color.G / 255, color.B == 0 ? 0 : color.B / 255), color.A); //objects.Add(pgon); @@ -165,30 +166,54 @@ namespace OSCADSharp.Solids.Imported return objects; } + private List getOrderedPoints(List> section) + { + List orderedPoints = new List(section.Count); + var grid = new AdjacentPixelMatrix(section); + var traversed = new HashSet(); + var neighborFinder = new NeighboringPointFinder(true); + Stack toTraverse = new Stack(); + toTraverse.Push(section[0].Key); + + while(toTraverse.Count > 0) + { + var origin = toTraverse.Pop(); + traversed.Add(origin); + orderedPoints.Add(origin); + + var below = neighborFinder.Below(origin); + if (!grid.IsOutOfBounds(below) && grid.At(below.X, below.Y) != null) + { + continue; + } + } + + return orderedPoints; + } + private List>> getContiguousSections(List> colorGrouping) { - Point topLeft; - Point bottomRight; - KeyValuePair?[,] grid = createGrid(colorGrouping, out topLeft, out bottomRight); + var grid = new AdjacentPixelMatrix(colorGrouping); var sections = new List>>(); while (colorGrouping.Count > 0) { var origin = colorGrouping[0]; colorGrouping.RemoveAt(0); - sections.Add(this.getConnectedPixelsOfSameColor(origin, grid, colorGrouping, topLeft, bottomRight)); + sections.Add(this.getConnectedPixelsOfSameColor(origin, grid, colorGrouping)); } foreach (var section in sections) { - this.removeCenterPixels(section, grid, topLeft, bottomRight); + this.removeCenterPixels(section, grid); } return sections; } - private void removeCenterPixels(List> section, KeyValuePair?[,] grid, Point topLeft, Point bottomRight) + private void removeCenterPixels(List> section, AdjacentPixelMatrix grid) { + var neighborFinder = new NeighboringPointFinder(true); for (int i = section.Count - 1; i >= 0; i--) { @@ -197,35 +222,30 @@ namespace OSCADSharp.Solids.Imported // We only care about cardinal directions for the purpose of removing pixels // that lie in the center of a grouping (to avoid redundant vertexes - List neighboringPoints = new List() { - new Point(origin.X, origin.Y + 1), //Above - new Point(origin.X, origin.Y - 1), //Below - new Point(origin.X - 1, origin.Y), //Left - new Point(origin.X + 1, origin.Y), //Right - }; + List neighboringPoints = neighborFinder.GetNeighbors(origin); bool isOnanEdge = false; foreach (var pt in neighboringPoints) { //If out of bounds, we found an edge - if (pt.X < topLeft.X || pt.X > bottomRight.X || pt.Y < topLeft.Y || pt.Y > bottomRight.Y) + if (grid.IsOutOfBounds(pt)) { isOnanEdge = true; break; } - int x = pt.X - topLeft.X; - int y = pt.Y - topLeft.Y; + int x = pt.X - grid.TopLeft.X; + int y = pt.Y - grid.TopLeft.Y; - if(grid[x, y] == null) + if(grid.At(x, y) == null) { isOnanEdge = true; break; } - if (grid[x, y] != null) + if (grid.At(x, y) != null) { - var nbr = (KeyValuePair)grid[x, y]; + var nbr = (KeyValuePair)grid.At(x, y); if(!nbr.Value.Equals(color)) { isOnanEdge = true; @@ -238,14 +258,14 @@ namespace OSCADSharp.Solids.Imported { section.RemoveAt(i); } - } } - private List> getConnectedPixelsOfSameColor(KeyValuePair origin, KeyValuePair?[,] grid, - List> colorGrouping, Point topLeft, Point bottomRight) + private List> getConnectedPixelsOfSameColor(KeyValuePair origin, AdjacentPixelMatrix grid, List> colorGrouping) { + var neighborFinder = new NeighboringPointFinder(); + List> neighbors = new List>(); HashSet> traversed = new HashSet>(); Queue> nextOrigins = new Queue>(); @@ -255,33 +275,23 @@ namespace OSCADSharp.Solids.Imported while (nextOrigins.Count > 0) { - origin = nextOrigins.Dequeue(); - - List neighboringPoints = new List() { - new Point(origin.Key.X, origin.Key.Y + 1), //Above - new Point(origin.Key.X, origin.Key.Y - 1), //Below - new Point(origin.Key.X - 1, origin.Key.Y), //Left - new Point(origin.Key.X + 1, origin.Key.Y), //Right - new Point(origin.Key.X - 1, origin.Key.Y+1), //UpperLeft - new Point(origin.Key.X + 1, origin.Key.Y + 1), //UpperRight - new Point(origin.Key.X - 1, origin.Key.Y - 1), //LowerLeft - new Point(origin.Key.X + 1, origin.Key.Y - 1), //LowerRight - }; + origin = nextOrigins.Dequeue(); + List neighboringPoints = neighborFinder.GetNeighbors(origin.Key); foreach (var pt in neighboringPoints) { //Ignore if out of bounds - if(pt.X < topLeft.X || pt.X > bottomRight.X || pt.Y < topLeft.Y || pt.Y > bottomRight.Y) + if(grid.IsOutOfBounds(pt)) { continue; } - int x = pt.X - topLeft.X; - int y = pt.Y - topLeft.Y; + int x = pt.X - grid.TopLeft.X; + int y = pt.Y - grid.TopLeft.Y; - if(grid[x, y] != null) + if(grid.At(x, y) != null) { - var nbr = (KeyValuePair)grid[x, y]; + var nbr = (KeyValuePair)grid.At(x, y); if (!traversed.Contains(nbr) && nbr.Value.Equals(origin.Value)) { nextOrigins.Enqueue(nbr); @@ -297,39 +307,7 @@ namespace OSCADSharp.Solids.Imported return neighbors; } - private static KeyValuePair?[,] createGrid(List> colorGrouping, out Point topLeft, out Point bottomRight) - { - topLeft = new Point(int.MaxValue, int.MaxValue); - bottomRight = new Point(int.MinValue, int.MinValue); - - - foreach (var pair in colorGrouping) - { - if (pair.Key.X < topLeft.X) - topLeft.X = pair.Key.X; - - if (pair.Key.Y < topLeft.Y) - topLeft.Y = pair.Key.Y; - - if (pair.Key.X > bottomRight.X) - bottomRight.X = pair.Key.X; - - if (pair.Key.Y > bottomRight.Y) - bottomRight.Y = pair.Key.Y; - } - - int width = bottomRight.X - topLeft.X + 1; - int height = bottomRight.Y - topLeft.Y + 1; - var grid = new KeyValuePair?[width, height]; - - foreach (var pair in colorGrouping) - { - var pt = pair.Key; - grid[pt.X - topLeft.X, pt.Y - topLeft.Y] = pair; - } - - return grid; - } + private Dictionary>> separateColors(Bitmap img) {