private void EdgeDetectionRefresh() { if (image == null) { return; } Potrace.Clear(); ListOfPaths.Clear(); double scale = OrigImage.ActualHeight / image.Height; if (scale <= 0) { return; } int x = (int)(RectCutX / scale); int w = (int)(RectCutWidth / scale); int y = (int)(RectCutY / scale); int h = (int)(RectCutHeight / scale); Bitmap cutImage = CropBitmap(image, new Rectangle(x, y, w, h)); Potrace.Potrace_Trace(cutImage, ListOfPaths); EdgePathGeometry.AddListOfPaths(ListOfPaths); }
protected override void SolveInstance(IGH_DataAccess DA) { // Variables Bitmap bm; GrasshopperBitmapGoo ghbm = new GrasshopperBitmapGoo(); double t = 50.0; double a = 1.0; double mts = 2; bool opt = true; double opttol = 0.2; bool inv = false; int colorCount = 2; // Get Data from Input Params if (!DA.GetData(0, ref ghbm)) { return; } if (DA.GetData(1, ref t)) { if (0.0 > t || t > 100.0) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Threshold must lie between 0.0 to 100.0"); return; } } if (DA.GetData(2, ref a)) { if (0.0 > a || a > 1.0) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Corner Threshold must lie between 0.0 to 1.0"); return; } } DA.GetData(3, ref mts); DA.GetData(4, ref opt); if (DA.GetData(5, ref opttol)) { if (0.0 > opttol || opttol > 1.0) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Tolerance for Optimization must lie between 0.0 to 1.0"); return; } } DA.GetData(6, ref inv); if (DA.GetData(7, ref colorCount)) { if (colorCount < 0) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Color Count cannot be negative"); return; } } else { if (getColors) { AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Color Count not set. Setting 'Get Colors' to False. Please set a value for Color Count and re-enable Get Colors by right-clicking on the component"); getColors = false; } } // set Data in Potrace fields Potrace.Treshold = t / 100; Potrace.alphamax = a * (4 / 3); Potrace.turdsize = ((int)Math.Round(mts, 0, MidpointRounding.AwayFromZero)); Potrace.curveoptimizing = opt; Potrace.opttolerance = opttol; if (ghbm.IsValid && ghbm.Image != null) { bm = ghbm.Image; } else { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Invalid Bitmap"); return; } // convert png transparent background to white if (!getColors || (colorCount == 0)) { using (Bitmap b = new Bitmap(bm.Width, bm.Height)) { b.SetResolution(bm.HorizontalResolution, bm.VerticalResolution); using (Graphics g = Graphics.FromImage(b)) { g.Clear(Color.White); g.DrawImageUnscaled(bm, 0, 0); } b.RotateFlip(RotateFlipType.RotateNoneFlipY); DataTree <Curve> crvs = new DataTree <Curve>(); Potrace.Potrace_Trace(b, crvs, inv); DA.SetDataTree(0, crvs); } } else { using (Bitmap b = new Bitmap(bm.Width, bm.Height)) { b.SetResolution(bm.HorizontalResolution, bm.VerticalResolution); using (Graphics g = Graphics.FromImage(b)) { g.Clear(Color.Transparent); g.DrawImageUnscaled(bm, 0, 0); } b.RotateFlip(RotateFlipType.RotateNoneFlipY); WuQuantizer quantizer = new WuQuantizer(); Bitmap quantized = (Bitmap)quantizer.QuantizeImage(b, colorCount + 1); Color[] colors = new Color[colorCount]; Array.Copy(quantized.Palette.Entries, 0, colors, 0, colorCount); DataTree <GH_Colour> colorsOut = new DataTree <GH_Colour>(); DataTree <Curve> crvs = new DataTree <Curve>(); for (int i = 0; i < colorCount; i++) { Bitmap temp = quantized.Clone(new Rectangle(0, 0, b.Width, b.Height), PixelFormat.Format32bppArgb); var bmData = temp.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); unsafe { byte *p = (byte *)bmData.Scan0; int stopAddress = (int)p + bmData.Stride * bmData.Height; while ((int)p != stopAddress) { if (p[0] == colors[i].B && p[1] == colors[i].G && p[2] == colors[i].R && p[3] == colors[i].A) { p[0] = p[1] = p[2] = p[3] = 255; } else { p[0] = p[1] = p[2] = p[3] = 0; } p += 4; } temp.UnlockBits(bmData); List <Curve> curves = new List <Curve>(); Potrace.Potrace_Trace(temp, curves, true); crvs.AddRange(curves, new GH_Path(i)); if (inv) { Color invCol = Color.FromArgb(((int)colors[i].A), (255 - ((int)colors[i].R)), (255 - ((int)colors[i].G)), (255 - ((int)colors[i].B))); colorsOut.Add(new GH_Colour(invCol), new GH_Path(i)); } else { colorsOut.Add(new GH_Colour(colors[i]), new GH_Path(i)); } Potrace.Clear(); Potrace.Treshold = t / 100; Potrace.alphamax = a * (4 / 3); Potrace.turdsize = ((int)Math.Round(mts, 0, MidpointRounding.AwayFromZero)); Potrace.curveoptimizing = opt; Potrace.opttolerance = opttol; } } DA.SetDataTree(0, crvs); DA.SetDataTree(2, colorsOut); } } Rectangle3d boundary = new Rectangle3d(Plane.WorldXY, (double)bm.Width, (double)bm.Height); DA.SetData(1, boundary); }
protected override void BeforeSolveInstance() { base.BeforeSolveInstance(); Potrace.Clear(); }
/// <summary> /// Command.RunCommand override /// </summary> protected override Result RunCommand(RhinoDoc doc, RunMode mode) { Potrace.Clear(); // Prompt the user for the name of the image file to vectorize. string path = GetImageFileName(mode); if (string.IsNullOrEmpty(path)) { return(Result.Cancel); } // Creates a bitmap from the specified file. var bitmap = Image.FromFile(path) as Bitmap; if (null == bitmap) { RhinoApp.WriteLine("The specified file cannot be identifed as a supported type."); return(Result.Failure); } // Verify bitmap size if (0 == bitmap.Width || 0 == bitmap.Height) { RhinoApp.WriteLine("Error reading the specified file."); return(Result.Failure); } // Calculate scale factor so curves of a reasonable size are added to Rhino var unit_scale = (doc.ModelUnitSystem != UnitSystem.Inches) ? RhinoMath.UnitScale(UnitSystem.Inches, doc.ModelUnitSystem) : 1.0; var scale = (double)(1.0 / bitmap.HorizontalResolution * unit_scale); // I'm not convinced this is useful... if (true) { var format = $"F{doc.DistanceDisplayPrecision}"; // Print image size in pixels RhinoApp.WriteLine("Image size in pixels: {0} x {1}", bitmap.Width, bitmap.Height ); // Print image size in inches var width = (double)(bitmap.Width / bitmap.HorizontalResolution); var height = (double)(bitmap.Height / bitmap.VerticalResolution); RhinoApp.WriteLine("Image size in inches: {0} x {1}", width.ToString(format, CultureInfo.InvariantCulture), height.ToString(format, CultureInfo.InvariantCulture) ); // Image size in in model units, if needed if (doc.ModelUnitSystem != UnitSystem.Inches) { width = (double)(bitmap.Width / bitmap.HorizontalResolution * unit_scale); height = (double)(bitmap.Height / bitmap.VerticalResolution * unit_scale); RhinoApp.WriteLine("Image size in {0}: {1} x {2}", doc.ModelUnitSystem.ToString().ToLower(), width.ToString(format, CultureInfo.InvariantCulture), height.ToString(format, CultureInfo.InvariantCulture) ); } } // Convert the bitmap to an Eto bitmap var eto_bitmap = ConvertBitmapToEto(bitmap); if (null == eto_bitmap) { RhinoApp.WriteLine("Unable to convert image to Eto bitmap."); return(Result.Failure); } // 12-Jan-2021 Dale Fugier // This should prevent Eto.Drawing.BitmapData.GetPixels() from throwing an exception if (!IsCompatibleBitmap(eto_bitmap)) { RhinoApp.WriteLine("The image has an incompatible pixel format. Please select an image with 24 or 32 bits per pixel, or 8 bit indexed."); return(Result.Failure); } // This bitmap is not needed anymore, so dispose of it bitmap.Dispose(); // Gets the Potrace settings from the plug-in settings file GetPotraceSettings(); // Create the conduit, which does most of the work var conduit = new VectorizeConduit( eto_bitmap, scale, doc.ModelAbsoluteTolerance, m_select_output ? Rhino.ApplicationSettings.AppearanceSettings.SelectedObjectColor : doc.Layers.CurrentLayer.Color ) { Enabled = true }; if (mode == RunMode.Interactive) { // Show the interactive dialog box var dialog = new VectorizeDialog(doc, conduit); dialog.RestorePosition(); var result = dialog.ShowSemiModal(doc, RhinoEtoApp.MainWindow); dialog.SavePosition(); if (result != Result.Success) { conduit.Enabled = false; Potrace.Clear(); doc.Views.Redraw(); return(Result.Cancel); } } else { // Show the command line options var go = new GetOption(); go.SetCommandPrompt("Vectorization options. Press Enter when done"); go.AcceptNothing(true); while (true) { conduit.TraceBitmap(); doc.Views.Redraw(); go.ClearCommandOptions(); // IgnoreArea var turdsize_opt = new OptionInteger(Potrace.turdsize, 2, 100); var turdsize_idx = go.AddOptionInteger("FilterSize", ref turdsize_opt, "Filter speckles of up to this size in pixels"); // TurnPolicy var turnpolicy_idx = go.AddOptionEnumList("TurnPolicy", Potrace.turnpolicy); // Optimizing var curveoptimizing_opt = new OptionToggle(Potrace.curveoptimizing, "No", "Yes"); var curveoptimizing_idx = go.AddOptionToggle("Optimizing", ref curveoptimizing_opt); // Tolerance var opttolerance_opt = new OptionDouble(Potrace.opttolerance, 0.0, 1.0); var opttolerance_idx = go.AddOptionDouble("Tolerance", ref opttolerance_opt, "Optimizing tolerance"); // CornerThreshold var alphamax_opt = new OptionDouble(Potrace.alphamax, 0.0, 100.0); var alphamax_idx = go.AddOptionDouble("CornerRounding", ref alphamax_opt, "Corner rounding threshold"); // Threshold var threshold_opt = new OptionDouble(Potrace.Treshold, 0.0, 100.0); var threshold_idx = go.AddOptionDouble("Threshold", ref threshold_opt, "Threshold"); // RestoreDefaults var defaults_idx = go.AddOption("RestoreDefaults"); var res = go.Get(); if (res == GetResult.Option) { var option = go.Option(); if (null != option) { if (turdsize_idx == option.Index) { Potrace.turdsize = turdsize_opt.CurrentValue; } if (turnpolicy_idx == option.Index) { var list = Enum.GetValues(typeof(TurnPolicy)).Cast <TurnPolicy>().ToList(); Potrace.turnpolicy = list[option.CurrentListOptionIndex]; } if (curveoptimizing_idx == option.Index) { Potrace.curveoptimizing = curveoptimizing_opt.CurrentValue; } if (opttolerance_idx == option.Index) { Potrace.opttolerance = opttolerance_opt.CurrentValue; } if (alphamax_idx == option.Index) { Potrace.alphamax = alphamax_opt.CurrentValue; } if (threshold_idx == option.Index) { Potrace.Treshold = threshold_opt.CurrentValue; } if (defaults_idx == option.Index) { Potrace.RestoreDefaults(); } } continue; } if (res != GetResult.Nothing) { conduit.Enabled = false; doc.Views.Redraw(); Potrace.Clear(); return(Result.Cancel); } break; } } // Group curves var attributes = doc.CreateDefaultAttributes(); attributes.AddToGroup(doc.Groups.Add()); for (var i = 0; i < conduit.OutlineCurves.Count; i++) { var rhobj_id = doc.Objects.AddCurve(conduit.OutlineCurves[i], attributes); if (m_select_output) { var rhobj = doc.Objects.Find(rhobj_id); if (null != rhobj) { rhobj.Select(true); } } } conduit.Enabled = false; Potrace.Clear(); doc.Views.Redraw(); // Set the Potrace settings to the plug -in settings file. SetPotraceSettings(); return(Result.Success); }
/// <summary> /// Trace the bitmap using Potrace. /// </summary> public void TraceBitmap() { Clear(); Potrace.Clear(); Potrace.Potrace_EtoTrace(m_bitmap, m_path_curves); }
protected override void SolveInstance(IGH_DataAccess DA) { DA.GetData(0, ref ImgPath); int t = 0; DA.GetData(1, ref t); Potrace.Treshold = (double)t / 100; DA.GetData(2, ref Potrace.alphamax); int policy = 0; DA.GetData(3, ref policy); Potrace.turnpolicy = (TurnPolicy)policy; DA.GetData(4, ref Potrace.turdsize); DA.GetData(5, ref Potrace.curveoptimizing); DA.GetData(6, ref Potrace.opttolerance); //DA.GetData(7, ref boundary); bool inv = false; DA.GetData(7, ref inv); int count = 0; DA.GetData(8, ref count); //Read and flip image bm = new Bitmap(ImgPath); bm.RotateFlip(RotateFlipType.RotateNoneFlipY); // convert to argb bm = bm.Clone(new Rectangle(0, 0, bm.Width, bm.Height), PixelFormat.Format32bppArgb); // get boundary int H = bm.Height; int W = bm.Width; Rectangle3d boundary = new Rectangle3d(Plane.WorldXY, W, H); DataTree <GH_Colour> GC = new DataTree <GH_Colour>(); DataTree <Curve> curves = new DataTree <Curve>(); if ((int)count < 1) { // convert png transparent background to white var b = new Bitmap(bm.Width, bm.Height); b.SetResolution(bm.HorizontalResolution, bm.VerticalResolution); using (var g = Graphics.FromImage(b)) { g.Clear(Color.White); g.DrawImageUnscaled(bm, 0, 0); } List <Curve> crvs = new List <Curve>(); Potrace.Potrace_Trace(b, crvs, inv); curves.AddRange(crvs); } else { // quantitize image Potrace.Treshold = 0.1; var quantizer = new WuQuantizer(); Bitmap quantized = (Bitmap)quantizer.QuantizeImage(bm, count + 1); Color[] colors = new Color[count]; Array.Copy(quantized.Palette.Entries, 0, colors, 0, count); // segment image by color // TODO, processing each color in parellel if (parallel) { for (int i = 0; i < colors.Length; i++) { Bitmap temp = quantized.Clone(new Rectangle(0, 0, bm.Width, bm.Height), PixelFormat.Format32bppArgb); var bmData = temp.LockBits(new Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); unsafe { byte *p = (byte *)bmData.Scan0; int stopAddress = (int)p + bmData.Stride * bmData.Height; while ((int)p != stopAddress) { if (p[0] == colors[i].B && p[1] == colors[i].G && p[2] == colors[i].R && p[3] == colors[i].A) { p[0] = p[1] = p[2] = p[3] = 255; } else { p[0] = p[1] = p[2] = p[3] = 0; } p += 4; } temp.UnlockBits(bmData); List <Curve> crvs = new List <Curve>(); Potrace.Potrace_Trace(temp, crvs, inv); curves.AddRange(crvs, new GH_Path(i)); GC.Add(new GH_Colour(colors[i]), new GH_Path(i)); Potrace.Clear(); } } } for (int i = 0; i < colors.Length; i++) { Bitmap temp = quantized.Clone(new Rectangle(0, 0, bm.Width, bm.Height), PixelFormat.Format32bppArgb); var bmData = temp.LockBits(new Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); unsafe { byte *p = (byte *)bmData.Scan0; int stopAddress = (int)p + bmData.Stride * bmData.Height; while ((int)p != stopAddress) { if (p[0] == colors[i].B && p[1] == colors[i].G && p[2] == colors[i].R && p[3] == colors[i].A) { p[0] = p[1] = p[2] = p[3] = 255; } else { p[0] = p[1] = p[2] = p[3] = 0; } p += 4; } temp.UnlockBits(bmData); List <Curve> crvs = new List <Curve>(); Potrace.Potrace_Trace(temp, crvs, inv); curves.AddRange(crvs, new GH_Path(i)); GC.Add(new GH_Colour(colors[i]), new GH_Path(i)); Potrace.Clear(); } } } DA.SetDataTree(0, curves); DA.SetData(1, boundary); DA.SetDataTree(2, GC); }
protected override void AfterSolveInstance() { base.AfterSolveInstance(); Potrace.Clear(); }
private void Trace() { Potrace.Clear(); ListOfPathes.Clear(); Potrace.Potrace_Trace(sourceImage, ListOfPathes); }