Recommended Posts

This is a plugin intended to help remove the backgrounds from images. I wasn't sure where to put it, but decided to make it an Adjustment.

 

The interface is:

 

                                      HSVEraserInterface_zpsmaz7ao1o.png

 

Match Color is normally the color to be erased, but it can optionally be the color to keep. It can be either the User Match Color, or the Primary or Secondary Color.

User Match Color is the Match Color when User Match Color is specified.

Hue Tolerance determines how closely the pixel's hue must match the Match Color's hue.

Saturation Tolerance determines how closely the pixel's saturation must match the Match Color's saturation.

Value Tolerance determines how closely the pixel's value must match the Match Color's value.

RGB Tolerance specifies the maximum allowed Euclidean distance between the pixel's RGB color and the Match Color's RGB color.

Gray Upper Limit (S×V) specifies the threshold for product of the saturation and value below which the Match Color or pixel's color will be classified as gray. Gray does not match any hue except gray, unless the Gray Matches All Hues option is selected.

Portion of Non-Erased Color to Preserve determines how much of the "non-erased" color should remain in the pixel. The color and alpha of the pixel are adjusted to account for the degree to which the pixel color matches the Match Color. Specifically (assuming the Match Color is being erased), the color is adjusted so that the alpha is as small as possible while keeping the same color if the pixel is alpha-blended into a Match Color background layer. This can be used to achieve a softer edge. Though its not really intended to be used when the Erase Non-Matching Pixels option is selected, it does work. The erased pixels will all be the Match Color, with appropriate alphas. The value can range from 0, to entirely erase the matching pixel, to 1, to preserve as much of the pixel's color as possible. Normally, it will probably be either 0 or 1.

Gray Matches All Hues specifies that colors classified as gray match all hues.

Erase Non-Matching Pixels specifies that matching pixels are kept and non-matching pixels are erased.

 

Here is the source:

Hidden Content:
// Author: MJW
// Name: HSV Eraser
// Title: HSV Eraser
// Desc: Erase pixels of selected HSV value.
// Keywords: HSV erase

#region UICode
byte Amount1 = 0; // Match Color|User Match Color|Primary Color|Secondary Color
ColorBgra Amount2 = ColorBgra.FromBgr(0,0,0); // User Match Color
double Amount3 = 0.1; // [0,1] Hue Tolerance
double Amount4 = 1; // [0,1] Saturation Tolerance
double Amount5 = 1; // [0,1] Value Tolerance
double Amount6 = 1; // [0,1] RGB Tolerance
double Amount7 = 0.15; // [0,1] Gray Upper Limit (S×V)
double Amount8 = 0; // [0,1] Portion of Non-Erased Color to Preserve
bool Amount9 = false; // [0,1] Gray Matches All Hues
bool Amount10 = false; // [0,1] Erase Non-Matching Pixels
#endregion

ColorBgra matchColor;
double matchB, matchG, matchR;
double preserve;

// Here is the main render loop function
void Render(Surface dst, Surface src, Rectangle rect)
{
    const double rgbDistScale = 3.0 * 255.0 * 255.0;
    int miB, miG, miR;
    double h, s, v;
    int dist2RGB;
    double matchH, matchS, matchV, tolH, tolS, tolV, limitGray;
    int tol2RGB;
    bool matchIsGray, pixelIsGray;
    bool grayMatchesAll = Amount9;
    bool invertErase = Amount10;
    preserve = Amount8;
    bool preservePortion = (preserve != 0.0);
     
    // Get hue to erase.
    if (Amount1 == 0)
        matchColor = Amount2;
    else if (Amount1 == 1)
        matchColor = (ColorBgra)EnvironmentParameters.PrimaryColor;
    else
        matchColor = (ColorBgra)EnvironmentParameters.SecondaryColor;
    matchB = miB = matchColor.B;
    matchG = miG = matchColor.G;
    matchR = miR = matchColor.R;
    RGBtoHSV(ref matchColor, out matchH, out matchS, out matchV);
    
    limitGray = Amount7;
    matchIsGray = (matchV * matchS <= limitGray);
    tolH = 0.5 * Amount3;
    tolS = Amount4;
    tolV = Amount5;
    tol2RGB = (int)(rgbDistScale * Amount6 * Amount6 + 0.5);
    
    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        if (IsCancelRequested) return;
        for (int x = rect.Left; x < rect.Right; x++)
        {
            ColorBgra pixel = src[x, y];
            if (pixel.A == 0)
            {
                // Just pass through transparent pixels
                dst[x, y] = pixel;
                continue;
            }
            
            RGBtoHSV(ref pixel, out h, out s, out v);
            pixelIsGray = (s * v <= limitGray);
            bool hueMatches;
            if (pixelIsGray || matchIsGray)
            {
                hueMatches = grayMatchesAll || (pixelIsGray && matchIsGray);
            }
            else
            {
                double distH = Math.Abs(h - matchH);
                if (distH > 0.5)    // Handle color wheel wrap-around.
                    distH = 1.0 - distH;
                hueMatches = (distH <= tolH);    
            }
         
            // Also do an RGB comparison, since this is sometimes better than HSV.
            int distB = miB - pixel.B, distG = miG - pixel.G, distR = miR - pixel.R;
            dist2RGB = distB * distB + distG * distG + distR * distR;
            
            // See if pixels should be erased (or partially erased)
            if (invertErase ^ (hueMatches &&
                (Math.Abs(s - matchS) <= tolS) &&
                (Math.Abs(v - matchV) <= tolV) &&
                (dist2RGB <= tol2RGB)))
            {
                if (!preservePortion)
                {
                    // Just erase the pixel.
                    pixel = ColorBgra.Transparent;
                }
                else
                {
                    // Preserve the non-erased portion of the pixel.
                    if (!invertErase)
                    {
                        pixel = RemoveColor(ref pixel);
                    }
                    else
                    {
                        // Handle the somewhat odd case.
                        // Preserve the non-erased portion of the pixel when the
                        // color to be erased is the non-matching color.
                        // The remaining color will be the match color with a modified
                        // alpha.
                        pixel = KeepColor(ref pixel);
                    }
                }
            }

            dst[x, y] = pixel;
        }
    }
}

// Get the color with the minimum alpha such that the source color can be
// produced by alpha blending with the subtracted color.
public ColorBgra RemoveColor(ref ColorBgra srcColor)
{
    double srcB = srcColor.B, srcG = srcColor.G, srcR = srcColor.R;
    double alpha = RemovedAlpha(srcB, srcG, srcR);
                        
    if (alpha == 0.0)
    {
       return ColorBgra.TransparentBlack;
    }
    else
    {
        double aRecip = 1.0 / alpha;
        int b = (int)(aRecip * (srcB - matchB) + matchB + 0.5);
        int g = (int)(aRecip * (srcG - matchG) + matchG + 0.5);
        int r = (int)(aRecip * (srcR - matchR) + matchR + 0.5);
        int a = (int)(alpha * preserve * (double)srcColor.A + 0.5);
        return ColorBgra.FromBgra((byte)b, (byte)g, (byte)r, (byte)a);
    }
}

ColorBgra KeepColor(ref ColorBgra srcColor)
{
     double srcB = srcColor.B, srcG = srcColor.G, srcR = srcColor.R;
     double alpha = RemovedAlpha(srcB, srcG, srcR);
                                       
     // The pixel will be the erase color, and alpha will be
     // the complimentary alpha of the non-inverted case.
     byte a = (byte)(preserve * (1.0 - alpha) * (double)srcColor.A + 0.5);
     return matchColor.NewAlpha((byte)a);
}

double RemovedAlpha(double srcB, double srcG, double srcR)
{
    return Max3(MinAlpha(srcB, matchB),
                MinAlpha(srcG, matchG),
                MinAlpha(srcR, matchR));

}

