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); } } } } }