tags:

views:

65

answers:

2

I'm currently writing an image editing application using Flex and I'm trying to make a blemish removal functionality like in picnik.com.

I have searched everywhere but couldn't find anything. What is the best way that's suitable for this?

I have tried to just blur away the blemishes, but the results are bad.

A: 

You didn't get any other answers, so I'll give it a try. I don't know anything about flex, but this should probably be doable:

1) The user selects an area (For argument's sake it is a circle)

2) You average the color values of the edges of the circle and store that color.

3) Do a Gaussian blend between the image and the calculated average color of the perimeter. That is, blend the center of the circle much more than the edges. The amount of blending should fade to nothing as you get closer to the edge. This should keep you from getting faint circle shapes on your image. This will also help prevent darkening as the blemish color will not be part of the averaging process.

Good Luck!

Edit

Here is some psudocode on the blending operation.

for (each pixel)
{
   blendingConstant = [A number between zero and one which is close 
   to 1.0 near the edge of the circle, and approaches a threshold 
   value the closer you get to the center];

   pixelColor = (blendingConstant * pixelColor + ( 1.0 - blendingConstant  ) * averagePerimiterColor);
}

The threshold value may be zero, it may not. You will have to play with it until it looks right.

Andres
Well, I already got the colors at the perimeter, but I don't know how to do the blending. Is there some algorithm or pseudocode for it?
See my edit. Hopefully that helps. Let me know if you need more details or if it doesn't work.
Andres
+1  A: 
function removeblemish(e:MouseEvent):void

{

var radius = 16;

var mX:Number = e.localX;
var mY:Number = e.localY;

var ar = 0, ag = 0, ab = 0;
var cnt = 0;

for(var i:int = 0; i < 360; i++)
{
    var radians:Number = i *  Math.PI / 180.0;

    var posX:Number = Math.floor(radius * Math.cos(radians)) + mX;
    var posY:Number = Math.floor(radius * Math.sin(radians)) + mY;

    if(posX >= 0 && posX < newBitmap.width && posY >= 0 && posY < newBitmap.height)
    {
        var pixel:uint = newBitmap.getPixel32(posX, posY);
        ar += (pixel >> 16) & 0xFF;
        ag += (pixel >> 8) & 0xFF;
        ab += (pixel >> 0) & 0xFF;
        cnt++
    }
}

ar = Math.floor(ar / cnt) % 255;
ag = Math.floor(ag / cnt) % 255;
ab = Math.floor(ab / cnt) % 255;

var threshold:Number = 0.75;

for(var i:int = 0; i <= radius; i++)
{
    for(var j:int = 0; j < 360; j++)
    {
        var radians:Number = j *  Math.PI / 180.0;

        var posX:Number = Math.floor(i * Math.cos(radians)) + mX;
        var posY:Number = Math.floor(i * Math.sin(radians)) + mY;

        if(posX >= 0 && posX < newBitmap.width && posY >= 0 && posY < newBitmap.height)
        {
            var blend = threshold + (i * 1.0 / radius) * (1.0 - threshold);

            var pixel:uint = newBitmap.getPixel32(posX, posY);
            var cr = (pixel >> 16) & 0xFF;
            var cg = (pixel >> 8) & 0xFF;
            var cb = (pixel >> 0) & 0xFF;

            cr = Math.floor(blend * cr + (1.0 - blend) * ar) % 255;
            cg = Math.floor(blend * cg + (1.0 - blend) * ag) % 255;
            cb = Math.floor(blend * cb + (1.0 - blend) * ab) % 255;

            newBitmap.setPixel32(posX, posY, (255 << 24) | (cr << 16) | (cg << 8) | cb);
        }
    }
}

}

That works! Thank you! his is what I got... But still there are some faint circles if the radius got smaller... Is there anyway to improve it?

How small are the circles in pixels? Does the blemish overlap the perimeter?
Andres
BTW, there must be a better way of iterating through the pixels in your circle. Your bugs could be caused by this. Going between 0 and 360 is not only a waste of cycles, it also disrupts the math. Try iterating between (mx +- radius) and (my +- radius). Then do a test to see if the pixel is within the circle or on the perimeter depending on the loop.
Andres