// Get the minimum alpha (0 <= alpha <= 1) such that the color component
// can be written as c = subC + alpha * (srcC - subC).
double MinAlpha(double srcC, double subC)
{
    if (srcC == subC)
    {
        return 0.0;
    }
    else if (srcC < subC)
    {
        return (subC - srcC) / subC;
    }
    else    // (srcC > subC)
    {
        return (srcC - subC) / (255.0 - subC);
    }
}

public void RGBtoHSV(ref ColorBgra color, out double H, out double S, out double V)
{
    RGBtoHSV(color.R, color.G, color.B, out H, out S, out V);
}

public void RGBtoHSV(int R, int G, int B, out double outH, out double outS, out double outV)
{
    const double H_UNDEFINED = 0.0;    // Arbitrarily set undefined hue to 0
    const double recip6 = 1.0 / 6.0;
    
    // R, G, and B must range from 0 to 255
    // Ouput value ranges:
    // outH - 0.0 to 1.0
    // outS - 0.0 to 1.0
    // outV - 0.0 to 1.0

    double dR = (double)R / 255.0;
    double dG = (double)G / 255.0;
    double dB = (double)B / 255.0;
    double dmaxRGB = Max3(dR, dG, dB);
    double dminRGB = Min3(dR, dG, dB);
    double delta = dmaxRGB - dminRGB;

    // Set value
    outV = dmaxRGB;

    // Handle special case of V = 0 (black)
    if (dmaxRGB == 0)
    {
        outH = H_UNDEFINED;
        outS = 0.0;
        return;
    }

    // Handle special case of S = 0 (gray)
    outS = delta / dmaxRGB;
    if (dmaxRGB == dminRGB)
    {
        outH = H_UNDEFINED;
        return;
    }

    // Finally, compute hue
    if (dR == dmaxRGB)
    {
        outH = (dG - dB) / delta;
    }
    else if (dG == dmaxRGB)
    {
        outH = 2.0 + (dB - dR) / delta;
    }
    else //if (dB == dmaxRGB)
    {
        outH = 4.0 + (dR - dG) / delta;
    }

    outH *= recip6;
    outH = HueConstrain(outH);
}

public double Max3(double x, double y, double z)
{
    return (x > y) ? ((x > z) ? x : z) : ((y > z) ? y : z);
}

public double Min3(double x, double y, double z)
{
    return (x < y) ? ((x < z) ? x : z) : ((y < z) ? y : z);
}

public double HueConstrain(double MyHue)
{
    // Makes sure that 0.0 <= MyAngle < 1.0
    // Wraps around the value if its outside this range
    while (MyHue >= 1.0)
    {
        MyHue -= 1.0;
    }
    while (MyHue < 0.0)
    {
        MyHue += 1.0;
    }
    return MyHue;
}






 

Here is the icon: post-53337-0-44648700-1432707367.png

 

Here is the plugin: HsvEraser.zip

 

I will happily consider any suggestions to make the interface clearer. I will also appreciate any spelling corrections.

 

EDIT: Fixed several bugs. Changed some of the wording in the interface. Added the RGB Tolerance.  Changed the version to 1.1.

Edited by MJW
  • Upvote 4

Share this post


Link to post
Share on other sites

I haven't tested the plugin out yet, but out of curiosity, is this sort of like an advanced version of Grim Color Reaper and Cut Color? It sounds like a color removing plugin that's been mixed with BoltBait's Hue/Saturation+ plugin, which is a pretty cool idea. :)

 

The adjustments remind me of Hue/Saturation+, but instead of re-coloring things, it removes(erases) colors.

Edited by Cc4FuzzyHuggles

Share this post


Link to post
Share on other sites

The adjustments remind me of Hue/Saturation+, but instead of re-coloring things, it removes(erases) colors.

My Hue/Saturation+ can erase colors by adjusting the alpha slider all the way to the left.

Share this post


Link to post
Share on other sites

The "partial erase" feature is similar to Grim Reaper's, but I think there are significant differences between the two plugins. Grim Reaper (like, I assume, the Magic Wand) uses the  RGB Euclidean distance to measure how close the colors are. For many purposes, I think HSV ranges often work better for removing backgrounds. Also, Grim Reaper reduces alpha based on the similarity of the colors. Once my program recognizes a pixel as matching, it reduces the alpha as far as possible while still producing the original color when alpha blending to the match color. Grim Reaper won't always produce the smallest possible alpha, and I'm pretty sure it won't always preserve the original color, since sometimes the adjusted RGB values will be clamped.

 

When used with Erase Non-Matching Pixels, my plugin is similar to Color Cut, but much more flexible.

 

I admit, if I'd realized that Hue/Saturation+ can erase background hues as well as it does, I wouldn't have written this plugin, which started out as Hue Eraser. Nevertheless, my plugin does have several useful additional features. First, it also allows the pixel's value to be considered; second, it has the Grim Reaperesque "partial erase"; third, it optionally allows the matching pixels to be kept; fourth, it allows choice of the primary and secondary colors as the matching color. The last may seem trivial, but it makes it easier to use the plugin with the color picker tool.

Edited by MJW

Share this post


Link to post
Share on other sites

My Hue/Saturation+ can erase colors by adjusting the alpha slider all the way to the left.

 

That's so cool! Thanks for enlightening me of that function (I apparently had an outdated version of the plugin). However, MJW's plugin still seems nicer for removing colors since it has options and adjustments specifically made for removing color, and it has the ability to work with paint.net's primary and secondary colors, which for example, when using GrimColor or CutColor, is a SUPER handy feature. I wouldn't mind seeing a primary/secondary color option in hue/sat+, as that would help make re-coloring images easier too, but even if it did gain that feature, it still is mainly a color changing plugin in my opinion. But, I will be adding both plugins to my personal list of "good ways to cut out images/remove backgrounds". :)

Edited by Cc4FuzzyHuggles

Share this post


Link to post
Share on other sites

I think I've fixed most of the bugs. I also added an RGB distance tolerance, since sometimes that's useful, either alone or combined with the HSV tolerance.

Share this post


Link to post
Share on other sites

I haven't been able to compile it for my PdN 3.5.11 because it throws me an error that tells me that TransparentBlack is not defined in PaintDotNet.ColorBgra

 

It happens in the function RemoveColor right where it reads return ColorBgra.TransparentBlack;

 

I suppose it's due to the old version of Codelab, but since I'm not a coder I can't be sure.

Share this post


Link to post
Share on other sites

You could use:

return ColorBgra.FromBgra(0,0,0,0);
Same thing.

Share this post


Link to post
Share on other sites

Maximilian, does it get an error if you change it to ColorBgra.Transparent? If not, I'll use that in the future, since all I really want to do is make the pixel transparent. BoltBait's solution will, of course, also work, but I'd prefer to use ColorBgra.Transparent if it works for older versions.

Edited by MJW

Share this post


Link to post
Share on other sites

Thank you both!

However, ...

 

Maximilian, does it get an error if you change it to ColorBgra.Transparent?

 

It compiled correctly with this change, but now when I run the plugin I get the following error:

 

 

File: C:\Archivos de programa\Paint.NET\Effects\HsvEraser.dll
      Name: HsvEraserEffect.HsvEraserEffectPlugin
      Version: 1.0.5627.35397
      Author: MJW
      Copyright: Copyright © MJW
      Website: http://www.getpaint.net/redirect/plugins.html
      Full error message: System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: defaultValue > maxValue
   at PaintDotNet.PropertySystem.ScalarProperty`1..ctor(Object name, T defaultValue, T minValue, T maxValue, Boolean readOnly, ValueValidationFailureResult vvfResult) in D:\src\pdn\pdn_3_5_11\src\Base\PropertySystem\ScalarProperty`1.cs:line 136
   at PaintDotNet.PropertySystem.DoubleProperty..ctor(Object name, Double defaultValue, Double minValue, Double maxValue) in D:\src\pdn\pdn_3_5_11\src\Base\PropertySystem\DoubleProperty.cs:line 27
   at HsvEraserEffect.HsvEraserEffectPlugin.OnCreatePropertyCollection()
   at PaintDotNet.Effects.PropertyBasedEffect.CreateConfigDialog() in D:\src\pdn\pdn_3_5_11\src\Effects\PropertyBasedEffect.cs:line 86
   at PaintDotNet.Menus.EffectMenuBase.RunEffectImpl(Type effectType) in D:\src\pdn\pdn_3_5_11\src\PaintDotNet\Menus\EffectMenuBase.cs:line 799

 

Any ideas?

Share this post


Link to post
Share on other sites

Yeah, I have an idea...

Upgrade to paint.net 4.0+ :P

If you can't do that, try changing the line:

 

double Amount7 = 0.15; // [0,1] Gray Upper Limit (S×V)
to

 

double Amount7 = 0.0; // [0,1] Gray Upper Limit (S×V)
This is due to a bug in CodeLab or Paint.NET 3.5x (not sure which).

Share this post


Link to post
Share on other sites

Back with a review! yes2.gif

At times I do models on Sculptris to later on edit them for some PdN piece and the background gives me some trouble. I'm aware I have other choices to pick from, but Sculptris is lightweight and relatively easy to handle for someone with budding 3D-sculpting skills and an outdated computer, despite its several shortcomings. To name one, I can save a capture as a transparent png, which is not exactly the case because in fact the background is saved as black and what I do want is full transparency. So, my seemingly only chance is to save the capture with the program's interface's background included, which is a sort of dark gray to black gradient, and then see how I manage to remove it. In the following example I couldn't seem to adjust the magic wand's tolerance to a value that wouldn't mutilate parts of the creature, especially below the left armpit and above the left side of the head. The solution came from picking a color in the vicinity of the difficult area and playing with Value Tolerance and Gray Upper Limit, leaving all other settings at defaults. I'm glad I had this plugin and glad I discovered how to use it for this particular case JC_cheers.gif

 

Follows a side-to-side view of the non-transparent original capture and the result after removing the somewhat tricky gradient background:

 

trooper_non_transparent_and_transparent_

Share this post


Link to post
Share on other sites

Thank you very much for the review, Maximilian. That is quite a challenging image, since the background is so similar to the foreground. I gave it a try myself, and it was quite interesting. I fairly easily removed most of the background, but there remained ragged horizontal stripes. I was able to remove them a few at a time by using the color picker to select the color, then running the HSV Eraser. It sort of made me wish I'd made it an effect instead of an Adjustment, so that I could have used "Repeat HSV Eraser." I think a black background would be easier to eliminate, assuming no foreground edge pixels are black. If you can generate the same image against different backgrounds in Sculptris, you could use the difference of the images to eliminate the background.

Share this post


Link to post
Share on other sites

That is quite a challenging image, since the background is so similar to the foreground. I gave it a try myself, and it was quite interesting. I fairly easily removed most of the background, but there remained ragged horizontal stripes.

 

I've seen better outcomes by applying the effect to specific selections, because at times the color one wants to get rid of may also show up in parts of the picture one needs to preserve, therefore rendering undesirable results. At least that's what I've experienced in this particular image on which I've done my tests. Of course it all depends on the specific characteristics of each piece.

 

 

I was able to remove them a few at a time by using the color picker to select the color, then running the HSV Eraser. It sort of made me wish I'd made it an effect instead of an Adjustment, so that I could have used "Repeat HSV Eraser."

 

It would be a very useful thing in matters of speeding up the workflow, as it may be necessary to repeat the operation in succession, especially when working on different selections like I suggested above.

 

 

If you can generate the same image against different backgrounds in Sculptris, you could use the difference of the images to eliminate the background.

 

Thanks a lot for this clue that should have occurred to me in the first place, but obviously didn't ignat_01.gif I tried it and it works wonders! The whole process would get faster with the aid of such generated mask if I had to process a good number of images. Gosh, one may have so much to learn yet! scratch_one-s_head.gif

