archip Posted May 30, 2013 Share Posted May 30, 2013 Hello, I have looked for a plugin that delete small groups of pixel that are left alone on a transparent background (for when I use the magic wand) but I haven't found such a plugin. So I decided to code it myself but it doesn't work, I don't know why. I use Code Lab to code. Here is my code : Rectangle R; Surface SRC; void Render(Surface dst, Surface src, Rectangle rect) { R =rect; SRC = src; ColorBgra CurrentPixel; for(int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { CurrentPixel = src[x,y]; if(testForDelete(x,y,0)) //If we need to delete the pixel CurrentPixel.A=0; // we delete it dst[x,y] = CurrentPixel; } } } //return true if the pixel has to be deleted bool testForDelete(int px, int py, int dist){ if(dist>2) return false; //too far from tested pixel if(px<R.Left || py<R.Top || px>=R.Right || py>=R.Bottom) return true; //out of screen if(SRC[px,py].A==0) return true; // transparent bool ret=true; if(ret) ret = testForDelete(px,py-1,dist+1); if(ret) ret = testForDelete(px,py+1,dist+1); if(ret) ret = testForDelete(px-1,py,dist+1); if(ret) ret = testForDelete(px+1,py,dist+1); //if surrounded only by pixel that must be deleted and pixel that are transparent, return true. return ret; } I don't understand why it doesn't work. It deletes only pixel when there are no pixel to its left and to its right. Quote Link to comment Share on other sites More sharing options...
Red ochre Posted May 30, 2013 Share Posted May 30, 2013 Hello archip, welcome to the forum.I'm no expert!However I believe accessing y, y-1 and y+1 is not possible in codelab because they will (probably) be in different rectangles of interest (Rois). Quite why that gives you the results you are getting - I don't know.I tried something similar in this thread, (to remove 'odd' pixels after creating an ink drawing effect). You may find it interesting:http://forums.getpaint.net/index.php?/topic/21157-complex-ink-drawing-effect-beta/ Member Null54 kindly got it working for me in Visual Sudio.I think VS soloution is probably the best way forward if you want to check all the way around each pixel.This could be a very useful plugin.Boltbait has produced a dust removal plugin in codelab which is worth taking a look at too.http://forums.getpaint.net/index.php?/topic/25458-remove-dust-from-scanned-photos/ Quote Red ochre Plugin pack.............. Diabolical Drawings ................Real Paintings Link to comment Share on other sites More sharing options...
Rick Brewster Posted May 30, 2013 Share Posted May 30, 2013 Yeah there's no way this is working correctly. Render() is called in parallel from multiple threads and you're sharing an instance variable across all of them. Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
archip Posted May 31, 2013 Author Share Posted May 31, 2013 You were right on the spot, ROI were the cause of the problem so I tried VS2010 and I have no more problem with it ! using System; using System.Collections; using System.Drawing; using PaintDotNet; using PaintDotNet.Effects; namespace StrayPixels { public class EffectPlugin : PaintDotNet.Effects.Effect { public static string StaticName { get { return "Stray deleter"; } } // public static Bitmap StaticIcon // { // get // { // return new Bitmap(typeof(EffectPlugin), "EffectPluginIcon.png"); // } // } // // Entire StaticIcon method (above) can be replaced by the line below referencing an icon (png) held in Resources // public static Bitmap StaticImage { get { return Properties.Resources.Icon; } } public static string StaticSubMenuName { get { // return null; // Use for no submenu return SubmenuNames.Render; // Use for existing submenu // return "My SubMenu"; // Use for custom submenu } } public EffectPlugin() : base(EffectPlugin.StaticName, EffectPlugin.StaticImage, EffectPlugin.StaticSubMenuName, EffectFlags.Configurable | EffectFlags.SingleThreaded) { } public override EffectConfigDialog CreateConfigDialog() { return new EffectPluginConfigDialog(); } Rectangle R; Surface SRC; public override void Render(EffectConfigToken parameters, RenderArgs dstArgs, RenderArgs srcArgs, Rectangle[] rois, int startIndex, int length) { /*PdnRegion selectionRegion = EnvironmentParameters.GetSelection(srcArgs.Bounds); for (int i = startIndex; i < startIndex + length; ++i) { Rectangle rect = rois[i]; for (int y = rect.Top; y < rect.Bottom; ++y) { for (int x = rect.Left; x < rect.Right; ++x) { // Render Code Here } } }*/ Surface src = srcArgs.Surface; dstArgs.Surface.CopySurface(src, rois); Surface dst = dstArgs.Surface; SRC = src; R = SRC.Bounds; ColorBgra CurrentPixel; for (int ite = 0; ite < 5; ite++) { for (int y = R.Top; y < R.Bottom; y++) { for (int x = R.Left; x < R.Right; x++) { CurrentPixel = src[x, y]; if (testForDelete(x, y, 0, true, true, true, true)) //If we need to delete the pixel CurrentPixel.A = 0; // we delete it dst[x, y] = CurrentPixel; } } } } //return true if the pixel has to be deleted bool testForDelete(int px, int py, int dist, bool h, bool b, bool g, bool d) { if (dist > 2) return false; //too far from tested pixel if (px < R.Left || py < R.Top || px >= R.Right || py >= R.Bottom) return true; //out of screen if (SRC[px, py].A == 0) return true; // transparent bool ret = true; if (ret && h == true) ret = testForDelete(px, py - 1, dist + 1, true, false, g, d); if (ret && b == true) ret = testForDelete(px, py + 1, dist + 1, false, true, g, d); if (ret && g == true) ret = testForDelete(px - 1, py, dist + 1, h, b, true, false); if (ret && d == true) ret = testForDelete(px + 1, py, dist + 1, h, b, false, true); //if surrounded only by pixel that must be deleted and pixel that are transparent, return true. return ret; } } } I have looked the dust removal filter but it doesn't do what I want so I'll finish this plugin. I have to optimize it (I have some ideas) and make an option to choose the threehold. I'll try to do it this weekend or next week and then I'll publish the DLL. Quote Link to comment Share on other sites More sharing options...
midora Posted May 31, 2013 Share Posted May 31, 2013 Happy coding :-) Have a look to PropertyBased Effect plugins. For the most plugins it is the easiest way to create a dialog. Quote Link to comment Share on other sites More sharing options...
Rick Brewster Posted May 31, 2013 Share Posted May 31, 2013 If you don't have anything to configure, you don't need to mark it as configurable. It looks like you're just showing a blank configuration dialog? Also, I'm pretty sure you don't need to specify the SingleThreaded flag. That is supposed to be used as an absolute last resort, or only to be used in circumstances where it just isn't possible for your algorithm to work across multiple threads. Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
archip Posted June 1, 2013 Author Share Posted June 1, 2013 My new optimisation made multithreading impossible Where can I have a look at PropertyBased Effect plugins ? I can't find anything Also, I have different problems : - Even if I click cancel, the effect is applied - When effect is applied I can't cancel it even if I go back in the history. My code so far : using System; using System.Collections; using System.Drawing; using PaintDotNet; using PaintDotNet.Effects; using System.Collections.Generic; namespace StrayPixels { public class EffectPlugin : PaintDotNet.Effects.Effect { public static string StaticName { get { return "Stray deleter"; } } // public static Bitmap StaticIcon // { // get // { // return new Bitmap(typeof(EffectPlugin), "EffectPluginIcon.png"); // } // } // // Entire StaticIcon method (above) can be replaced by the line below referencing an icon (png) held in Resources // public static Bitmap StaticImage { get { return Properties.Resources.Icon; } } public static string StaticSubMenuName { get { return SubmenuNames.Render; // Use for existing submenu } } public EffectPlugin() : base(EffectPlugin.StaticName, EffectPlugin.StaticImage, EffectPlugin.StaticSubMenuName, EffectFlags.Configurable | EffectFlags.SingleThreaded) { } public override EffectConfigDialog CreateConfigDialog() { return new EffectPluginConfigDialog(); } Rectangle R; Surface SRC; List<Point> ToDelete = new List<Point>(); int DISTANCE = 3; public override void Render(EffectConfigToken parameters, RenderArgs dstArgs, RenderArgs srcArgs, Rectangle[] rois, int startIndex, int length) { Surface src = srcArgs.Surface; dstArgs.Surface.CopySurface(src, rois); Surface dst = dstArgs.Surface; SRC = src; R = SRC.Bounds; ColorBgra VoidPixel = new ColorBgra(); VoidPixel.A = 0; for (int y = R.Top; y < R.Bottom; y++) { for (int x = R.Left; x < R.Right; x++) { ToDelete.Clear(); if (testForDelete(x, y, 0, true, true, true, true)) { //If we need to delete the pixel dst[x, y] = VoidPixel; // we delete it foreach (Point p in ToDelete) //delete contiguous pixels { SRC[p.X, p.Y] = VoidPixel; } } } } for (int y = R.Top; y < R.Bottom; y++) { for (int x = R.Left; x < R.Right; x++) { dst[x, y] = SRC[x, y]; //copy new image } } } //return true if the pixel has to be deleted bool testForDelete(int px, int py, int dist, bool h, bool b, bool g, bool d) { if (dist > DISTANCE) return false; //too far from tested pixel if (px < R.Left || py < R.Top || px >= R.Right || py >= R.Bottom) return true; //out of screen if (SRC[px, py].A == 0) return true; // transparent bool ret = true; if (ret && h == true) ret = testForDelete(px, py - 1, dist + 1, true, false, g, d); if (ret && b == true) ret = testForDelete(px, py + 1, dist + 1, false, true, g, d); if (ret && g == true) ret = testForDelete(px - 1, py, dist + 1, h, b, true, false); if (ret && d == true) ret = testForDelete(px + 1, py, dist + 1, h, b, false, true); //if surrounded only by pixel that must be deleted and pixel that are transparent, return true. if (ret) ToDelete.Add(new Point(px, py)); return ret; } } } Quote Link to comment Share on other sites More sharing options...
Djisves Posted June 1, 2013 Share Posted June 1, 2013 I'm way out of my league in this section of the forums but I remembered a thread where a similar plugin was discussed. I don't know if it can be of any help but, anyway, here it is: Easy way to remove random single/groups noise pixels ? Quote Link to comment Share on other sites More sharing options...
archip Posted June 1, 2013 Author Share Posted June 1, 2013 Yes, it's exactly the tools I want but it seems like avim hasn't developped the plugin. Also, I've found Ed Harvey's threshold effect but it isn't what I want. Quote Link to comment Share on other sites More sharing options...
archip Posted June 3, 2013 Author Share Posted June 3, 2013 I've solved most of my problems and I've manage to do an UI but there is still a bug.The plugin works but when I launch it 3 or 4 times, it crashes. Exception details:PaintDotNet.UnobservedErrorException: This result is an error, but it was not observed. Stack trace: (--- at PaintDotNet.Functional.ResultErrorData..ctor(Exception error, Boolean requireObservation) in D:\src\pdn\pdn_35x\src\Base\Functional\ResultErrorData.cs:line 110at PaintDotNet.Functional.Result.NewError(Exception error, Boolean requireObservation) in D:\src\pdn\pdn_35x\src\Base\Functional\Result.cs:line 42at PaintDotNet.Functional.Func.Try(Action f) in D:\src\pdn\pdn_35x\src\Base\Functional\Func.cs:line 126at PaintDotNet.Threading.ThreadDispatcher.<>c__DisplayClassa.<Enqueue>b__6() in D:\src\pdn\pdn_35x\src\Base\Threading\ThreadDispatcher.cs:line 132at PaintDotNet.Threading.ThreadDispatcher.ExecThreadLoop() in D:\src\pdn\pdn_35x\src\Base\Threading\ThreadDispatcher.cs:line 110at PaintDotNet.Threading.ThreadDispatcher.ExecThread() in D:\src\pdn\pdn_35x\src\Base\Threading\ThreadDispatcher.cs:line 65at System.Threading.ThreadHelper.ThreadStart_Context(Object state)at System.Threading.ExecutionContext.runTryCode(Object userData)at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)at System.Threading.ThreadHelper.ThreadStart()---) ---> PaintDotNet.WorkerThreadException: Worker thread threw an exception ---> System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.at PaintDotNet.Surface.set_Item(Int32 x, Int32 y, ColorBgra value) in D:\src\pdn\pdn_35x\src\Core\Surface.cs:line 770at StrayPixels.EffectPlugin.Render(EffectConfigToken parameters, RenderArgs dstArgs, RenderArgs srcArgs, Rectangle[] rois, Int32 startIndex, Int32 length)at PaintDotNet.Effects.Effect.Render(EffectConfigToken parameters, RenderArgs dstArgs, RenderArgs srcArgs, Rectangle[] rois) in D:\src\pdn\pdn_35x\src\Effects\Effect.cs:line 211at PaintDotNet.Effects.BackgroundEffectRenderer.ThreadFunction() in D:\src\pdn\pdn_35x\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:line 167 It looks like memory leak or something but I don't understand why since there is a garbage collector in C#. I've tried different thing but nothing work. Rectangle R; Surface SURF = null; List<Point> ToDelete = new List<Point>(); protected override void OnRender(Rectangle[] rois, int startIndex, int length) { Surface src = SrcArgs.Surface; R = SrcArgs.Surface.Bounds; SURF = new Surface(R.Size); SURF.CopySurface(SrcArgs.Surface); for (int y = R.Top; y < R.Bottom; y++) { for (int x = R.Left; x < R.Right; x++) { ToDelete.Clear(); if (testForDelete(x, y, 0, true, true, true, true)) { //If we need to delete the pixel SURF[x, y] = ColorBgra.Transparent; // we delete it foreach (Point p in ToDelete) //delete contiguous pixels { SURF[p.X, p.Y] = ColorBgra.Transparent; } } } } DstArgs.Surface.CopySurface(SURF); } //return true if the pixel has to be deleted bool testForDelete(int px, int py, int dist, bool h, bool b, bool g, bool d) { if (dist > propThresHoldSlider) return false; //too far from tested pixel if (px < R.Left || py < R.Top || px >= R.Right || py >= R.Bottom) return true; //out of screen if (SURF[px, py].A == 0) return true; // transparent bool ret = true; if (ret && h == true) ret = testForDelete(px, py - 1, dist + 1, true, false, g, d); if (ret && b == true) ret = testForDelete(px, py + 1, dist + 1, false, true, g, d); if (ret && g == true) ret = testForDelete(px - 1, py, dist + 1, h, b, true, false); if (ret && d == true) ret = testForDelete(px + 1, py, dist + 1, h, b, false, true); //if surrounded only by pixel that must be deleted and pixel that are transparent, return true. if (ret) ToDelete.Add(new Point(px, py)); return ret; } I've joined the full code. I hope you will help me solve my last problems straypixelremover.txt Quote Link to comment Share on other sites More sharing options...
Rick Brewster Posted June 3, 2013 Share Posted June 3, 2013 That's not a memory leak. You're trying to access pixels outside of the bounds of the surface that you're given. Your math is wrong somewhere. Notice how the callstack goes all the way back to your Render method. Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
archip Posted June 3, 2013 Author Share Posted June 3, 2013 Actually the program works well. The problem was that sometimes I launched the old version of the plugin... :S Thanks for your help, I'll make a release soon Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.