Yesterday night it was too warm to sleep and I have no air conditioning at home, so I thought it was a good opportunity to make some experiments related to the high temperature… “the burning man”
Here’s the algorithm in human language:
- pick up the image from the webcam
- perform a threshold on it to exclude (alpha 0) every pixel below the average skin amount of red (between 44 and 90 depending on the light)
- perform a second threshold to exclude (alpha 0) every pixel above the average skin amount of green and blue (between 3344 and 6677 depending on the light)
- draw the result onto a new bitmapdata with the ADD blendmode
- apply blur to the bitmapdata
- generate a perlin noise of one channel (i chose red)
- displace the bitmapdata towards -y
- apply again a displacement map filter to displace horizontally (i used a cos function to make the x displacement a little likely)
- apply a displacement map towards +y with just a bit less strenght than the previous “-y” one (this is to make the fire propagation much more likely)
- then merge the original webcam output with the displaced one…and you’ve the burning man!
here the actionscript implementation
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BitmapDataChannel;
import flash.display.BlendMode;
import flash.display.Sprite;
import flash.events.Event;
import flash.filters.BlurFilter;
import flash.filters.DisplacementMapFilter;
import flash.filters.DisplacementMapFilterMode;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.media.Camera;
import flash.media.Video;
[SWF(width="320",height="240",frameRate="31",backgroundColor="0x000000")]
public class BurningMan extends Sprite
{
private var _cam:Camera;
private var _vid:Video;
private var _fireThreshold:BitmapData;
private var _firePerlin:BitmapData;
private var _fireOffset:Point;
private var _fireSpeed:int = 20;
private var _fireDisplaceUP:DisplacementMapFilter;
private var _fireDisplaceDWN:DisplacementMapFilter;
private var _fireDisplaceSrc:BitmapData;
private var _fireBlur:BlurFilter;
private var _fireColorTransform:ColorTransform;
private var _fireMerge:BitmapData;
private var _camBd:BitmapData;
private var _b:Bitmap;
private static const WIDTH:Number = 320;
private static const HEIGHT:Number = 240;
private static const ORIGIN:Point = new Point();
private static const REFLECT:Matrix = new Matrix(-1,0,0,1,WIDTH,0);
private static const RESIZE_2X:Matrix = new Matrix(2,0,0,2);
private var _rect:Rectangle;
public function BurningMan()
{
super();
init();
}
private function init():void
{
_cam = Camera.getCamera();
_cam.setMode(WIDTH,HEIGHT,31);
_vid = new Video(WIDTH,HEIGHT);
_vid.attachCamera(_cam);
_fireOffset = new Point();
_fireThreshold = new BitmapData(WIDTH,HEIGHT,true,0x00000000);
_fireMerge = new BitmapData(WIDTH,HEIGHT,true,0x00000000);
_firePerlin = new BitmapData(WIDTH*.5,HEIGHT*.5,false,0x000000);
_fireDisplaceSrc = new BitmapData(WIDTH,HEIGHT,false,0x000000);
_fireDisplaceUP = new DisplacementMapFilter(_fireDisplaceSrc,ORIGIN,BitmapDataChannel.RED,BitmapDataChannel.RED,0,-15,DisplacementMapFilterMode.CLAMP);
_fireDisplaceDWN = new DisplacementMapFilter(_fireDisplaceSrc,ORIGIN,BitmapDataChannel.RED,BitmapDataChannel.RED,0,9,DisplacementMapFilterMode.CLAMP);
_fireBlur = new BlurFilter(2,2,4);
_fireColorTransform = new ColorTransform(1.3,1.1,1,.7);
_camBd = new BitmapData(WIDTH,HEIGHT,true,0x00000000);
_rect = _camBd.rect;
_b = new Bitmap(_camBd);
_firePerlin.lock();
_fireMerge.lock();
addChild(_b);
addEventListener(Event.ENTER_FRAME,update);
}
private function update(e:Event=null):void
{
//locks
_camBd.lock();
_fireOffset.y+=_fireSpeed;
_firePerlin.perlinNoise(_firePerlin.width*.125,_firePerlin.height*.125,1,0,true,false,BitmapDataChannel.RED,false,[_fireOffset])
_fireDisplaceSrc.draw(_firePerlin,RESIZE_2X);
_camBd.draw(_vid,REFLECT);
_fireThreshold.setVector(_rect,_camBd.getVector(_rect));
_fireThreshold.threshold(_fireThreshold,_rect,ORIGIN,"<",0xFF440000,0x00000000,0xFF0000,true);
_fireThreshold.threshold(_fireThreshold,_rect,ORIGIN,">",0xFF003344,0x00000000,0x00FFFF,true);
_fireMerge.draw(_fireThreshold,null,_fireColorTransform,BlendMode.ADD,_rect);
_fireDisplaceUP.scaleX = Math.cos(((_fireOffset.y/(2<<3))%360)*180/Math.PI)*2;
_fireDisplaceDWN.scaleX = -_fireDisplaceUP.scaleX;
_fireMerge.applyFilter(_fireMerge,_fireMerge.rect,ORIGIN,_fireBlur);
_fireMerge.applyFilter(_fireMerge,_rect,ORIGIN,_fireDisplaceUP);
_fireMerge.applyFilter(_fireMerge,_rect,ORIGIN,_fireDisplaceDWN);
_camBd.draw(_fireMerge,null,_fireColorTransform,BlendMode.HARDLIGHT,_rect);
//unlocks
_camBd.unlock();
}
}
}
here's a preview of myself empowered by the force of the fire!:D

click the photo to see the demo in action