Share this post


Link to post
Share on other sites

A method I find useful when cutting out backgrounds (which was mentioned by someone else recently) is to eliminate from consideration all but the edge of the foreground image by using the Paintbrush. The idea is the put a new semi-transparent layer on top, then paint over the foreground, almost up to the edge. It's pretty easy to do. It's a good idea to release the cursor button reasonably often so undoing a mistake doesn't undo everything up till then (though mistakes can also be fixed with the Eraser). Once the inside edge has been painted, the interior can be filled with the Paint Bucket. The unpainted area can then be selected and used as a mask to eliminate most of the foreground image. If you have a steady hand, well-defined smooth edges can often be painted well enough that no other form of background removal is necessary. I find it works much better than the Lasso tool. For hair, fur, and other rough edges like that, something like the HSV Eraser can be used to eliminate the background pixels near the edge. A nice feature of the painted mask is that it can be blurred, so the Magic Wand tolerance allows the mask to be expanded or contracted.

 

EDIT: Something that just occurred to me, which I haven't tried but seems like a good idea, is to make a new layer that just contains the part of the image that was painted over. Then edit the original image with no selections except those to limit the background elimination to specific regions, not worrying too much about interior foreground pixels. Once the edge has been cleaned up, overlay the saved cutout interior, so that any lost interior pixels will be restored.

 

ANOTHER EDIT: Or perhaps make the cutout-section layer visible, and behind the original image that will be edited. Then any erasures of non-edge foreground pixels won't even be visible during the edit. It will be as if only background and the edge foreground pixels can be edited. Perhaps the mask could even be expanded with a blur to generate a selection that's outside the foreground image, which could then be used to eliminate all but the edge background pixels. I'm not sure if that would make it easier or harder to remove the background.

Edited by MJW
  • Upvote 1

Share this post


Link to post
Share on other sites

These are really neat ideas I'll try out soon. I'll have to take a good note of them all. Thanks for taking the time to write all that!

 

The difference mask and/or this plugin and the magic wand have proved good enough for what I'm working on at the moment, but then there's the tricky matter of hair that always causes trouble. At one time I tried some of that and the results were unsatisfactory, like I lost threads of hair in the resulting picture, therefore losing the original's realism. Then again, I didn't have this plugin back then, for which reason I'm interested in giving the matter another try.

Share this post


Link to post
Share on other sites

Yesterday, just for the heck of it, I tried removing the background from a picture of Humphrey Bogart posted on a thread about cutting out backgrounds. I'd forgotten how useful the Erase tool can be when the edge is smooth. Painting a mask layer is better for more complex situations, but for simple edges, just erasing the background is quick and quite easy. Generally, it works best to use a reasonably large brush size. The HSV Eraser was mostly written to handle tricky situations like hair and fur. Those are difficult because the background and foreground are mixed together, so many of the pixels are half background and half foreground. If the colors are similar, then those pixels can usually be left unchanged and later feathered. If the colors contrast, then hopefully the HSV Eraser's partial-erase feature can be used to eliminate the background color while leaving the foreground color.

 

(Of course, if you can generate different backgrounds for the same image, that makes things a lot simpler, and is far better than erasing or painting a mask.)

Share this post


Link to post
Share on other sites

The peculiar features of my eyes make me choose dark colors for most things and when I do the captures on Sculptris I have the dark backgrounds on one hand and a dark main object on the other, which at times makes things harder when I have to create the difference mask because both the mask and the background appear dark at first sight. What I've been doing lately is find a way to highlight the main object while preserving the darkness of the backgrounds, for example by doing a preliminary Additive  blending before doing the Difference thing.   This way, the resulting mask looks more visible, or so I feel it. Perhaps all this is way too obvious, but anyway I thought I'd say it. Sometimes one gets overexcited to achieve a final result and may overlook basic procedures that might eventually improve such result :P

Share this post


