Flexで画像処理(ソース付き)
ActionScriptには便利な画像処理機能があるので、それを使ってみました。
激重なので、閲覧には気をつけてください。
#音声ヒストグラムと色ヒストグラムは特に重いので初期で無効にしています。
http://moeten.info/flex/20080218_videofilter/bin/main.html
ソースでっす。
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()" xmlns:flashFilter="flash.filters.*" height="634" width="100%" viewSourceURL="srcview/index.html"> <mx:Style> Application { backgroundColor: #cccccc; backgroundGradientColors: #cccccc, #333333; themeColor: #000033; color: #333333; } Panel { borderAlpha: 0; roundedBottomCorners: true; cornerRadius: 12; headerHeight: 22; backgroundAlpha: 0.28; highlightAlphas: 0.18, 0.15; headerColors: #ffffff, #999999; dropShadowEnabled: true; shadowDistance: 0; shadowDirection: right; titleStyleName: "mypanelTitle"; } .mypanelTitle { letterSpacing: 0; color: #ffffff; fontFamily: Trebuchet MS; fontSize: 14; fontWeight: bold; } </mx:Style> <mx:Script> <![CDATA[ import mx.rpc.events.ResultEvent; import mx.collections.ArrayCollection; import flash.filters.*; import flash.display.*; import flash.filters.ColorMatrixFilter; import flash.geom.Point; private var s:BitmapData; private var d:BitmapData; private var r:Rectangle; private var p:Point; private var trColor:uint; private var set_fillter:Boolean = true; //初期化関数 private function init():void{ var context:LoaderContext = new LoaderContext(true); //画像の初期化 s = new BitmapData(flv.width, flv.height); d = new BitmapData(flv.width, flv.height); r = new Rectangle(0, 0, flv.width, flv.height); p = new Point( 0,0 ); //各フィルターの初期化 setMovieMode(); //ムービーモード setMos(); //モザイク setTrColor(); //閾値 setBlurvalue(); //ぼかし setSharpvalue(); //シャープ setBevel(); //ベベル setDiffvalue(); //動き検出 setEdgevalue(); //エッジ setRGBvalue(); //RGB setColorMatrix();//ネガ //ヨツベから動画選択 youtube_s.send({ "tag": "ハルヒゲリオン", "page":1 }); youtube_s.addEventListener(ResultEvent.RESULT , function ():void{ flvurl.send({ flv_id:youtube_s.lastResult.items.item[0].flv_id }); flvurl.addEventListener(ResultEvent.RESULT , function():void{ if( flvurl.lastResult.flvurl ){ flv.source = "test.flv"; } myLog.text = String( flvurl.lastResult.flvurl ); flv.play(); //タイマーイベント作成 addEventListener(Event.ENTER_FRAME , timer); //音声ヒストグラム作成 Spectrum(); }); }); } private function timer( event:Event ):void { s.draw( flv ); //音声ヒストグラム描画 // soundGraphe(); // showHist(); ColorMatrix(); //閾値画像描画 if( CheckTr.selected ){ showTr(); } if( CheckEdge.selected ){ showEdge(); } if( CheckBlur.selected ){ showBlur(); } if( CheckSharp.selected ){ showSharp();} if( CheckDiff.selected ){ showDiff(); } if( CheckNega.selected ){ showNega(); } if( CheckMos.selected ){ showMos(); } if( CheckBevel.selected ){ showBevel();} } // flv camera モード private function setMovieMode():void{ if ( moviemode.selectedItem.value == 0 ){ //flv }else{ //cameara flv.pause(); // カメラソースを取得 var tape:Camera = Camera.getCamera(); // 表示処理 if ( tape != null ) { tape.setMode(320,240,30,true); flv.attachCamera( tape ); } var myMic:Microphone = Microphone.getMicrophone(); if (myMic != null) { myMic.setLoopBack(true); myMic.setUseEchoSuppression(true); myMic.setSilenceLevel(100,1000); } } } /* モザイク */ private function setMos():void{ mos_val.text = int( s_mos.value ).toString(); } private function showMos():void{ d = s.clone(); var mos:int = int( s_mos.value ); //小さいキャンバスを用意 var bmp:BitmapData = new BitmapData( Math.floor( d.width / mos ) , Math.floor( d.height / mos ) , false); var mtx:Matrix = new Matrix(); mtx.scale( 1/ mos , 1 / mos ); bmp.draw( d ,mtx ); myimage6.source = new Bitmap( bmp ); } /* 色ヒストグラム */ private function showHist():void{ // グレースケール化 var cmf:ColorMatrixFilter = new ColorMatrixFilter([ 0.33,0.33,0.33,0,0, 0.33,0.33,0.33,0,0, 0.33,0.33,0.33,0,0, 0,0,0,1,0 ]); d = s.clone(); d.applyFilter( d, r, p, cmf); // threshold でカウント var linedata:ArrayCollection = new ArrayCollection(); var values:Array = new Array(); for(var cnt:int = 0; cnt < 255; cnt++) { values[cnt] = d.threshold( d, r , p , "==", cnt, 0 , 0xff , false ); linedata.addItem({ color : cnt , sum : values[cnt] / 300 } ); } linec.dataProvider = linedata; } /* ブラー */ private var blur_x:Number; private var blur_y:Number; private function setBlurvalue():void{ blur_x = s_blur_x.value; blur_y = s_blur_y.value; blur_x_val.text = "x " + int( s_blur_x.value ); blur_y_val.text = "y " + int( s_blur_y.value ); } private function showBlur():void{ d = s.clone(); var Blur:BlurFilter = new BlurFilter(); Blur.blurX = blur_x; Blur.blurY = blur_y; d.applyFilter( d , r , p , Blur ); myimage0.source = new Bitmap( d ); } /* シャープ */ private var sharp_value:Number; private function setSharpvalue():void{ sharp_value = s_sharp.value; sharp_val.text = "" + int( s_sharp.value ); } private function showSharp():void{ d = s.clone(); var con:ConvolutionFilter = new ConvolutionFilter(3,3); con.matrix = new Array( 0, -1, 0, -1, sharp_value, -1, 0, -1, 0 ); d = s.clone(); d.applyFilter( d , r , p , con ); myimage2.source = new Bitmap( d ); } /* ベベル */ private function setBevel():void{ bevel_val.text = int( s_bevel.value ).toString(); } private function showBevel():void{ var con:ConvolutionFilter = new ConvolutionFilter(3,3); con.matrix = new Array( -s_bevel.value, -s_bevel.value+1, 0, -s_bevel.value+1, 1, s_bevel.value-1, 0, s_bevel.value-1, s_bevel.value ); d = s.clone(); d.applyFilter( d , r , p , con ); myimage7.source = new Bitmap( d ); } /* エッジ */ private function setEdgevalue():void{ } private function showEdge():void{ d = s.clone(); var Con:ConvolutionFilter = new ConvolutionFilter(3,3); sharp_value = int( s_sharp.value ); Con.matrix = new Array( 0, -1, 0, -1, 4, -1, 0, -1, 0 ); Con.bias = 255; d.applyFilter( d , r , p , Con ); if( edge2mono.selected ){ if( tr2mono.selected ){ d.threshold( d , r , p , "<" , 0xffdddddd , 0xff000000 ); } } myimage4.source = new Bitmap( d ); } /* ネガ */ private function showNega():void{ d = s.clone(); var Con:ConvolutionFilter = new ConvolutionFilter(3,3); sharp_value = int( s_sharp.value ); Con.matrix = new Array( 0, 0, 0, 0,-1, 0, 0, 0, 0 ); Con.bias = 255; d.applyFilter( d , r , p , Con ); myimage5.source = new Bitmap( d ); } /* 動き検出 */ private var i:int = 0; private var image_a:Array = new Array(); private var num:int = 10; private function setDiffvalue():void{ num = s_diff.value; diff_val.text = "フレーム " + num ; } private function showDiff():void{ d = s.clone(); image_a[i] = d; if( image_a.length > num ){ image_a[i-num].draw( d , new Matrix(), new ColorTransform(), "difference" ); if( diff2mono.selected ){ //2値化 image_a[ i - num ].threshold( image_a[ i-num ] , r , p , '>', 0xff111111 , 0xffffffff); } myimage3.source = new Bitmap( image_a[i-num] ); image_a.shift(); } i++; } /* 閾値 */ private function setTrColor():void{ trColor = 0xff4abd48; s_r.value = ( (trColor >> 16) & 0xFF ); s_g.value = ( (trColor >> 8) & 0xFF ); s_b.value = ( (trColor ) & 0xFF ); p_color.selectedColor = trColor; } private function set_trRGB():void{ trColor = uint( "0xff" +s_r.value.toString(16) + s_g.value.toString(16) + s_b.value.toString(16) ); p_color.selectedColor = trColor; setRGBvalue(); } private function change_rgb():void{ s_r.value = int(( ( int(p_color.value) >> 16) & 0xFF ).toString(10)); s_g.value = int(( ( int(p_color.value) >> 8) & 0xFF ).toString(10)); s_b.value = int(( ( int(p_color.value) ) & 0xFF ).toString(10)); trColor = uint( "0xff" +s_r.value.toString(16) + s_g.value.toString(16) + s_b.value.toString(16) ); setRGBvalue(); } private function setRGBvalue():void{ trColor_val.text = trColor.toString(16); r_val.text = "R " + int( s_r.value ).toString(); g_val.text = "G " + int( s_g.value ).toString(); b_val.text = "B " + int( s_b.value ).toString(); } private function showTr():void{ d = s.clone(); if( set_fillter ){ d.threshold( d , r , p , "<=" , trColor,0xff000000 ); if( tr2mono.selected ){ d.threshold( d , r , p , "!=" , 0xff000000 , 0xffffffff ); } } myimage.source = new Bitmap( d ); } /* カラーモード */ private function ColorMatrix():void{ var ColorMatrix:ColorMatrixFilter = new ColorMatrixFilter(); ColorMatrix.matrix = color_matrix[colormatrix.selectedItem.value]; s.applyFilter( s , r , p , ColorMatrix ); } private var color_matrix:Array = new Array(); private function setColorMatrix():void{ color_matrix[0] = new Array( 1,0,0,0,0, 0,1,0,0,0, 0,0,1,0,0, 0,0,0,1,0 ); color_matrix[1] = new Array( 0,0,1,0,0, 0,1,0,0,0, 1,0,0,0,0, 0,0,0,1,0 ); color_matrix[2] = new Array( 0.33,0.33,0.33,0,0, 0.33,0.33,0.33,0,0, 0.33,0.33,0.33,0,0, 0,0,0,1,0 ); color_matrix[3] = new Array( 0.5,0.5,0.5,0,0, 0.33,0.33,0.33,0,0, 0.25,0.25,0.25,0,0, 0,0,0,1,0 ); color_matrix[4] = new Array( 1,0,0,-1,0, 0,1,0,-1,0, 0,0,1,-1,0, -2,1,1,1,0 ); color_matrix[5] = new Array( 1,0,0,-1,0, 0,1,0,-1,0, 0,0,1,-1,0, 1,-2,1,1,0 ); color_matrix[6] = new Array( 1,0,0,-1,0, 0,1,0,-1,0, 0,0,1,-1,0, 1,1,-2,1,0 ); } private function maxFlv(event:Event):void{ } /* サウンドヒストグラム */ import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.Event; import flash.geom.ColorTransform; import flash.geom.Rectangle; import flash.media.Sound; import flash.media.SoundChannel; import flash.media.SoundMixer; import flash.net.URLRequest; import flash.utils.ByteArray; private var bytes:ByteArray = new ByteArray(); private var spectrum:Array = new Array(256); private var bd:BitmapData = new BitmapData( 213,103 , false, 0x000000); private var tonedown:ColorTransform = new ColorTransform(0.999, 0.999, 0.999); public function Spectrum():void{ myimage1.addChild( new Bitmap( bd ) ); } public function soundGraphe():void{ SoundMixer.computeSpectrum(bytes, true, 0); for (var i:int = 0; i < 256; ++i) { spectrum[i] = bytes.readFloat(); } bd.scroll(2, -0.5); //org 2,-1 bd.fillRect(new Rectangle(0, 0, 2, bd.height), 0x000000); bd.colorTransform(bd.rect, tonedown); for (var x:int = 0; x < 256; ++x) { var v:Number = spectrum[x]; var h:int = v * 100;//org 256 var y:int = bd.height - (256/2) + x/2 ; bd.fillRect( new Rectangle( x*0.1 , y - h +40, 1 , h + 1 ), HSBtoRGB( x / 256, 1, 1)); } } public static function RGBtoHSB(r:int, g:int, b:int):Array { var cmax:Number = Math.max(r, g, b); var cmin:Number = Math.min(r, g, b); var brightness:Number = cmax / 255.0; var hue:Number = 0; var saturation:Number = (cmax != 0) ? (cmax - cmin) / cmax : 0; if (saturation != 0) { var redc:Number = (cmax - r) / (cmax - cmin); var greenc:Number = (cmax - g) / (cmax - cmin); var bluec:Number = (cmax - b) / (cmax - cmin); if (r == cmax) { hue = bluec - greenc; } else if (g == cmax) { hue = 2.0 + redc - bluec; } else { hue = 4.0 + greenc - redc; } hue = hue / 6.0; if (hue < 0) { hue = hue + 1.0; } } return [hue, saturation, brightness]; } public static function HSBtoRGB(hue:Number, saturation:Number, brightness:Number):uint { var r:int = 0; var g:int = 0; var b:int = 0; if (saturation == 0) { r = g = b = brightness * 255.0 + 0.5; } else { var h:Number = (hue - Math.floor(hue)) * 6.0; var f:Number = h - Math.floor(h); var p:Number = brightness * (1.0 - saturation); var q:Number = brightness * (1.0 - saturation * f); var t:Number = brightness * (1.0 - (saturation * (1.0 - f))); switch (int(h)) { case 0: r = brightness * 255.0 + 0.5; g = t * 255.0 + 0.5; b = p * 255.0 + 0.5; break; case 1: r = q * 255.0 + 0.5; g = brightness * 255.0 + 0.5; b = p * 255.0 + 0.5; break; case 2: r = p * 255.0 + 0.5; g = brightness * 255.0 + 0.5; b = t * 255.0 + 0.5; break; case 3: r = p * 255.0 + 0.5; g = q * 255.0 + 0.5; b = brightness * 255.0 + 0.5; break; case 4: r = t * 255.0 + 0.5; g = p * 255.0 + 0.5; b = brightness * 255.0 + 0.5; break; case 5: r = brightness * 255.0 + 0.5; g = p * 255.0 + 0.5; b = q * 255.0 + 0.5; break; } } return (r << 16) | (g << 8) | (b << 0); } ]]> </mx:Script> <!-- ########################### HTTP searvice ############################## --> <mx:HTTPService method="GET" id="youtube_s" showBusyCursor="true" url="http://moeten.info/aizoom/kawai/?movie_site=youtube&page=1&type=xml" resultFormat="e4x" useProxy="false" /> <mx:HTTPService method="GET" id="flvurl" showBusyCursor="true" url="http://moeten.info/aizoom/kawai/?type=flvurl" resultFormat="e4x" useProxy="false" /> <mx:Panel x="519" y="10" width="240" height="300" layout="absolute" title="閾値" id="tr_"> <mx:ColorPicker x="81" y="168" change="change_rgb()" selectedColor="#ff00ff" enabled="true" id="p_color"/> <mx:HSlider x="51" y="201" minimum="0" maximum="255" id="s_r" allowTrackClick="true" change="set_trRGB()" borderColor="#ff0000" fillColors="[#ff0000, #ff0000]" width="159"/> <mx:HSlider x="51" y="217" minimum="0" maximum="255" id="s_g" allowTrackClick="true" change="set_trRGB()" fillColors="[#00ff00, #00ff00]" borderColor="#00ff00" width="159"/> <mx:HSlider x="51" y="232" minimum="0" maximum="255" id="s_b" allowTrackClick="true" change="set_trRGB()" fillColors="[#0000ff, #0000ff]" borderColor="#0000ff" width="159"/> <mx:Text x="15" y="204" text="R" color="#ff0000" id="r_val"/> <mx:Text x="15" y="237" text="B" color="#0000a0" id="b_val"/> <mx:Text x="15" y="220" text="G" color="#00ff00" id="g_val"/> <mx:Text x="10" y="191" text="テキスト" id="trColor_val"/> <mx:Image x="10" y="10" width="200" height="150" id="myimage" click="maxFlv(event)"/> <mx:CheckBox x="149" y="168" label="2値化" id="tr2mono" selected="false" enabled="true"/> <mx:CheckBox x="10" y="168" label="効果オン" id="CheckTr"/> </mx:Panel> <mx:Panel x="10" y="10" width="253" height="608" layout="absolute" title="オリジナル"> <mx:Image x="10" y="258" width="213" height="103" id="myimage1"/> <mx:VideoDisplay x="10" y="10" width="213" height="151" id="flv"/> <mx:ComboBox x="63" y="202" change="setColorMatrix()" id="colormatrix" width="87"> <mx:ArrayCollection> <mx:Object value="0" label="通常" /> <mx:Object value="1" label="RGB反転" /> <mx:Object value="2" label="モノクロ" /> <mx:Object value="3" label="セピア" /> <mx:Object value="4" label="R色のみ" /> <mx:Object value="5" label="G色のみ" /> <mx:Object value="6" label="B色のみ" /> </mx:ArrayCollection> </mx:ComboBox> <mx:TextArea x="10" y="531" width="213" height="37" id="mylog" visible="false"/> <mx:Text x="10" y="232" text="音声ヒストグラム"/> <mx:Text x="10" y="204" text="色モード"/> <mx:Text x="10" y="505" text="ログ"/> <mx:Label x="10" y="369" text="カラーヒストグラム"/> <mx:LineChart x="10" y="395" id="linec" height="102" width="213"> <mx:series> <mx:ColumnSeries xField="color" yField="sum" maxHeight="30" /> </mx:series> </mx:LineChart> <mx:Text x="10" y="171" text="動画モード"/> <mx:ComboBox x="63" y="169" width="87" change="setMovieMode()" id="moviemode"> <mx:ArrayCollection> <mx:Object value="0" label="FLV" /> <mx:Object value="1" label="カメラ" /> </mx:ArrayCollection> </mx:ComboBox> <mx:TextArea x="10" y="520" width="213" id="myLog"/> </mx:Panel> <mx:Panel x="767" y="10" width="240" height="300" layout="absolute" title="ぼかし"> <mx:HSlider x="64.5" y="193" change="setBlurvalue()" enabled="true" minimum="0" maximum="255" allowTrackClick="true" id="s_blur_x" value="4" width="145.5"/> <mx:HSlider x="64.5" y="218" change="setBlurvalue()" enabled="true" minimum="0" maximum="255" allowTrackClick="true" id="s_blur_y" value="4" width="145.5"/> <mx:Text x="31" y="197" text="x" id="blur_x_val"/> <mx:Text x="31" y="222" text="y" id="blur_y_val"/> <mx:Image x="10" y="7" width="200" height="150" id="myimage0" click="maxFlv(event)"/> <mx:CheckBox x="10" y="165" label="効果オン" id="CheckBlur"/> </mx:Panel> <mx:Panel x="1015" y="10" width="240" height="300" layout="absolute" title="シャープ"> <mx:Text x="20" y="197" text="value" id="sharp_val"/> <mx:Image x="10" y="10" width="200" height="150" id="myimage2" click="maxFlv(event)"/> <mx:CheckBox x="10" y="168" label="効果オン" id="CheckSharp"/> <mx:HSlider x="63" y="194" minimum="5" maximum="10" enabled="true" allowTrackClick="true" id="s_sharp" change="setSharpvalue()" value="5" width="145.5"/> </mx:Panel> <mx:Panel x="519" y="318" width="240" height="300" layout="absolute" title="動き検出"> <mx:HSlider x="79" y="211" id="s_diff" allowTrackClick="true" minimum="0" maximum="100" change="setDiffvalue()" enabled="true" value="10" width="131"/> <mx:Text x="10" y="211" text="テキスト" id="diff_val"/> <mx:CheckBox x="149" y="168" label="2値化" id="diff2mono" selected="false"/> <mx:Image x="10" y="10" width="200" height="150" id="myimage3" click="maxFlv(event)"/> <mx:CheckBox x="10" y="168" label="効果オン" id="CheckDiff"/> </mx:Panel> <mx:Panel x="767" y="318" width="240" height="300" layout="absolute" title="エッジ検出"> <mx:Image x="10" y="10" width="200" height="150" id="myimage4" click="maxFlv(event)"/> <mx:CheckBox x="149" y="168" label="2値化" id="edge2mono" selected="false" enabled="true"/> <mx:CheckBox x="10" y="168" label="効果オン" id="CheckEdge"/> </mx:Panel> <mx:Panel x="1015" y="318" width="240" height="300" layout="absolute" title="ネガ"> <mx:Image x="10" y="10" width="200" height="150" id="myimage5" click="maxFlv(event)"/> <mx:CheckBox x="10" y="168" label="効果オン" id="CheckNega"/> </mx:Panel> <mx:Panel x="271" y="10" width="240" height="300" layout="absolute" title="モザイク"> <mx:Image x="10" y="10" width="200" height="150" id="myimage6" click="maxFlv(event)"/> <mx:HSlider x="51" y="219" id="s_mos" value="4" change="setMos()" enabled="true" allowTrackClick="true" minimum="1" maximum="30" width="159"/> <mx:Text x="10" y="219" text="テキスト" id="mos_val"/> <mx:CheckBox x="10" y="168" label="効果オン" id="CheckMos"/> </mx:Panel> <mx:Panel x="271" y="318" width="240" height="300" layout="absolute" title="ベベル"> <mx:Image x="10" y="10" width="200" height="150" id="myimage7" click="maxFlv(event)"/> <mx:TextArea width="203" height="35" cornerRadius="6" text="Some text" x="8" y="220" visible="false"> <mx:filters> <flashFilter:DropShadowFilter inner="true" distance="3" angle="120" color="0x000000" alpha="0.4" /> </mx:filters> </mx:TextArea> <mx:HSlider x="51" y="209" id="s_bevel" minimum="2" maximum="10" allowTrackClick="true" enabled="true" change="setBevel()" width="159"/> <mx:Text x="10" y="209" text="テキスト" id="bevel_val"/> <mx:CheckBox x="10" y="168" label="効果オン" id="CheckBevel"/> </mx:Panel> </mx:Application>