diff --git a/OSCADSharp/OSCADSharp/Solids/Imported/ImageImportOptions.cs b/OSCADSharp/OSCADSharp/Solids/Imported/ImageImportOptions.cs index 97a8809..83f5357 100644 --- a/OSCADSharp/OSCADSharp/Solids/Imported/ImageImportOptions.cs +++ b/OSCADSharp/OSCADSharp/Solids/Imported/ImageImportOptions.cs @@ -20,5 +20,10 @@ namespace OSCADSharp.Solids.Imported /// Converts the colors in the image to black and white /// public bool UseGrayScale { get; set; } = false; + + /// + /// Reduces the total number of colors in the image by merging similar colors together. + /// + public byte SimplificationAmount { get; set; } = 0; } } diff --git a/OSCADSharp/OSCADSharp/Solids/Imported/ImportedImage.cs b/OSCADSharp/OSCADSharp/Solids/Imported/ImportedImage.cs index 8302590..d13a0d0 100644 --- a/OSCADSharp/OSCADSharp/Solids/Imported/ImportedImage.cs +++ b/OSCADSharp/OSCADSharp/Solids/Imported/ImportedImage.cs @@ -35,7 +35,7 @@ namespace OSCADSharp.Solids.Imported options = new ImageImportOptions(); } - processor = new CubistImageProcessor(imagePath, options.HeightMapping, options.UseGrayScale); + processor = new CubistImageProcessor(imagePath, options.HeightMapping, options.UseGrayScale, options.SimplificationAmount); var obj = processor.ProcessImage(); var img = new ImportedImage() diff --git a/OSCADSharp/OSCADSharp/Utility/Images/CubistImageProcessor.cs b/OSCADSharp/OSCADSharp/Utility/Images/CubistImageProcessor.cs index dd2e968..f7c7977 100644 --- a/OSCADSharp/OSCADSharp/Utility/Images/CubistImageProcessor.cs +++ b/OSCADSharp/OSCADSharp/Utility/Images/CubistImageProcessor.cs @@ -22,17 +22,19 @@ namespace OSCADSharp.Utility.Images List cubes = new List(); private Color[,] pixels; private bool useGrayScale; + private byte simplificationAmount; #endregion #region Internal Fields public Bounds ImageBounds { get; set; } #endregion - internal CubistImageProcessor(string imagePath, bool includeHeight = true, bool useGrayScale = false) + internal CubistImageProcessor(string imagePath, bool includeHeight = true, bool useGrayScale = false, byte simplificationAmount = 0) { this.includeHeight = includeHeight; this.imagePath = imagePath; this.useGrayScale = useGrayScale; + this.simplificationAmount = simplificationAmount; } public OSCADObject ProcessImage() @@ -47,6 +49,7 @@ namespace OSCADSharp.Utility.Images { Bitmap img = new Bitmap(Image.FromFile(this.imagePath)); this.setColorArray(img); + this.simlifyColors(img); this.setHeightMappings(img); this.ImageBounds = new Bounds(new Vector3(), new Vector3(img.Width, img.Height, 1)); @@ -82,6 +85,56 @@ namespace OSCADSharp.Utility.Images return cubes; } + private void simlifyColors(Bitmap img) + { + if (this.simplificationAmount == 0) + { + return; + } + + // These calls color values from their originals to new ones based upon the color's proximity to the simpification amount + // the channelSelector and colorFactory functions allow us to use the same function for each. + // TODO: Refactor into a class + mapColors(img, (c) => { return c.R; }, (orig, retained) => { return Color.FromArgb(orig.A, retained.R, orig.G, orig.B); }); + mapColors(img, (c) => { return c.G; }, (orig, retained) => { return Color.FromArgb(orig.A, orig.R, retained.G, orig.B); }); + mapColors(img, (c) => { return c.B; }, (orig, retained) => { return Color.FromArgb(orig.A, orig.R, orig.G, retained.B); }); + } + + private void mapColors(Bitmap img, Func channelSelector, Func colorFactory) + { + Dictionary colorsRetained = new Dictionary(); + + for (int x = 0; x < img.Width; x++) + { + for (int y = 0; y < img.Height; y++) + { + Color curColor; + bool colorMatched = false; + curColor = this.pixels[x, y]; + for (int simVal = 0; simVal < this.simplificationAmount; simVal++) + { + int upperLimit = channelSelector(curColor) + simVal; + int lowerLimit = channelSelector(curColor) - simVal; + if (upperLimit <= 255 && colorsRetained.ContainsKey((byte)upperLimit)) + { + this.pixels[x, y] = colorFactory(curColor, colorsRetained[(byte)upperLimit]); + colorMatched = true; + } + else if (lowerLimit >= 0 && colorsRetained.ContainsKey((byte)lowerLimit)) + { + this.pixels[x, y] = colorFactory(curColor, colorsRetained[(byte)lowerLimit]); + colorMatched = true; + } + } + + if (!colorMatched) + { + colorsRetained[channelSelector(curColor)] = curColor; + } + } + } + } + private void setColorArray(Bitmap img) { this.pixels = new Color[img.Width, img.Height];