views:

1886

answers:

4

I am trying to write something in my Flex 3 application with actionscript that will take an image and when a user clicks a button, it will strip out all the white(ish) pixels and convert them to transparent, I say white(ish) because I have tried exactly white, but I get a lot of artifacts around the edges. I have gotten somewhat close using the following code:

targetBitmapData.threshold(sourceBitmapData, sourceBitmapData.rect, new Point(0,0), ">=", 0xFFf7f0f2, 0x00FFFFFF, 0xFFFFFFFF, true);

However, it also makes red or yellows disappear. Why is it doing this? I'm not exactly sure how to make this work. Is there another function that is better suited for my needs?

+1  A: 

A friend and I were trying to do this a while back for a project, and found writing an inline method that does this in ActionScript to be incredibly slow. You have to scan each pixel and do a computation against it, but doing it with PixelBender proved to be lightning fast (if you can use Flash 10, otherwise your stuck with slow AS).

The pixel bender code looks like:

input image4 src;
output float4 dst;

// How close of a match you want
parameter float threshold
<
  minValue:     0.0;
  maxValue:     1.0;
  defaultValue: 0.4;
>;

// Color you are matching against.
parameter float3 color
<
  defaultValue: float3(1.0, 1.0, 1.0);
>;

void evaluatePixel()
{
  float4 current = sampleNearest(src, outCoord());
  dst = float4((distance(current.rgb, color) < threshold) ? 0.0 : current);
}

If you need to do it in AS you can use something like:

function threshold(source:BitmapData, dest:BitmapData, color:uint, threshold:Number) {
  dest.lock();

  var x:uint, y:uint;
  for (y = 0; y < source.height; y++) {
    for (x = 0; x < source.width; x++) {
      var c1:uint = source.getPixel(x, y);
      var c2:uint = color;
      var rx:uint = Math.abs(((c1 & 0xff0000) >> 16) - ((c2 & 0xff0000) >> 16));
      var gx:uint = Math.abs(((c1 & 0xff00) >> 8) - ((c2 & 0xff00) >> 8));
      var bx:uint = Math.abs((c1 & 0xff) - (c2 & 0xff));

      var dist = Math.sqrt(rx*rx + gx*gx + bx*bx);

      if (dist <= threshold)
        dest.setPixel(x, y, 0x00ffffff);
      else
        dest.setPixel(x, y, c1);
    }
  }
  dest.unlock();
}
Adam
What are c1 and c2 in your actionscript example? color?
bkildow
Yes, sorry I moved this code from somewhere else and had to modify it a bit. It should be fixed now.
Adam
Also note there was a missing pair of lines after dest.setPixel
Adam
This is wonderful, thank you, thank you! The only thing I saw was in your actionscript example, I think the line: dest.setPixel(x, y, 0x00ffffff); should be dest.setPixel32(x, y, 0x00ffffff); to set the alpha channel.
bkildow
A: 

it looks like the above code would make a range of colors transparent.

pseudo-code:
    for each pixel in targetBitmapData
        if pixel's color is >= #FFF7F0F2
            change color to #00FFFFFF

something like this will never be perfect, because you will lose any light colors i would find an online color picker that you can use to see exactly what colors will be altered

Samuel
A: 

You can actually do it without pixelbender and real-time thanks to the inbuilt threshold function :

// Creates a new transparent BitmapData (in case the source is opaque)
var dest:BitmapData = new BitmapData(source.width,source.height,true,0x00000000);

// Copies the source pixels onto it
dest.draw(source);

// Replaces all the pixels greater than 0xf1f1f1 by transparent pixels
dest.threshold(source, source.rect, new Point(), ">", 0xfff1f1f1,0x00000000);

// And here you go ...  
addChild(new Bitmap(dest));
Theo.T
Oops, you must have edited your answer while I wrote this ...
Theo.T
A: 

The answer in 1 of pixel bender code:

dst = float4((distance(current.rgb, color) < threshold) ? 0.0 : current);

should be:

dst = (distance(current.rgb, color) < threshold) ? float4(0.0) : current;

or

if (distance(current.rgb, color) < threshold) dst = float4(0.0); else dst = float4(current);