2008年のエントリー VTR調変換 AS3.0 の内容を動画でも試してみました。
ダビングや経年などで劣化したVTR映像のような効果を付加するものです。
2年前の記事では OpenCV の IplImage 構造体を模した独自クラスを作り、
そのクラスのメンバに画像データを入れて処理しています。
今回は対象が動画ということもあり、メインのロジック部分をコンパクトにしました。
なるべくキャッシュにヒットするように書いているつもりですが、
GCのSweep Phaseで荒らされると思いますので気休め程度に。
走査線、ゴースト(二重写り)、ガンマ補正の3つの効果がかかっていますが、
色相をいじったり、ノイズを乗せたりして効果を追加していくのもいいかもしれません。
wonderfl にも投稿しておきましたので参考になれば。
VTR Filter for web camera – wonderfl build flash online
・VTR調変換用クラス (inherit flash.media.Video)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.Event; import flash.media.Video; public class VTR extends Video { private var _contrast:int; private var _thick:int; private var _ghost:int; private var gtbl:Vector.<uint>; private var frame:BitmapData; public function VTR(screen:Bitmap) { super(screen.width, screen.height); frame = screen.bitmapData; gtbl = new Vector.<uint>(256, true); } public function setCondition(contrast:int, ghost:int, thick:int, gamma:Number):void { _contrast = contrast; _ghost = ghost; _thick = thick; // require 2^x /* create table for gamma correction */ for(var i:int=0; i<256; i++) { gtbl[i] = Math.pow(i/255.0, gamma)*255.0; } } public function start():void { addEventListener(Event.ENTER_FRAME, apply, false, 0, true); } /** main algorithm add scanning lines, ghost and gamma correction. **/ private function apply(e:Event):void { frame.draw(super); frame.lock(); var data:Vector.<uint> = frame.getVector(frame.rect); var w:int = frame.width, h:int = frame.height; var r:uint = 0, g:uint = 0, b:uint = 0; var i:int = 0, j:int = 0, step:int = w; var f:int = 1; var len:int = data.length; for(var y:int = 0; y < h; y++, step += w) { f = (y & _thick - 1) << 1 < _thick ? 1 : 0; if(step + _ghost >= len) break; for(j = i + _ghost; i < step; i++, j++) { r = ((data[i] >> 16) & 0xff) + ((data[j] >> 16) & 0xff) >> 1; g = ((data[i] >> 8) & 0xff) + ((data[j] >> 16) & 0xff) >> 1; b = (data[i] & 0xff) + (data[j] & 0xff) >> 1; if(f && (r = g = b += _contrast) > 0xff) { r = g = b = 0xff; } data[i] = gtbl[r] << 16 | gtbl[g] << 8 | gtbl[b]; } } frame.setVector(frame.rect, data); frame.unlock(); } } } |
・クライアント
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package { import com.rt.VTR; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.ActivityEvent; import flash.media.Camera; public class Main extends Sprite { private var camera:Camera; private var screen:Bitmap; private var vtr:VTR; public function Main() { stage.scaleMode = "noScale"; camera = Camera.getCamera(); if(camera) { camera.setMode(stage.stageWidth/2, stage.stageHeight/2, 30); screen = new Bitmap(new BitmapData(camera.width, camera.height, false)); camera.addEventListener(ActivityEvent.ACTIVITY, onActivity); vtr = new VTR(screen); vtr.attachCamera(camera); vtr.setCondition(4, 4, 4, 2.0); addChild(screen); }else { throw new Error("required camera"); } } private function onActivity(e:ActivityEvent):void { removeEventListener(ActivityEvent.ACTIVITY, arguments.callee); vtr.start(); } } } |