Link to post
Share on other sites

I think it would be quite easy to write a  little plugin that would erase the matching or non-matcing colors for two images, if that would be useful.

Share this post


Link to post
Share on other sites

If you post an example of the same image with different backgrounds, I'll see what I can do.

Edited by MJW

Share this post


Link to post
Share on other sites

This is a random sculpture on a black background, and the same random sculpture on a dark gray gradient background.

 

However, it seems I have found yet another workaround. Yesterday, Red ochre made me notice the possibility to change the Sculptris interface background to an image of one's choice, which of course I should have noticed by myself but was too silly for such a simple find ignat_01.gif Anyway, I made a light blue image and set it as the interface background, which appears to contrast nicely with the mostly darkish materials I use when sculpting. This should really simplify the process of cutting backgrounds.

 

It would nonetheless be interesting to see such a plugin as you suggest. I suppose it may be helpful for other situations, but only if you have time to put on it. No hurries really good.gif

Share this post


Link to post
Share on other sites

Here's the source for a little plugin to erase the mismatches between two images:

 

Hidden Content:
// Author: MJW
// Name: Mismatch Eraser
// Title: Mismatch Eraser
// Submenu: color
// Desc: Erase mismatched pixels between clipboard and image
// Keywords: erase mismatch clipboard
#region UICode
#endregion

// Here is the main render loop function
void Render(Surface dst, Surface src, Rectangle rect)
{
    bool haveCbImage = (img != null) && (src.Width == img.Width) && (src.Height == img.Height);
    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        if (IsCancelRequested) return;
        for (int x = rect.Left; x < rect.Right; x++)
        {
            ColorBgra CbPixel;
            ColorBgra CurrentPixel = src[x, y];
            if (IsCancelRequested) return;
            // If clipboard has an image, get it. Check every time.
            if (haveCbImage)
                haveCbImage = (img != null);          
            CbPixel = haveCbImage ? img[x, y] : CurrentPixel;
            dst[x, y] = (CurrentPixel == CbPixel) ? CurrentPixel : ColorBgra.Transparent;
        }
    }
}

// Setup for getting an image from the clipboard
protected Surface img
{
    get
    {
        if (_img != null)
            return _img;
        else
        {
            System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(GetImageFromClipboard));
            t.SetApartmentState(System.Threading.ApartmentState.STA);
            t.Start();
            t.Join();
            return _img;
        }
    }
}
private Surface _img = null;
private void GetImageFromClipboard()
{
    Bitmap aimg = null;
    System.Windows.Forms.IDataObject clippy;
    try
    {
        clippy = System.Windows.Forms.Clipboard.GetDataObject();
        if (clippy != null)
        {
            if (System.Windows.Forms.Clipboard.ContainsData("PNG"))
            {
                // Handle bitmaps with transparency
                Object png_object = System.Windows.Forms.Clipboard.GetData("PNG");
                if (png_object is System.IO.MemoryStream)
                {
                    System.IO.MemoryStream png_stream = png_object as System.IO.MemoryStream;
                    aimg = (Bitmap)Image.FromStream(png_stream);
                }
            }
            else if (clippy.GetDataPresent(System.Windows.Forms.DataFormats.Bitmap))
            {
                // If that didn't work, try bitmaps without transparency
                aimg = (Bitmap)clippy.GetData(typeof(System.Drawing.Bitmap));
            }
        }
    }
    catch (Exception)
    {
    }
    if (aimg != null)
    {
        _img = Surface.CopyFromBitmap(aimg);
    }
    else
    {
        _img = null;
    }
}

 

Here's a zip file of the effect: MismatchEraser.zip

Here's an icon (yes, I actually made one for this effect): post-53337-0-76853500-1434156377.png

 

The plugin is in the Effects>Color menu. Load one image into PDN, and copy the other into the clipboard. The images have to be the same size. The plugin isn't fancy. If the pixel doesn't match, it just erases it, with no attempt at antialiasing.

 

Edited by MJW
  • Upvote 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now