mirror of
https://github.com/eliasstepanik/OSCADSharpDotnet7.git
synced 2026-01-24 19:48:28 +00:00
Extracted cube centric image processing code to CubistImageProcessor.cs
This commit is contained in:
parent
825791e0fa
commit
79268ef334
@ -57,7 +57,7 @@ namespace OSCADSharp.ConsoleTests
|
|||||||
|
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
var img = new ImportedImage("seahawks coaster.png");
|
var img = ImportedImage.FromFile("seahawks coaster.png");
|
||||||
img.ToFile("seaImg").Open();
|
img.ToFile("seaImg").Open();
|
||||||
|
|
||||||
//makePeg();
|
//makePeg();
|
||||||
|
|||||||
@ -48,6 +48,7 @@
|
|||||||
<Compile Include="OSCADObject.BaseTransform.cs" />
|
<Compile Include="OSCADObject.BaseTransform.cs" />
|
||||||
<Compile Include="OSCADObject.BasicTransforms.cs" />
|
<Compile Include="OSCADObject.BasicTransforms.cs" />
|
||||||
<Compile Include="OSCADObject.Booleans.cs" />
|
<Compile Include="OSCADObject.Booleans.cs" />
|
||||||
|
<Compile Include="Solids\Imported\CubistImageProcessor.cs" />
|
||||||
<Compile Include="Solids\Imported\ImportedImage.cs" />
|
<Compile Include="Solids\Imported\ImportedImage.cs" />
|
||||||
<Compile Include="Solids\Imported\ImportedModel.cs" />
|
<Compile Include="Solids\Imported\ImportedModel.cs" />
|
||||||
<Compile Include="Utility\Dependencies.cs" />
|
<Compile Include="Utility\Dependencies.cs" />
|
||||||
|
|||||||
178
OSCADSharp/OSCADSharp/Solids/Imported/CubistImageProcessor.cs
Normal file
178
OSCADSharp/OSCADSharp/Solids/Imported/CubistImageProcessor.cs
Normal file
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Processes a bitmap image by treating contiguous same-color regions as cubes
|
||||||
|
/// </summary>
|
||||||
|
internal class CubistImageProcessor
|
||||||
|
{
|
||||||
|
#region Private Fields
|
||||||
|
private string imagePath;
|
||||||
|
List<OSCADObject> cubes = new List<OSCADObject>();
|
||||||
|
#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<OSCADObject> 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<OSCADObject> cubes = new List<OSCADObject>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,183 +14,73 @@ namespace OSCADSharp.Solids.Imported
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ImportedImage : OSCADObject
|
public class ImportedImage : OSCADObject
|
||||||
{
|
{
|
||||||
#region Private Fields
|
#region Internal Properties
|
||||||
private string imagePath;
|
internal OSCADObject m_Object { get; set; }
|
||||||
|
internal Bounds m_Bounds { get; set; }
|
||||||
List<OSCADObject> cubes;
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructors / Initialization
|
#region Constructors / Initialization
|
||||||
/// <summary>
|
public static ImportedImage FromFile(string imagePath)
|
||||||
/// Creates an imported image with the default settings
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="imagePath"></param>
|
|
||||||
public ImportedImage(string imagePath)
|
|
||||||
{
|
{
|
||||||
this.imagePath = imagePath;
|
|
||||||
|
|
||||||
this.cubes = this.processImage();
|
var processor = new CubistImageProcessor(imagePath);
|
||||||
}
|
var obj = processor.ProcessImage();
|
||||||
|
|
||||||
private List<OSCADObject> processImage()
|
var img = new ImportedImage() {
|
||||||
{
|
m_Object = obj,
|
||||||
Bitmap img = new Bitmap(Image.FromFile(this.imagePath));
|
m_Bounds = processor.ImageBounds
|
||||||
if(img.Width > 200 || img.Height > 200)
|
};
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Cannot process images larger greater than 200x200 pixels");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<OSCADObject> cubes = new List<OSCADObject>();
|
return img;
|
||||||
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
|
#endregion
|
||||||
|
|
||||||
#region OSCADObject Overrides
|
#region OSCADObject Overrides
|
||||||
|
/// <summary>
|
||||||
|
/// Imported images have no bindable properties
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="property"></param>
|
||||||
|
/// <param name="variable"></param>
|
||||||
public override void Bind(string property, Variable variable)
|
public override void Bind(string property, Variable variable)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotSupportedException("Imported images have no bindable properties");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the approximate boundaries of this OpenSCAD object
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public override Bounds Bounds()
|
public override Bounds Bounds()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return m_Bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a copy of this object that is a new instance
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public override OSCADObject Clone()
|
public override OSCADObject Clone()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return this.m_Object.Clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the position of this object's center (origin) in
|
||||||
|
/// world space
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public override Vector3 Position()
|
public override Vector3 Position()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return Vector3.Average(this.m_Bounds.BottomLeft, this.m_Bounds.TopRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts this object to an OpenSCAD script
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Script for this object</returns>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
var obj = new MultiStatementObject("union()", cubes);
|
return this.m_Object.ToString();
|
||||||
return obj.ToString();
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user