Merge pull request #3 from clayschaad/develope
export images with lego numbers
This commit is contained in:
commit
5dd28a0f20
8 changed files with 155 additions and 58 deletions
|
|
@ -22,6 +22,10 @@
|
||||||
<TextBox Width="300" x:Name="tbImagePath" />
|
<TextBox Width="300" x:Name="tbImagePath" />
|
||||||
<Button Width="50" Name="btnImageChooser" Content="..." Click="btnImageChooser_Click"/>
|
<Button Width="50" Name="btnImageChooser" Content="..." Click="btnImageChooser_Click"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" Height="25" VerticalAlignment="Top" Margin="5, 0 , 0, 0">
|
||||||
|
<Button Width="200" Name="btnInstructionsPersister" Content="Save Instructions" Click="btnInstructionsPersister_Click"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel Grid.Row="2" Orientation="Vertical" Name="parentStackPanel" VerticalAlignment="Top">
|
<StackPanel Grid.Row="2" Orientation="Vertical" Name="parentStackPanel" VerticalAlignment="Top">
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
using LegoArtTool;
|
using System.Drawing;
|
||||||
|
using LegoArtTool;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Shapes;
|
using Rectangle = System.Windows.Shapes.Rectangle;
|
||||||
|
|
||||||
namespace LegoArt
|
namespace LegoArt
|
||||||
{
|
{
|
||||||
|
|
@ -17,7 +18,7 @@ namespace LegoArt
|
||||||
private readonly LegoArtColorService legoArtColorService;
|
private readonly LegoArtColorService legoArtColorService;
|
||||||
private readonly BitmapHelperService bitmapHelperService;
|
private readonly BitmapHelperService bitmapHelperService;
|
||||||
private readonly ImageHelperService imageHelperService;
|
private readonly ImageHelperService imageHelperService;
|
||||||
private LegoArtImageGenerationService legoArtImageGenerationService;
|
private readonly LegoArtImageGenerationService legoArtImageGenerationService;
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
|
|
@ -27,6 +28,8 @@ namespace LegoArt
|
||||||
bitmapHelperService = new BitmapHelperService();
|
bitmapHelperService = new BitmapHelperService();
|
||||||
imageHelperService = new ImageHelperService();
|
imageHelperService = new ImageHelperService();
|
||||||
legoArtImageGenerationService = new LegoArtImageGenerationService();
|
legoArtImageGenerationService = new LegoArtImageGenerationService();
|
||||||
|
|
||||||
|
btnInstructionsPersister.Visibility = Visibility.Hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnImageChooser_Click(object sender, RoutedEventArgs e)
|
private void btnImageChooser_Click(object sender, RoutedEventArgs e)
|
||||||
|
|
@ -50,6 +53,15 @@ namespace LegoArt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void btnInstructionsPersister_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var d = new DataObject(DataFormats.Bitmap, scaledImage.Source, true);
|
||||||
|
var bitmap = d.GetData(typeof(Bitmap)) as Bitmap;
|
||||||
|
var outputPath = BuildingInstructionService.PersistBuildingInstructions(bitmap, tbImagePath.Text);
|
||||||
|
MessageBox.Show($"Instructions saved to {outputPath}", "Saved", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void LoadImage(string path)
|
private void LoadImage(string path)
|
||||||
{
|
{
|
||||||
var bitmap = new System.Drawing.Bitmap(path);
|
var bitmap = new System.Drawing.Bitmap(path);
|
||||||
|
|
@ -61,9 +73,11 @@ namespace LegoArt
|
||||||
var sourceBitmap = bitmapHelperService.Scale(reducedBitMap, 1);
|
var sourceBitmap = bitmapHelperService.Scale(reducedBitMap, 1);
|
||||||
sourceImage.Source = imageHelperService.LoadToImage(sourceBitmap);
|
sourceImage.Source = imageHelperService.LoadToImage(sourceBitmap);
|
||||||
|
|
||||||
var convertedBitmap = bitmapHelperService.ConvertToPixelMatrix(sourceBitmap, 20);
|
var convertedBitmap = bitmapHelperService.ConvertToPixelMatrix(sourceBitmap, legoArtColorService.GetLegoColors());
|
||||||
scaledImage.Source = imageHelperService.LoadToImage(convertedBitmap);
|
scaledImage.Source = imageHelperService.LoadToImage(convertedBitmap);
|
||||||
|
|
||||||
|
btnInstructionsPersister.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
parentStackPanel.Children.Clear();
|
parentStackPanel.Children.Clear();
|
||||||
|
|
||||||
AddLine(System.Drawing.Color.White, "Have", "Needed", "RGB", "#", "Difference", System.Drawing.Color.White);
|
AddLine(System.Drawing.Color.White, "Have", "Needed", "RGB", "#", "Difference", System.Drawing.Color.White);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using LegoArtTool.Pixels;
|
using LegoArtTool.Pixels;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
|
||||||
namespace LegoArtTool
|
namespace LegoArtTool
|
||||||
|
|
@ -29,9 +30,9 @@ namespace LegoArtTool
|
||||||
return scaledBitmap;
|
return scaledBitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bitmap ConvertToPixelMatrix(Bitmap bitmap, int pixelSize)
|
public Bitmap ConvertToPixelMatrix(Bitmap bitmap, List<LegoArtColorInfo> legoArtColors)
|
||||||
{
|
{
|
||||||
var pixelMatrix = new PixelMatrix(pixelSize, bitmap);
|
var pixelMatrix = new PixelMatrix(bitmap, legoArtColors);
|
||||||
return pixelMatrix.ConvertBitmap();
|
return pixelMatrix.ConvertBitmap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
49
LegoArtTool/BuildingInstructionService.cs
Normal file
49
LegoArtTool/BuildingInstructionService.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace LegoArtTool
|
||||||
|
{
|
||||||
|
public static class BuildingInstructionService
|
||||||
|
{
|
||||||
|
public static string PersistBuildingInstructions(Bitmap bitmap, string path)
|
||||||
|
{
|
||||||
|
var segmentSize = bitmap.Width / 3;
|
||||||
|
var outPutDirectory = CreateOutPutDirectory(path);
|
||||||
|
|
||||||
|
var segmentNumber = 1;
|
||||||
|
for (var h = 0; h < 3; h++)
|
||||||
|
{
|
||||||
|
for (var w = 0; w < 3; w++)
|
||||||
|
{
|
||||||
|
var rectangle = new Rectangle(w * segmentSize, h * segmentSize, segmentSize, segmentSize);
|
||||||
|
var segment = bitmap.Clone(rectangle, bitmap.PixelFormat);
|
||||||
|
segment.Save($"{outPutDirectory}/{segmentNumber++}.png", ImageFormat.Png);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return outPutDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CreateOutPutDirectory(string path)
|
||||||
|
{
|
||||||
|
var increment = 0;
|
||||||
|
|
||||||
|
while (Directory.Exists(GetOutputDirectoryPath(increment, path)))
|
||||||
|
{
|
||||||
|
increment++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var outputDirectoryPath = GetOutputDirectoryPath(increment, path);
|
||||||
|
Directory.CreateDirectory(outputDirectoryPath);
|
||||||
|
return outputDirectoryPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetOutputDirectoryPath(int increment, string path)
|
||||||
|
{
|
||||||
|
var parent = Directory.GetParent(path);
|
||||||
|
var filename = Path.GetFileNameWithoutExtension(path);
|
||||||
|
return increment == 0 ? $"{parent}/legoArtInstructions" : $"{parent}/legoArtInstructions_{filename} ({increment})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -35,19 +35,19 @@ namespace LegoArtTool
|
||||||
var reducedBitmap = new Bitmap(bitmap.Width, bitmap.Height);
|
var reducedBitmap = new Bitmap(bitmap.Width, bitmap.Height);
|
||||||
|
|
||||||
var remainingColors = legoArtColorInfos.ToDictionary(k => k.LegoNumber, v => v.HaveCount);
|
var remainingColors = legoArtColorInfos.ToDictionary(k => k.LegoNumber, v => v.HaveCount);
|
||||||
|
|
||||||
|
|
||||||
while (pixels.Count(p => !p.IsAssigned) > 0)
|
while (pixels.Count(p => !p.IsAssigned) > 0)
|
||||||
{
|
{
|
||||||
var minOffset = pixels.Where(p => !p.IsAssigned).SelectMany(p => p.LegoArtColorOffsets.Where(o => o.IsAvailable)
|
var minOffset = pixels.Where(p => !p.IsAssigned).SelectMany(p => p.LegoArtColorOffsets.Where(o => o.IsAvailable)
|
||||||
.Select(o => o.ColorOffset))
|
.Select(o => o.ColorOffset))
|
||||||
.Min(x => x);
|
.Min(x => x);
|
||||||
|
|
||||||
var pixel = pixels.Where(p => !p.IsAssigned).First(p => p.LegoArtColorOffsets.Any(o => o.ColorOffset == minOffset));
|
var pixel = pixels.Where(p => !p.IsAssigned).First(p => p.LegoArtColorOffsets.Where(o => o.IsAvailable)
|
||||||
|
.Any(o => o.ColorOffset == minOffset));
|
||||||
var legoArtColorInfo = pixel.LegoArtColorOffsets.First(o => o.ColorOffset == minOffset).LegoArtColorInfo;
|
var legoArtColorInfo = pixel.LegoArtColorOffsets.First(o => o.ColorOffset == minOffset).LegoArtColorInfo;
|
||||||
|
|
||||||
reducedBitmap.SetPixel(pixel.X, pixel.Y, legoArtColorInfo.Color);
|
reducedBitmap.SetPixel(pixel.X, pixel.Y, legoArtColorInfo.Color);
|
||||||
|
|
||||||
|
|
||||||
remainingColors[legoArtColorInfo.LegoNumber]--;
|
remainingColors[legoArtColorInfo.LegoNumber]--;
|
||||||
pixel.IsAssigned = true;
|
pixel.IsAssigned = true;
|
||||||
|
|
||||||
|
|
@ -62,50 +62,21 @@ namespace LegoArtTool
|
||||||
return reducedBitmap;
|
return reducedBitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pixel MapPixel(
|
private Pixel MapPixel(Color inputColor, int xPosition, int yPosition)
|
||||||
Color inputColor,
|
|
||||||
int xPosition,
|
|
||||||
int yPosition)
|
|
||||||
{
|
{
|
||||||
var legoArtColorOffsets = legoArtColorInfos.Select(i => ColorOffset(inputColor, i)).ToList();
|
var legoArtColorOffsets = legoArtColorInfos.Select(i => ColorDistance(inputColor, i)).ToList();
|
||||||
return new Pixel(xPosition, yPosition, legoArtColorOffsets);
|
return new Pixel(xPosition, yPosition, legoArtColorOffsets);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LegoArtColorOffset ColourDistance(Color inputColor, LegoArtColorInfo colorInfo)
|
private static LegoArtColorOffset ColorDistance(Color inputColor, LegoArtColorInfo colorInfo)
|
||||||
{
|
{
|
||||||
var targetColor = colorInfo.Color;
|
var targetColor = colorInfo.Color;
|
||||||
var rmean = ((long)inputColor.R + (long)targetColor.R) / 2;
|
var rmean = (inputColor.R + (long)targetColor.R) / 2;
|
||||||
var r = (long)inputColor.R - (long)targetColor.R;
|
var r = inputColor.R - (long)targetColor.R;
|
||||||
var g = (long)inputColor.G - (long)targetColor.G;
|
var g = inputColor.G - (long)targetColor.G;
|
||||||
var b = (long)inputColor.B - (long)targetColor.B;
|
var b = inputColor.B - (long)targetColor.B;
|
||||||
var offset = (decimal) Math.Sqrt((((512 + rmean) * r * r) >> 8) + 4 * g * g + (((767 - rmean) * b * b) >> 8));
|
var offset = (decimal) Math.Sqrt((((512 + rmean) * r * r) >> 8) + 4 * g * g + (((767 - rmean) * b * b) >> 8));
|
||||||
return new LegoArtColorOffset((decimal) offset, colorInfo);
|
return new LegoArtColorOffset((decimal) offset, colorInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LegoArtColorOffset ColorOffset(Color inputColor, LegoArtColorInfo colorInfo)
|
|
||||||
{
|
|
||||||
var offset = Math.Abs(
|
|
||||||
ColorNum(inputColor) - ColorNum(colorInfo.Color)) +
|
|
||||||
GetHueDistance(inputColor.GetHue(), colorInfo.Color.GetHue());
|
|
||||||
|
|
||||||
return new LegoArtColorOffset((decimal) offset, colorInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static float GetBrightness(Color c)
|
|
||||||
{
|
|
||||||
return (c.R * 0.299f + c.G * 0.587f + c.B *0.114f) / 256f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static float GetHueDistance(float hue1, float hue2)
|
|
||||||
{
|
|
||||||
var d = Math.Abs(hue1 - hue2);
|
|
||||||
return d > 180 ? 360 - d : d;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static float ColorNum(Color c)
|
|
||||||
{
|
|
||||||
return c.GetSaturation() + GetBrightness(c);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
42
LegoArtTool/Pixels/GraphicExtensions.cs
Normal file
42
LegoArtTool/Pixels/GraphicExtensions.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Drawing2D;
|
||||||
|
|
||||||
|
namespace LegoArtTool.Pixels
|
||||||
|
{
|
||||||
|
public static class GraphicsExtensions
|
||||||
|
{
|
||||||
|
public static void DrawCircle(this Graphics g, Pen pen, float centerX, float centerY, float radius)
|
||||||
|
{
|
||||||
|
g.DrawEllipse(pen, centerX - radius, centerY - radius, radius + radius, radius + radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void FillCircle(this Graphics g, Brush brush, float centerX, float centerY, float radius)
|
||||||
|
{
|
||||||
|
g.FillEllipse(brush, centerX - radius, centerY - radius, radius + radius, radius + radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DrawText(this Graphics g, Brush brush, Font font, int size, StringAlignment alignment, string text)
|
||||||
|
{
|
||||||
|
var format = new StringFormat();
|
||||||
|
format.LineAlignment = alignment;
|
||||||
|
format.Alignment = alignment;
|
||||||
|
|
||||||
|
var rectf = new RectangleF(0, 0, size, size);
|
||||||
|
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||||
|
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||||
|
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||||
|
g.DrawString(text, font, brush, rectf, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Font GetIdealFontSize(this Graphics g, int size, string referenceText, Font preferedFont)
|
||||||
|
{
|
||||||
|
var room = new Size(size, size);
|
||||||
|
var realSize = g.MeasureString(referenceText, preferedFont);
|
||||||
|
var heightScaleRatio = room.Height / realSize.Height;
|
||||||
|
var widthScaleRatio = room.Width / realSize.Width;
|
||||||
|
var scaleRatio = (heightScaleRatio < widthScaleRatio) ? heightScaleRatio : widthScaleRatio;
|
||||||
|
var scaleFontSize = preferedFont.Size * scaleRatio;
|
||||||
|
return new Font(preferedFont.FontFamily, scaleFontSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,27 +6,42 @@ namespace LegoArtTool.Pixels
|
||||||
{
|
{
|
||||||
public Color Color { get; private set; }
|
public Color Color { get; private set; }
|
||||||
public int Number { get; private set; }
|
public int Number { get; private set; }
|
||||||
public int Size { get; private set; }
|
|
||||||
|
|
||||||
public Pixel(Color color, int size, int number)
|
public const int PixelSize = 50;
|
||||||
|
private const int NumberSize = 20;
|
||||||
|
|
||||||
|
public Pixel(Color color, int number)
|
||||||
{
|
{
|
||||||
Color = color;
|
Color = color;
|
||||||
Size = size;
|
|
||||||
Number = number;
|
Number = number;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bitmap GetBitmap()
|
public Bitmap GetBitmap()
|
||||||
{
|
{
|
||||||
var bitmap = new Bitmap(Size, Size);
|
var bitmap = new Bitmap(PixelSize, PixelSize);
|
||||||
for (int x = 0; x < Size; x++)
|
for (int x = 0; x < PixelSize; x++)
|
||||||
{
|
{
|
||||||
for (int y = 0; y < Size; y++)
|
for (int y = 0; y < PixelSize; y++)
|
||||||
{
|
{
|
||||||
bitmap.SetPixel(x, y, Color);
|
bitmap.SetPixel(x, y, Color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DrawNumber(bitmap, Number);
|
||||||
|
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawNumber(Bitmap bitmap, int number)
|
||||||
|
{
|
||||||
|
|
||||||
|
using (var g = Graphics.FromImage(bitmap))
|
||||||
|
{
|
||||||
|
g.FillCircle(Brushes.White, PixelSize / 2, PixelSize / 2, NumberSize / 2);
|
||||||
|
var font = g.GetIdealFontSize(NumberSize, "15", new Font("Arial", 8));
|
||||||
|
g.DrawText(Brushes.Black, font, PixelSize, StringAlignment.Center, number.ToString());
|
||||||
|
g.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace LegoArtTool.Pixels
|
namespace LegoArtTool.Pixels
|
||||||
{
|
{
|
||||||
|
|
@ -7,14 +9,12 @@ namespace LegoArtTool.Pixels
|
||||||
{
|
{
|
||||||
public Pixel[,] Pixels { get; private set; }
|
public Pixel[,] Pixels { get; private set; }
|
||||||
public int MatrixSize { get; private set; }
|
public int MatrixSize { get; private set; }
|
||||||
public int PixelSize { get; private set; }
|
|
||||||
|
|
||||||
private Random rnd = new Random();
|
private Random rnd = new Random();
|
||||||
|
|
||||||
public PixelMatrix(int pixelSize, Bitmap sourceBitmap)
|
public PixelMatrix(Bitmap sourceBitmap, List<LegoArtColorInfo> legoArtColors)
|
||||||
{
|
{
|
||||||
MatrixSize = sourceBitmap.Width;
|
MatrixSize = sourceBitmap.Width;
|
||||||
PixelSize = pixelSize;
|
|
||||||
Pixels = new Pixel[MatrixSize, MatrixSize];
|
Pixels = new Pixel[MatrixSize, MatrixSize];
|
||||||
|
|
||||||
for (int x = 0; x < MatrixSize; x++)
|
for (int x = 0; x < MatrixSize; x++)
|
||||||
|
|
@ -23,14 +23,15 @@ namespace LegoArtTool.Pixels
|
||||||
{
|
{
|
||||||
var pixel = sourceBitmap.GetPixel(x, y);
|
var pixel = sourceBitmap.GetPixel(x, y);
|
||||||
var sourceColor = Color.FromArgb(pixel.ToArgb());
|
var sourceColor = Color.FromArgb(pixel.ToArgb());
|
||||||
Pixels[x, y] = new Pixel(sourceColor, PixelSize, 9);
|
var legoNumber = legoArtColors.Single(l => l.Color == sourceColor).LegoNumber;
|
||||||
|
Pixels[x, y] = new Pixel(sourceColor, legoNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bitmap ConvertBitmap()
|
public Bitmap ConvertBitmap()
|
||||||
{
|
{
|
||||||
var bitmapSize = MatrixSize * PixelSize;
|
var bitmapSize = MatrixSize * Pixel.PixelSize;
|
||||||
var bitmap = new Bitmap(bitmapSize, bitmapSize);
|
var bitmap = new Bitmap(bitmapSize, bitmapSize);
|
||||||
using (var g = Graphics.FromImage(bitmap))
|
using (var g = Graphics.FromImage(bitmap))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue