You love Chroma Key, don’t you? As you know, this method is used to replace a background color with what you want. Today this technique is used everywhere also where you don’t expect. Have a look at the video below.

So, let’s see how to implement the Chroma Key in Adobe Flash. In my demo, I used a video object linked to the camera. Also, you’ll see that I used the black as chroma key color. But as you know, in a real application we will use a blue color instead (do you know blue screen?) or a green color (Pantone 354). Further, you’ll notice that the values uint are in the format 0xAARRGGBB, where AA is the alpha channel used for transparency.

In order to process the image, I used the BitmapData threshold method. This is a very powerful method. It is easy to use as well. It can check every pixel, one to one, with a mask in order to verify if the colors are equals, greats than or less than and replace them with a specified color:

public function threshold(sourceBitmapData:BitmapData, 
  sourceRect:Rectangle, 
  destPoint:Point, 
  operation:String, 
  threshold:uint, 
  color:uint = 0, 
  mask:uint = 0xFFFFFFFF, 
  copySource:Boolean = false):uint
  • sourceBitmapData the source bitmap. You can use a different source
  • sourceRect an instance of Rectangle class used as source size delimiter
  • destPoint an instance of Point class used as destination coordinate, usually 0,0
  • operation this is a string that define the operation to apply on every pixel (see below for detail) “<“, “<=”, “>”, “>=”, “==”, “!=”
  • threshold is a unsigned int, used in compare, for example 0x00FF0000
  • color is a unsigned int. This is the color that will be replaced when the compared is successfully
  • mask is a unsigned int. This is the mask to apply to threshold and source bitmap pixels
  • copySource is a boolean used to copy the original value when compared fail

This method returns the number of pixels changed. Now, let me show you the steps of this method:

1. Start from sourceRect
2. Read the first-pixel value from sourceBitmap and apply mask value on it. A logical AND is made: ( readPixel & mask )
3. The result value is compared with ( threshold & mask ) using the operation
4. If the result is true, then starting from destPoint that pixel is set to color
5. If the result is false, then nothing happen

In the demo you’ll see 4 boxes with camera image:

chromakey

In the first box (top left), you’ll see the original video capture. It is as it is in the Camera object.

var cam:Camera = Camera.getCamera();
vid_video.attachCamera( cam );

Below (bottom left) you’ll see the Threshold box:

// Threshold
var simpleBitmapData:BitmapData = new BitmapData( vid_video.width, vid_video.height, true);
var simpleBitmap:Bitmap = new Bitmap(simpleBitmapData);
addChild(simpleBitmap);

And then:

addEventListener( Event.ENTER_FRAME,
  function (e:Event):void {
    simpleBitmap.bitmapData.draw( vid_video );
    simpleBitmap.bitmapData.threshold(simpleBitmap.bitmapData, 
      new Rectangle(0, 0, simpleBitmap.bitmapData.width, simpleBitmap.bitmapData.height), 
        new Point(0,0), 
        "==", 
        0x00000000, 
        0x00000000, 
        0x00FFFFFF, 
        true );
  }
);

As you can see we got a simple chroma key for black color. Beware because a color value as 0x00000001 will be ignored! To fix it, we have to use a different value and operator, for example:

addEventListener( Event.ENTER_FRAME,
  function (e:Event):void {
    simpleBitmap.bitmapData.draw( vid_video );

    simpleBitmap.bitmapData.threshold(simpleBitmap.bitmapData, 
      new Rectangle(0, 0, simpleBitmap.bitmapData.width, simpleBitmap.bitmapData.height), 
        new Point(0,0), 
        "<=", 
        0x00222222, 
        0x00000000, 
        0x00FFFFFF, 
        true );
  }
);

The code above uses the <=operator. However, we have an issue again! In fact, a color value as 0x00002200 (dark green) will get as chroma color because we are using <= 0x00222222. To fix that, we have to create a special function in order to get only black and gray shades:

function applyThresholdRGB( myBitmapData:BitmapData, channel:String, op:String, threshold:uint ):void {
  var maskColor:uint = (channel == "red") ? 0x00FF0000 : ( (channel == "green") ? 0x0000FF00 : 0x000000FF );
  myBitmapData.threshold(myBitmapData, 
    new Rectangle(0, 0, myBitmapData.width, myBitmapData.height), 
    new Point(0,0), 
    op, 
    threshold, 
    0x00000000, 
    maskColor, 
    false );
}

next:

function createBitmapMask(myBitmapData:BitmapData, colorLow:uint, colorHi:uint):void {
  //red
  applyThresholdRGB( myBitmapData, "red", "<", colorLow );
  applyThresholdRGB( myBitmapData, "red", ">", colorHi );

  //green
  applyThresholdRGB( myBitmapData, "green", "<", colorLow );
  applyThresholdRGB( myBitmapData, "green", ">", colorHi );

  //blue
  applyThresholdRGB( myBitmapData, "blue", "<", colorLow );
  applyThresholdRGB( myBitmapData, "blue", ">", colorHi );
}

You can see this bitmap mask in the box Bitmap Mask (top right), and finally:

addEventListener( Event.ENTER_FRAME,
  function (e:Event):void {
    // clone video
    simpleBitmap.bitmapData.draw( vid_video );
        
    // create a new bitmap with the same original data
    var bitmapMask:Bitmap = new Bitmap(simpleBitmap.bitmapData.clone());
        
    // create the mask
    createBitmapMask(bitmapMask.bitmapData,  0x00000000, 0x00222222);
  
    // apply the mask to original with BlendMode.ERASE
    simpleBitmap.bitmapData.draw(bitmapMask, null,  null, BlendMode.ERASE, null, false);
 }
);

You’ll see the final result in the Bitmap Merge box (bottom right) of the demo. That’s all.