From 79268ef334d833dff1f7107aa8f67977d183f844 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Tue, 3 May 2016 19:16:32 -0700 Subject: [PATCH] Extracted cube centric image processing code to CubistImageProcessor.cs --- OSCADSharp/OSCADSharp.ConsoleTests/Program.cs | 2 +- OSCADSharp/OSCADSharp/OSCADSharp.csproj | 1 + .../Solids/Imported/CubistImageProcessor.cs | 178 ++++++++++++++++ .../Solids/Imported/ImportedImage.cs | 192 ++++-------------- 4 files changed, 221 insertions(+), 152 deletions(-) create mode 100644 OSCADSharp/OSCADSharp/Solids/Imported/CubistImageProcessor.cs diff --git a/OSCADSharp/OSCADSharp.ConsoleTests/Program.cs b/OSCADSharp/OSCADSharp.ConsoleTests/Program.cs index 9eaaa50..92b3d14 100644 --- a/OSCADSharp/OSCADSharp.ConsoleTests/Program.cs +++ b/OSCADSharp/OSCADSharp.ConsoleTests/Program.cs @@ -57,7 +57,7 @@ namespace OSCADSharp.ConsoleTests static void Main(string[] args) { - var img = new ImportedImage("seahawks coaster.png"); + var img = ImportedImage.FromFile("seahawks coaster.png"); img.ToFile("seaImg").Open(); //makePeg(); diff --git a/OSCADSharp/OSCADSharp/OSCADSharp.csproj b/OSCADSharp/OSCADSharp/OSCADSharp.csproj index 35f5d32..8e45585 100644 --- a/OSCADSharp/OSCADSharp/OSCADSharp.csproj +++ b/OSCADSharp/OSCADSharp/OSCADSharp.csproj @@ -48,6 +48,7 @@ + diff --git a/OSCADSharp/OSCADSharp/Solids/Imported/CubistImageProcessor.cs b/OSCADSharp/OSCADSharp/Solids/Imported/CubistImageProcessor.cs new file mode 100644 index 0000000..86ec37d --- /dev/null +++ b/OSCADSharp/OSCADSharp/Solids/Imported/CubistImageProcessor.cs @@ -0,0 +1,178 @@ +using OSCADSharp.Spatial; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OSCADSharp.Solids.Imported +{ + /// + /// Processes a bitmap image by treating contiguous same-color regions as cubes + /// + internal class CubistImageProcessor + { + #region Private Fields + private string imagePath; + List cubes = new List(); + #endregion + + #region Internal Fields + internal Bounds ImageBounds { get; set; } + #endregion + + public CubistImageProcessor(string imagePath) + { + this.imagePath = imagePath; + } + + internal OSCADObject ProcessImage() + { + this.cubes = this.processImage(); + OSCADObject obj = new OSCADObject.MultiStatementObject("union()", cubes); + obj = obj.Rotate(0, 0, 180); + obj = obj.Translate(ImageBounds.Length, ImageBounds.Width, 0); + + return obj; + } + + + private List processImage() + { + Bitmap img = new Bitmap(Image.FromFile(this.imagePath)); + + if (img.Width > 200 || img.Height > 200) + { + throw new InvalidOperationException("Cannot process images larger greater than 200x200 pixels"); + } + + this.ImageBounds = new Bounds(new Vector3(), new Vector3(img.Width, img.Height, 0)); + + List cubes = new List(); + bool[,] visited = new bool[img.Width, img.Height]; + + Point? start = this.getNextPoint(img, ref visited, img.Width - 1, img.Height - 1); + do + { + System.Drawing.Color color = img.GetPixel(((Point)start).X, ((Point)start).Y); + + var cube = this.traverseNext(img, (Point)start, ref visited, color); + if (cube != null) + { + this.markVisited(ref visited, cube, (Point)start, img); + if (color.A != 0) + { + cubes.Add(cube.Translate(((Point)start).X, ((Point)start).Y, 0) + .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)); + } + } + + start = this.getNextPoint(img, ref visited, img.Width - 1, img.Height - 1); + } while (start != null); + + return cubes; + } + + private void markVisited(ref bool[,] visited, Cube cube, Point start, Bitmap img) + { + var bounds = cube.Bounds(); + for (int column = start.X; column < start.X + bounds.Width; column++) + { + for (int row = start.Y; row < start.Y + bounds.Length; row++) + { + visited[column, row] = true; + } + } + } + + private Cube traverseNext(Bitmap img, Point start, ref bool[,] visited, System.Drawing.Color color, Cube cube = null) + { + bool canTraverse = true; + if (cube != null) + { + canContinueTraversal(img, ref start, ref visited, color, cube, ref canTraverse); + } + else + { + canTraverse = pixelCanBeTraversed(img, ref visited, new Point(start.X + 1, start.Y + 1), color) && + pixelCanBeTraversed(img, ref visited, new Point(start.X + 1, start.Y), color) && + pixelCanBeTraversed(img, ref visited, new Point(start.X, start.Y + 1), color); + } + + + if (canTraverse) + { + if (cube == null) + { + cube = new Cube(); + cube.Size.X += 1; + cube.Size.Y += 1; + + return traverseNext(img, start, ref visited, color, cube); + } + else + { + cube.Size.X += 1; + cube.Size.Y += 1; + return traverseNext(img, start, ref visited, color, cube); + } + } + else + { + + if (cube == null) + { + return new Cube(); + } + else + { + return cube; + } + } + + } + + private void canContinueTraversal(Bitmap img, ref Point start, ref bool[,] visited, Color color, Cube cube, ref bool canTraverse) + { + var bounds = cube.Bounds(); + for (int column = start.X; column < start.X + bounds.Width && canTraverse; column++) + { + for (int row = start.Y; row < start.Y + bounds.Length && canTraverse; row++) + { + if (start.X + column >= img.Width || start.Y + row >= img.Height) + { + canTraverse = false; + } + else + { + canTraverse = canTraverse && pixelCanBeTraversed(img, ref visited, new Point(column, row), color); + } + } + } + } + + private bool pixelCanBeTraversed(Bitmap img, ref bool[,] visited, Point pixel, Color colorToMatch) + { + return pixel.X < img.Width && pixel.Y < img.Height && + visited[pixel.X, pixel.Y] == false && img.GetPixel(pixel.X, pixel.Y) == colorToMatch; + + } + + private Point? getNextPoint(Bitmap img, ref bool[,] visited, int width, int height) + { + for (int column = 0; column < width; column++) + { + for (int row = 0; row < height; row++) + { + if (visited[column, row] == false) + { + return new Point(column, row); + } + } + } + + return null; + } + } +} diff --git a/OSCADSharp/OSCADSharp/Solids/Imported/ImportedImage.cs b/OSCADSharp/OSCADSharp/Solids/Imported/ImportedImage.cs index e08471e..ba16bb9 100644 --- a/OSCADSharp/OSCADSharp/Solids/Imported/ImportedImage.cs +++ b/OSCADSharp/OSCADSharp/Solids/Imported/ImportedImage.cs @@ -14,183 +14,73 @@ namespace OSCADSharp.Solids.Imported /// public class ImportedImage : OSCADObject { - #region Private Fields - private string imagePath; - - List cubes; + #region Internal Properties + internal OSCADObject m_Object { get; set; } + internal Bounds m_Bounds { get; set; } #endregion - + #region Constructors / Initialization - /// - /// Creates an imported image with the default settings - /// - /// - public ImportedImage(string imagePath) + public static ImportedImage FromFile(string imagePath) { - this.imagePath = imagePath; - this.cubes = this.processImage(); + var processor = new CubistImageProcessor(imagePath); + var obj = processor.ProcessImage(); + + var img = new ImportedImage() { + m_Object = obj, + m_Bounds = processor.ImageBounds + }; + + return img; } - - private List processImage() - { - Bitmap img = new Bitmap(Image.FromFile(this.imagePath)); - if(img.Width > 200 || img.Height > 200) - { - throw new InvalidOperationException("Cannot process images larger greater than 200x200 pixels"); - } - - List cubes = new List(); - bool[,] visited = new bool[img.Width, img.Height]; - - Point? start = this.getNextPoint(img, ref visited, img.Width-1, img.Height-1); - do - { - System.Drawing.Color color = img.GetPixel(((Point)start).X, ((Point)start).Y); - - var cube = this.traverseNext(img, (Point)start, ref visited, color); - if(cube != null) - { - this.markVisited(ref visited, cube, (Point)start, img); - if(color.A != 0) - { - cubes.Add(cube.Translate(((Point)start).X, ((Point)start).Y, 0) - .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)); - } - } - - start = this.getNextPoint(img, ref visited, img.Width-1, img.Height-1); - } while (start != null); - - return cubes; - } - - private void markVisited(ref bool[,] visited, Cube cube, Point start, Bitmap img) - { - var bounds = cube.Bounds(); - for(int column = start.X; column < start.X + bounds.Width; column++) - { - for (int row = start.Y; row < start.Y + bounds.Length; row++) - { - visited[column, row] = true; - } - } - } - - Cube traverseNext(Bitmap img, Point start, ref bool[,] visited, System.Drawing.Color color, Cube cube = null) - { - bool canTraverse = true; - if(cube != null) - { - canContinueTraversal(img, ref start, ref visited, color, cube, ref canTraverse); - } - else - { - canTraverse = pixelCanBeTraversed(img, ref visited, new Point(start.X + 1, start.Y + 1), color) && - pixelCanBeTraversed(img, ref visited, new Point(start.X + 1, start.Y), color) && - pixelCanBeTraversed(img, ref visited, new Point(start.X, start.Y + 1), color); - } - - - if(canTraverse) - { - if (cube == null) - { - cube = new Cube(); - cube.Size.X += 1; - cube.Size.Y += 1; - - return traverseNext(img, start, ref visited, color, cube); - } - else - { - cube.Size.X += 1; - cube.Size.Y += 1; - return traverseNext(img, start, ref visited, color, cube); - } - } - else - { - - if(cube == null) - { - return new Cube(); - } - else - { - return cube; - } - } - - } - - private void canContinueTraversal(Bitmap img, ref Point start, ref bool[,] visited, Color color, Cube cube, ref bool canTraverse) - { - var bounds = cube.Bounds(); - for (int column = start.X; column < start.X + bounds.Width && canTraverse; column++) - { - for (int row = start.Y; row < start.Y + bounds.Length && canTraverse; row++) - { - if (start.X + column >= img.Width || start.Y + row >= img.Height) - { - canTraverse = false; - } - else - { - canTraverse = canTraverse && pixelCanBeTraversed(img, ref visited, new Point(column, row), color); - } - } - } - } - - bool pixelCanBeTraversed(Bitmap img, ref bool[,] visited, Point pixel, Color colorToMatch) { - return pixel.X < img.Width && pixel.Y < img.Height && - visited[pixel.X, pixel.Y] == false && img.GetPixel(pixel.X, pixel.Y) == colorToMatch; - - } - - Point? getNextPoint(Bitmap img, ref bool[,] visited, int width, int height) - { - for (int column = 0; column < width; column++) - { - for (int row = 0; row < height; row++) - { - if(visited[column, row] == false) - { - return new Point(column, row); - } - } - } - - return null; - } #endregion #region OSCADObject Overrides + /// + /// Imported images have no bindable properties + /// + /// + /// public override void Bind(string property, Variable variable) { - throw new NotImplementedException(); + throw new NotSupportedException("Imported images have no bindable properties"); } + /// + /// Returns the approximate boundaries of this OpenSCAD object + /// + /// public override Bounds Bounds() { - throw new NotImplementedException(); + return m_Bounds; } + /// + /// Gets a copy of this object that is a new instance + /// + /// public override OSCADObject Clone() { - throw new NotImplementedException(); + return this.m_Object.Clone(); } + /// + /// Gets the position of this object's center (origin) in + /// world space + /// + /// public override Vector3 Position() { - throw new NotImplementedException(); + return Vector3.Average(this.m_Bounds.BottomLeft, this.m_Bounds.TopRight); } + /// + /// Converts this object to an OpenSCAD script + /// + /// Script for this object public override string ToString() { - var obj = new MultiStatementObject("union()", cubes); - return obj.ToString(); + return this.m_Object.ToString(); } #endregion }