Jump to content

Delete stray pixel plugin


archip

Recommended Posts

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.

Link to comment
Share on other sites

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/

 

Red ochre Plugin pack.............. Diabolical Drawings ................Real Paintings

 

PdnForumSig2.jpg

Link to comment
Share on other sites

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.

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

Happy coding :-)

 

Have a look to PropertyBased Effect plugins. For the most plugins it is the easiest way to create a dialog.

midoras signature.gif

Link to comment
Share on other sites

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.

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

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;
        }


    }
}

 

 

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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 110
at PaintDotNet.Functional.Result.NewError(Exception error, Boolean requireObservation) in D:\src\pdn\pdn_35x\src\Base\Functional\Result.cs:line 42
at PaintDotNet.Functional.Func.Try(Action f) in D:\src\pdn\pdn_35x\src\Base\Functional\Func.cs:line 126
at PaintDotNet.Threading.ThreadDispatcher.<>c__DisplayClassa.<Enqueue>b__6() in D:\src\pdn\pdn_35x\src\Base\Threading\ThreadDispatcher.cs:line 132
at PaintDotNet.Threading.ThreadDispatcher.ExecThreadLoop() in D:\src\pdn\pdn_35x\src\Base\Threading\ThreadDispatcher.cs:line 110
at PaintDotNet.Threading.ThreadDispatcher.ExecThread() in D:\src\pdn\pdn_35x\src\Base\Threading\ThreadDispatcher.cs:line 65
at 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 770
at 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 211
at 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

Link to comment
Share on other sites

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.

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

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

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...