private static double errorPolygon(Sample s, PixelMap original, TrigradOptions options) { if(options.ResampleColors) foreach (var sample in s.Samples) { sample.Color = original[sample.Point]; } double error = 0d; foreach (var t in s.Triangles) { t.CenterColor = original[t.CenterPoint]; options.Renderer.Fill(t,tempMap); foreach (var drawPoint in t.Points) { Pixel a = original[drawPoint.Point]; Pixel b = tempMap[drawPoint.Point]; Pixel diff = a - b; error += diff.R; error += diff.G; error += diff.B; } } return error; }
private static void drawMesh(List<SampleTri> mesh, PixelMap output, TrigradOptions options) { int i = 0; int count = mesh.Count; foreach (var triangle in mesh) { options.Renderer.Fill(triangle,output); if (i % 50 == 0 && OnUpdate != null) OnUpdate((double)i / count); i++; } }
/// <summary> Decompresses a trigrad compressed bitmap. </summary> /// <param name="compressionData"> The TrigradCompressed data.</param> /// <param name="original"> The original image to determine the most effect fill mode.</param> /// <param name="options"> Options dictating the decompression.</param> public static TrigradDecompressed DecompressBitmap(TrigradCompressed compressionData, TrigradOptions options) { TrigradDecompressed decompressed = new TrigradDecompressed(compressionData.Width, compressionData.Height); for (int x = 0; x < compressionData.Width; x++) for (int y = 0; y < compressionData.Height; y++) decompressed.Output[x, y] = new Pixel(Color.HotPink); drawMesh(compressionData.Mesh, decompressed.Output, options); fillGaps(decompressed.Output); return decompressed; }
/// <summary> Compresses a bitmap using TrigradCompression. </summary> /// <param name="bitmap"> The input bitmap.</param> /// <param name="options"> TrigradOptions specifying how the image will be compressed.</param> public static TrigradCompressed CompressBitmap(Bitmap bitmap, TrigradOptions options) { TrigradCompressed compressed = new TrigradCompressed { Height = bitmap.Height, Width = bitmap.Width }; List<Point> samplePoints = new List<Point>(); samplePoints.Add(new Point(0, 0)); samplePoints.Add(new Point(bitmap.Width - 1, 0)); samplePoints.Add(new Point(0, bitmap.Height - 1)); samplePoints.Add(new Point(bitmap.Width - 1, bitmap.Height - 1)); double baseChance = 1d / ((double)(bitmap.Width * bitmap.Height) / options.SampleCount / 8); for (int x = 0; x < bitmap.Width; x++) { for (int y = 0; y < bitmap.Height; y++) { double chance = ((options.FrequencyTable != null) ? options.FrequencyTable.Table[x, y] : 1d) * baseChance; if (options.Random.NextDouble() < chance) { samplePoints.Add(new Point(x, y)); } } } foreach (var sample in samplePoints) { List<Color> averageColors = new List<Color>(); for (int x = sample.X - options.SampleRadius; x < sample.X + options.SampleRadius + 1; x++) { for (int y = sample.Y - options.SampleRadius; y < sample.Y + options.SampleRadius + 1; y++) { if (y >= 0 && y < bitmap.Height && x >= 0 && x < bitmap.Width) { averageColors.Add(bitmap.GetPixel(x, y)); } } } byte R = (byte)averageColors.Average(c => c.R); byte G = (byte)averageColors.Average(c => c.G); byte B = (byte)averageColors.Average(c => c.B); compressed.SampleTable[sample] = Color.FromArgb(R, G, B); } return compressed; }
public static void OptimiseMesh(TrigradCompressed compressionData, PixelMap original, TrigradOptions options) { var mesh = compressionData.Mesh; GPUT.CalculateMesh(mesh); var samples = mesh.SelectMany(t => t.Samples).Distinct().ToList(); for (int i = 0; i < options.Iterations; i++) { minimiseMesh(samples, options, original); Console.WriteLine("{0}/{1}", i, options.Iterations); } compressionData.Mesh = mesh; }
/// <summary> Compresses a bitmap using TrigradCompression. </summary> /// <param name="pixelmap"> The input bitmap.</param> /// <param name="options"> TrigradOptions specifying how the image will be compressed.</param> public static TrigradCompressed CompressBitmap(PixelMap pixelmap, TrigradOptions options) { TrigradCompressed compressed = new TrigradCompressed { Height = pixelmap.Height, Width = pixelmap.Width }; double baseChance = options.SampleCount / (options.FrequencyTable.Sum); int i = 0; int count = pixelmap.Width * pixelmap.Height; for (int x = 0; x < pixelmap.Width; x++) { for (int y = 0; y < pixelmap.Height; y++) { if ((x == 0 && y == 0) || (x == pixelmap.Width - 1 && y == 0) || (x == 0 && y == pixelmap.Height - 1) || (x == pixelmap.Width - 1 && y == pixelmap.Height - 1)) { compressed.SampleTable[new Point(x, y)] = pixelmap[new Point(x, y)]; continue; } double chance = ((options.FrequencyTable != null) ? options.FrequencyTable.Table[x, y] : 1d) * baseChance; lock(options.Random) if (options.Random.NextDouble() < chance) { lock (compressed.SampleTable) compressed.SampleTable[new Point(x, y)] = pixelmap[new Point(x, y)]; } if (i % 50 == 0 && OnUpdate != null) OnUpdate((double)i / count); i++; } } return compressed; }
private static void minimiseSample(Sample s, int resamples, PixelMap original, TrigradOptions options) { if (s.Point.X == 0 || s.Point.Y == 0) return; if (s.Point.X == original.Width - 1 || s.Point.Y == original.Height - 1) return; var curPoints = s.Points; double minError = errorPolygon(s, original, options); Point bestPoint = s.Point; if (polygonConvex(s)) return; int count = curPoints.Count; int skip = count / resamples; if (skip == 0) skip = 1; foreach (var drawPoint in curPoints.Where((x, i) => i % skip == 0)) { s.Point = drawPoint.Point; TriangleRasterization.CalculateMesh(s.Triangles); double error = errorPolygon(s, original, options); if (error < minError) { bestPoint = drawPoint.Point; minError = error; } } s.Point = bestPoint; TriangleRasterization.CalculateMesh(s.Triangles); }
static void minimiseMesh(List<Sample> samples, TrigradOptions options, PixelMap original) { tempMap = new PixelMap(original.Width,original.Height); int o = 0; int count = samples.Count; foreach (var sample in samples) { minimiseSample(sample, options.Resamples, original, options); o++; if (o%1000 == 0) Console.WriteLine("{0}/{1}", o, samples.Count); if (o%100 == 0 && OnUpdate != null) OnUpdate((double)o/count); } }
static void Main(string[] args) { Stopwatch s = new Stopwatch(); s.Start(); Console.WriteLine("Trigrad"); string input = "tests\\input\\Tulips.jpg"; PixelMap inputBitmap = PixelMap.SlowLoad(new Bitmap(input)); FrequencyTable table = new FrequencyTable(inputBitmap,1); var options = new TrigradOptions { SampleCount = 40000, FrequencyTable = table, Resamples = 4, Iterations =0, Random = new Random(0), ResampleColors = true, Renderer = new ShapeFill(3) }; var results = TrigradCompressor.CompressBitmap(inputBitmap, options); results.DebugVisualisation().GetBitmap().Save("tests\\points.png"); results.Mesh = MeshBuilder.BuildMesh(results.SampleTable); results.MeshOutput(inputBitmap).GetBitmap().Save("tests\\mesh_a.png"); TrigradOptimiser.OptimiseMesh(results, inputBitmap, options); GPUT.CalculateMesh(results.Mesh); //results.Save(new FileStream("tests\\out.tri", FileMode.Create)); results.MeshOutput(inputBitmap).GetBitmap().Save("tests\\mesh_b.png"); Console.WriteLine(results.SampleTable.Count); //var loaded = new TrigradCompressed(new FileStream("tests\\out.tri", FileMode.Open)); //results.Mesh = results.Mesh.OrderBy(t => t.CenterColor.Lightness).ToList(); results.Mesh.Shuffle(options.Random); var returned = TrigradDecompressor.DecompressBitmap(results, options); returned.Output.GetBitmap().Save("tests\\output.png"); returned.DebugOutput.GetBitmap().Save("tests\\debug_output.png"); int error = errorBitmap(inputBitmap, returned.Output); double avgError = Math.Round((double)error / (inputBitmap.Width * inputBitmap.Height * 3), 2); Console.WriteLine("{0} error", avgError); Console.WriteLine("{0} s", Math.Round(s.ElapsedMilliseconds / 1000d, 2)); saveBackup(avgError, Path.GetFileNameWithoutExtension(input), options); Console.Beep(); Console.ReadKey(); }
static void saveBackup(double error, string file, TrigradOptions options) { Random r = new Random(); var path = Path.Combine(backup, string.Format("{0} Test #{1}, {2} err, {3} samples, {4} resamples, {5} iterations", file, r.Next(), error, options.SampleCount, options.Resamples, options.Iterations)); Directory.CreateDirectory(path); File.Copy("tests\\points.png", Path.Combine(path, "points.png")); File.Copy("tests\\output.png", Path.Combine(path, "output.png")); //File.Copy("tests\\debug_output.png", Path.Combine(path, "debug_output.png")); File.Copy("tests\\mesh_a.png", Path.Combine(path, "mesh_a.png")); File.Copy("tests\\mesh_b.png", Path.Combine(path, "mesh_b.png")); File.Copy("tests\\error.png", Path.Combine(path, "error.png")); }