public SampledImage SampleImage(Bitmap image, int sampleCount) { Init(image, sampleCount); sampleCount = Math.Min(sampleCount, image.Width * image.Height); SampledImage sampledImage = new SampledImage() { Width = width, Height = height }; Progress.ReportProgress(0); // NOTE: some random position candidates may go outside the image // and thus are do not contribute as the real image samples Stopwatch stopwatch = Stopwatch.StartNew(); GenerateSamples(image, sampleCount, sampledImage); if (canReadImageDirectly) { image.UnlockBits(imageData); } stopwatch.Stop(); Progress.TimeReport["sampler"] = stopwatch.ElapsedMilliseconds; Progress.ReportProgress(100); return sampledImage; }
public static SampledImage LoadFromFile(string fileName) { SampledImage sampledImage = new SampledImage(); using (StreamReader reader = new StreamReader(fileName)) { try { sampledImage.Width = Int32.Parse(reader.ReadLine()); sampledImage.Height = Int32.Parse(reader.ReadLine()); string line = reader.ReadLine(); while ((line != null) && (line.Length != 0)) { string[] sampleFields = line.Split(new char[] { ' ' }, 3); int x = Int32.Parse(sampleFields[0]); int y = Int32.Parse(sampleFields[1]); int argb = Int32.Parse(sampleFields[2]); Color color = Color.FromArgb(argb); sampledImage.AddSample(new ImageSample(x, y, color)); line = reader.ReadLine(); } } catch (FormatException ex) { Console.Error.WriteLine(ex.Message); return null; } } return sampledImage; }
protected int GenerateCluster(Bitmap image, int sampleCount, SampledImage sampledImage, int samplePercent, int percentDone, int maxIterations) { for (int i = 0; (sampledImage.Samples.Count < sampleCount) && (i < maxIterations); i++) { int x; int y; if (!Next2DWithinImage(out x, out y)) { continue; } Color color; if (canReadImageDirectly) { // faster, only for BGR or BGRA unsafe { byte* imagePtr = imagePtrBase + (y * imageData.Stride) + x * bands; byte b = imagePtr[0]; byte g = imagePtr[1]; byte r = imagePtr[2]; byte a = 255; if (hasAlpha) { a = imagePtr[3]; } color = Color.FromArgb(a, r, g, b); } } else { // slower, more general color = image.GetPixel(x, y); } sampledImage.AddSample(new ImageSample(x, y, color)); if ((samplePercent > 500) && (sampledImage.Samples.Count % samplePercent == 0)) { percentDone++; Progress.ReportProgress(percentDone); } } return percentDone; }
protected override void GenerateSamples(Bitmap image, int sampleCount, SampledImage sampledImage) { // NOTE: some random position candidates may go outside the image // and thus are do not contribute as the real image samples int samplePercent = sampleCount / 100; int percentDone = 0; int samplesPerCluster = sampleCount / ClusterCount; int targetSampleCount = sampleCount % ClusterCount; for (int cluster = 0; cluster < ClusterCount; cluster++) { targetSampleCount += samplesPerCluster; int maxIterations = 10 * targetSampleCount; // standard deviation of the cluster //double stdDev = maxStdDev * random.NextDouble(); stdDev = meanStdDev * gaussRandom.Next(1, 0.5); // center of the cluster centerX = random.Next(0, width - 1); centerY = random.Next(0, height - 1); percentDone = GenerateCluster(image, targetSampleCount, sampledImage, samplePercent, percentDone, maxIterations); } }
public Bitmap ReconstructImage(SampledImage sampledImage) { int width = sampledImage.Width; int height = sampledImage.Height; Stopwatch stopwatch = Stopwatch.StartNew(); Bitmap image = new Bitmap(width, height); // fill with black background using (Graphics graphics = Graphics.FromImage(image)) { graphics.FillRectangle(Brushes.Black, 0, 0, width, height); } // TODO: // - directly write into the image via pointers // - at least for RGBA images int samplePercent = sampledImage.Samples.Count / 100; int currentSample = 0; int percentDone = 0; if ((image.PixelFormat == PixelFormat.Format32bppArgb) || (image.PixelFormat == PixelFormat.Format24bppRgb)) { // faster, only for BGR or BGRA BitmapData imageData = image.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, image.PixelFormat); bool hasAlpha = image.PixelFormat == PixelFormat.Format32bppArgb; int bands = hasAlpha ? 4 : 3; unsafe { byte* imagePtrBase = (byte*)imageData.Scan0; foreach (ImageSample sample in sampledImage.Samples) { byte* imagePtr = imagePtrBase + (sample.Y * imageData.Stride) + sample.X * bands; imagePtr[0] = sample.color.B; imagePtr[1] = sample.color.G; imagePtr[2] = sample.color.R; if (hasAlpha) { imagePtr[3] = sample.color.A; } ReportProgress(samplePercent, ref currentSample, ref percentDone); } } image.UnlockBits(imageData); } else { // slower, more general foreach (ImageSample sample in sampledImage.Samples) { image.SetPixel(sample.X, sample.Y, sample.color); ReportProgress(samplePercent, ref currentSample, ref percentDone); } } Progress.TimeReport["visualizer"] = stopwatch.ElapsedMilliseconds; Progress.ReportProgress(100); return image; }
private static void SaveSamplesToFile(SampledImage sampledImage, string sampler) { sampledImage.SaveToFile(string.Format("{0}{1}_samples_{2}_{3}.txt", RESULTS_DIR, INPUT_IMAGE, sampler, SAMPLE_COUNT)); }
private static void ReconstructImage(SampledImage sampledImage, ISampledImageVisualizer visualizer, string sampler) { Bitmap reconstructedImage = visualizer.ReconstructImage(sampledImage); reconstructedImage.Save(string.Format("{0}{1}_reconstructed_{2}_{3}.png", RESULTS_DIR, INPUT_IMAGE, sampler, SAMPLE_COUNT)); }
public Bitmap ReconstructImage(SampledImage sampledImage) { int width = sampledImage.Width; int height = sampledImage.Height; Bitmap image = new Bitmap(width, height); Progress.ReportProgress(0); Stopwatch stopwatch = Stopwatch.StartNew(); // use cached triangulation if possible PointSet triMesh = (TriangulationCachingEnabled && sampledImage.Equals(previousSampledImage)) ? previousTriMesh : voronoiHelper.MakeDelaunayTriangulation(sampledImage); stopwatch.Stop(); Progress.TimeReport["delaunay"] = stopwatch.ElapsedMilliseconds; previousTriMesh = triMesh; previousSampledImage = sampledImage; Progress.ReportProgress(25); stopwatch.Reset(); stopwatch.Start(); using (Graphics graphics = Graphics.FromImage(image)) { // fill with black background //graphics.FillRectangle(Brushes.White, 0, 0, width, height); graphics.SmoothingMode = AntiAliasingEnabled ? System.Drawing.Drawing2D.SmoothingMode.AntiAlias : System.Drawing.Drawing2D.SmoothingMode.None; if (VoronoiCellsEnabled) { DrawVoronoiCells(triMesh, graphics, sampledImage); } Progress.ReportProgress(50); if (DelaunayTrianglesEnabled) { DrawDelaunayTriangles(triMesh, graphics); } Progress.ReportProgress(75); DrawAdditionalFeatures(width, height, triMesh, graphics, image); Progress.ReportProgress(100); } Progress.TimeReport["visualizer"] = stopwatch.ElapsedMilliseconds; return image; }
private void DrawVoronoiCells(PointSet triMesh, Graphics graphics, SampledImage sampledImage) { int samplePercent = 4 * triMesh.Triangles.Count / 100; int trianglesDone = 0; int percentDone = 0; // Voronoi cell which have already been drawn onto the image HashSet<Vector2> drawnVoronoiCells = new HashSet<Vector2>(); foreach (DelaunayTriangle triangle in triMesh.Triangles) { // draw Voronoi cells, one around each Delaunay triangle vertex foreach (TriangulationPoint triVertex in triangle.Points) { // each cell corresponds to a triangulation vertex Vector2 point = triVertex.ToVector2(); if (drawnVoronoiCells.Contains(point)) { // this Voronoi cell was already drawn continue; } drawnVoronoiCells.Add(point); IList<DelaunayTriangle> surroundingTriangles = VoronoiHelper.GetSurroundingTriangles(triangle, triVertex); if (surroundingTriangles.Count < 3) { // there are not enough vertices to make a polygon continue; } // TODO: the computation of the circumference could // be cached since the voronoi vertices are usually // share among multiple triangles IEnumerable<Vector2> voronoiCellVertices = surroundingTriangles.Select( (DelaunayTriangle tri) => VoronoiHelper.GetCircumcenter(tri)); PointF[] voronoiCellPolygon = voronoiCellVertices.Select( (Vector2 vertex) => vertex.ToPointF()).ToArray(); //Color sampleColor = GetRandomColor(); ImageSample imageSample; Color sampleColor = Color.Pink; // DEBUG if (sampledImage.SampleMap.TryGetValue((int)triVertex.Y * sampledImage.Width + (int)triVertex.X, out imageSample)) { sampleColor = imageSample.color; } graphics.FillPolygon(new SolidBrush(sampleColor), voronoiCellPolygon); if (VoronoiCellBordersEnabled) { graphics.DrawPolygon(Pens.Gray, voronoiCellPolygon); } } trianglesDone++; if ((samplePercent > 500) && (trianglesDone % samplePercent == 0)) { percentDone++; Progress.ReportProgress(25 + percentDone); } } }
protected virtual void GenerateSamples(Bitmap image, int sampleCount, SampledImage sampledImage) { int maxIterations = 10 * sampleCount; int samplePercent = sampleCount / 100; int percentDone = 0; GenerateCluster(image, sampleCount, sampledImage, samplePercent, percentDone, maxIterations); }
public PointSet MakeDelaunayTriangulation(SampledImage sampledImage) { List<TriangulationPoint> points = new List<TriangulationPoint>(); foreach (ImageSample sample in sampledImage.Samples) { points.Add(new TriangulationPoint(sample.position.X, sample.position.Y)); } int width = sampledImage.Width; int height = sampledImage.Height; // add a bounding box in order to make Voronoi cells bounded // inside the image points.Add(new TriangulationPoint(-width, -height)); points.Add(new TriangulationPoint(-width, 2 * height)); points.Add(new TriangulationPoint(2 * width, 2 * height)); points.Add(new TriangulationPoint(2 * width, -height)); return MakeDelaunayTriangulation(points); }