views:

41

answers:

2

I have small function which will recolor pixels in a Bitmap from a given color to a new given color.

The problems I have with the code are as follows:

1) The function gives results which are remapping white pixels which should not be concidered since I have a threshold... (unless I have defined this calculation wrong)

2) When certain colors are given e.g. LimeGreen wierd results are seen in the image returned from the function (I beleive this is due to overflow of the byte type in the addition or subtraction case)

The base image I am using can be found here:

http://www.freeimagehosting.net/uploads/c8745a9de1.png

Results I have obtained can be found here:

freeimagehosting.net/uploads/fa48e5a0eb.png (Called with Color.Magenta as remapColor, Color.Red as newColor, Seems like white pixels are effected and the end of the gradient is not colored correctly)

freeimagehosting.net/uploads/8faec6a569.png (Called with Color.Magenta as remapColor, Color.Yellow as newColor, Seems like white pixels are effected and the end of the gradient is not colored correctly)

freeimagehosting.net/uploads/2efd4c04aa.png (Called with Color.Magenta as remapColor, Color.Blue as newColor, Seems like gradient not colored correctly)

freeimagehosting.net/uploads/defdf04e16.png (Called with Color.Magenta as remapColor, Color.Teal as newColor, Seems like white pixels are effected and none of the gradient is calculated correctly)

The function I have for this code is below: UPDATED per suggestions

public unsafe static Bitmap RecolorImage(Bitmap original, Color remapColor, Color newColor)
    {
        Bitmap result = new Bitmap(original.Width, original.Height);

        //lock the original bitmap in memory
        BitmapData originalData = original.LockBits(
           new Rectangle(0, 0, original.Width, original.Height),
           ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

        //lock the new bitmap in memory
        BitmapData newData = result.LockBits(
           new Rectangle(0, 0, original.Width, original.Height),
           ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);

        //set the number of bytes per pixel
        int pixelSize = 4;

        int rthreshold = 128;
        int gthreshold = 128;
        int bthreshold = 128;

        for (int y = 0; y < original.Height; y++)
        {
            //get the data from the original image
            byte* oRow = (byte*)originalData.Scan0 + (y * originalData.Stride);

            //get the data from the new image
            byte* nRow = (byte*)newData.Scan0 + (y * newData.Stride);

            for (int x = 0; x < original.Width; x++)
            {
                //examine the rgb values
                byte r = (byte)((oRow[x * pixelSize]));
                byte g = (byte)((oRow[x * pixelSize + 1]));
                byte b = (byte)((oRow[x * pixelSize + 2]));
                byte a = (byte)((oRow[x * pixelSize + 3]));

                if (a > 0 && 
                    Math.Abs(remapColor.R - r) <= rthreshold &&
                    Math.Abs(remapColor.B - b) <= bthreshold &&
                    Math.Abs(remapColor.G - g) <= gthreshold 
                    )
                {
                    if (newColor.R == 0)
                    {
                        r = 0;
                    }
                    else
                    {
                        if (newColor.R > remapColor.R)
                            r = (byte)(r - newColor.R);
                        else
                            r = (byte)(r + newColor.R);
                    }

                    if (newColor.G == 0)
                    {
                        g = 0;
                    }
                    else
                    {
                        if (newColor.G > remapColor.G)
                            g = (byte)(g - newColor.G);
                        else
                            g = (byte)(g + newColor.G);
                    }

                    if (newColor.B == 0)
                    {
                        b = 0;
                    }
                    else
                    {
                        if (newColor.B > remapColor.B)
                            b = (byte)(b - newColor.B);
                        else
                            b = (byte)(b + newColor.B);
                    }
                }


                //set the new image's pixel remaped pixel color
                nRow[x * pixelSize] = b; //B
                nRow[x * pixelSize + 1] = g; //G
                nRow[x * pixelSize + 2] = r; //R
                nRow[x * pixelSize + 3] = a; //A
            }
        }

        original.UnlockBits(originalData);
        result.UnlockBits(newData);


        return result;
    }        

What gives....

Is what I am trying to do possible?

Is it reliable?

Is there just a bug in my code?

Is there a better way to achive this "re-mapable technique" on bitmaps using gradients?

Thank you for your time.

A: 

It looks like your threshold test is incorrect. Take the line:

remapColor.R - r <= rthreshold

If the current pixel is white, then r will be 255, and the test will always be true, no matter what remapColor.R and rthreshold are.

I think Math.Abs(remapColor.R - r) might work.

And you're likely correct about your byte values being out of bounds. Fixing the threshold test might stop that from happening. Otherwise, try putting some bounds checking in to see where it's happening.

Andy
Hey,Thank you for the advice however it seems that it did not work as intended.Here is the output of the code after making the suggested changes.http://www.freeimagehosting.net/uploads/d5ea228622.pngCalled with Color.Magenta as the remapColor and Color.Red as the newColorAm I going about this the wrong way?
Jay
A: 

I have decided that although this may be possible if I study the various materials regarding color spaces and their supporting theories. It seems that this will take a bit more than some quick threshold calculation and normalization to the remapColor.

I am going to propose that instead of performing this type of modification on a raster bitmap image that the graphics be modified in their vector form.

The process should be something like this:

The graphics are created in whatever imaging suite the designer is working in.

They are saved to a vector format e.g. SVG this will allow the customizable paths to be named, traversed and altered programmatically (and for more than color if needed) with SVG Rendering Engine(http://svg.codeplex.com/)

With this solution we can either output the SVG direct to the browser if supported and do the modifications directly on the client or use the server and output as PNG when needed.

I feel that this arrangement will provide us with more flexibility and a more robust solution than what I was initially going to hack together.

Thank you guys for your time!

Jay