package be.aboutme.differenceKey
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.Event;
import flash.filters.BlurFilter;
/**
* This class calculates the difference between 2 displayobjects
* and only shows the pixels that are truely difference
*
* This can be used to create difference and/or color keying applications
*
* Usage:
*
* 1. Create a new instance of this class:
*
* var differenceImage:DifferenceImage = new DifferenceImage();
*
* 2. Set the background property to the the image you want to remove from the picture.
* Most of the time, this will be the webcam image without the user in the frame
*
* differenceImage.background = referenceToBackground;
*
* 3. Set the foreground property to the image you want to keep.
* Most of the time, this will be the webcame image with the user inside the frame
*
* differenceImage.foreground = referenceToForeground;
*
* 4. Add it to the displaylist to see the keyed result.
*
* addChild(differenceImage);
*/
public class DifferenceImage extends Sprite
{
private var keyContainer:Sprite;
private var differenceKeyShader:DifferenceKeyShader;
private var foregroundBitmap:Bitmap;
private var foregroundBitmapData:BitmapData;
private var backgroundBitmap:Bitmap;
private var backgroundBitmapData:BitmapData;
private var outputMaskBitmap:Bitmap;
private var outputMaskBitmapData:BitmapData;
private var outputBitmap:Bitmap;
private var outputBitmapData:BitmapData;
private var animatedBackground:Boolean;
private var backgroundInvalidated:Boolean;
/**
* Amount of difference in the first color component to remove a pixel
* The first color component is Red in RGB mode, or Hue in HSB mode.
* This is a value between 0 and 1
*
* @default 0.1
*/
public function set tolerance1(value:Number):void
{
differenceKeyShader.tolerance1 = value;
if(foregroundBitmap != null)
{
foregroundBitmap.blendShader = differenceKeyShader;
}
}
public function get tolerance1():Number
{
return differenceKeyShader.tolerance1;
}
/**
* Amount of difference in the second color component to remove a pixel
* The second color component is Green in RGB mode, or Saturation in HSB mode.
* This is a value between 0 and 1
*
* @default 0.1
*/
public function set tolerance2(value:Number):void
{
differenceKeyShader.tolerance2 = value;
if(foregroundBitmap != null)
{
foregroundBitmap.blendShader = differenceKeyShader;
}
}
public function get tolerance2():Number
{
return differenceKeyShader.tolerance2;
}
/**
* Amount of difference in the third component to remove a pixel
* The third color component is Blue in RGB mode, or Brightness in HSB mode.
* This is a value between 0 and 1
*
* @default 0.1
*/
public function set tolerance3(value:Number):void
{
differenceKeyShader.tolerance3 = value;
if(foregroundBitmap != null)
{
foregroundBitmap.blendShader = differenceKeyShader;
}
}
public function get tolerance3():Number
{
return differenceKeyShader.tolerance3;
}
/**
* Amount of blur to apply before we calculate the difference
* This is usefull to remove noise from the images before the difference calculation
*
* @default 10
*/
private var _preBlur:Number;
public function get preBlur():Number
{
return _preBlur;
}
public function set preBlur(value:Number):void
{
_preBlur = value;
if(backgroundBitmap) backgroundBitmap.filters = [new BlurFilter(_preBlur, _preBlur)];
if(foregroundBitmap) foregroundBitmap.filters = [new BlurFilter(_preBlur, _preBlur)];
}
/**
* Amount of blur to apply after we calculated the difference
* This is usefull to remove gaps from the image mask
*
* @default 10
*/
private var _postBlur:Number;
public function get postBlur():Number
{
return _postBlur;
}
public function set postBlur(value:Number):void
{
_postBlur = value;
if(outputMaskBitmap) outputMaskBitmap.filters = [new BlurFilter(_postBlur, _postBlur)];
}
/**
* Use RGB (0) or HSB (1) mode for the calculations
*
* @default 0
*/
public function set mode(value:uint):void
{
differenceKeyShader.mode = value;
if(foregroundBitmap != null)
{
foregroundBitmap.blendShader = differenceKeyShader;
}
}
public function get mode():uint
{
return differenceKeyShader.mode;
}
private var _foreground:DisplayObject;
/**
* The image containing the pixels you want to keep in the keyed image.
* Most of the time, this will be a reference to a webcam feed with the user inside the frame
*/
public function get foreground():DisplayObject
{
return _foreground;
}
public function set foreground(value:DisplayObject):void
{
_foreground = value;
if(_foreground != null)
{
resetBitmap("foregroundBitmap", "foregroundBitmapData");
foregroundBitmapData = new BitmapData(_foreground.width, _foreground.height, true, 0x00000000);
foregroundBitmap = new Bitmap(foregroundBitmapData);
foregroundBitmap.blendShader = differenceKeyShader;
foregroundBitmap.filters = [new BlurFilter(_preBlur, _preBlur)];
keyContainer.addChild(foregroundBitmap);
//create output
resetBitmap("outputBitmap", "outputBitmapData");
outputBitmapData = new BitmapData(_foreground.width, _foreground.height, true, 0x00000000);
outputBitmap = new Bitmap(outputBitmapData);
outputBitmap.cacheAsBitmap = true;
addChild(outputBitmap);
resetBitmap("outputMaskBitmap", "outputMaskBitmapData");
outputMaskBitmapData = outputBitmapData.clone();
outputMaskBitmap = new Bitmap(outputMaskBitmapData);
outputMaskBitmap.filters = [new BlurFilter(_postBlur, _postBlur)];
outputMaskBitmap.cacheAsBitmap = true;
addChild(outputMaskBitmap);
outputBitmap.mask = outputMaskBitmap;
executeKeying();
}
}
private var _background:DisplayObject;
/**
* The image containing the pixels you want to remove from the foreground.
* Most of the time, this will be a webcam snapshot with the user outside of the frame
*/
public function get background():DisplayObject
{
return _background;
}
public function set background(value:DisplayObject):void
{
_background = value;
if(_background != null)
{
resetBitmap("backgroundBitmap", "backgroundBitmapData");
backgroundBitmapData = new BitmapData(_background.width, _background.height, true, 0x00000000);
backgroundBitmap = new Bitmap(backgroundBitmapData);
backgroundBitmap.filters = [new BlurFilter(_preBlur, _preBlur)];
keyContainer.addChildAt(backgroundBitmap, 0);
backgroundInvalidated = true;
executeKeying();
}
}
public function DifferenceImage()
{
differenceKeyShader = new DifferenceKeyShader();
_preBlur = 10;
_postBlur = 10;
keyContainer = new Sprite();
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
private function enterFrameHandler(event:Event):void
{
executeKeying();
}
private function resetBitmap(bitmapName:String, bitmapDataName:String):void
{
if(this[bitmapName] != null)
{
this[bitmapName].parent.removeChild(this[bitmapName]);
this[bitmapName] = null;
}
if(this[bitmapDataName] != null)
{
this[bitmapDataName].dispose();
this[bitmapDataName] = null;
}
}
private function executeKeying():void
{
if(_foreground != null)
{
if(_background != null)
{
foregroundBitmapData.draw(_foreground);
if(backgroundInvalidated)
{
backgroundBitmapData.draw(_background);
backgroundInvalidated = false;
}
outputBitmapData.draw(_foreground);
outputMaskBitmapData.draw(keyContainer);
if(animatedBackground) backgroundInvalidated = true;
}
else
{
outputBitmapData.draw(_foreground);
outputMaskBitmapData.draw(_foreground);
}
}
}
}
}