AS3でCannyエッジ検出を勉強する

Filed under AS3



Cannyエッジ検出ってのがあります。

アルゴリズムをよく知らずに使っていたので、勉強として作ってみました。

こんな感じになりました。

閾値は上が60で下が30に固定されています。

アルゴリズムに沿って作ってるので結構重い。FPSが10くらい。

一応Cannyっぽいのが出来た・・・気がする。

間違いに気付いた方がいたら教えてください。

以下、ソースです。

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageQuality;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.filters.ColorMatrixFilter;
	import flash.filters.ConvolutionFilter;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.media.Camera;
	import flash.media.Video;

	public class Main extends Sprite
	{
		private static const COLOR_MATRIX:ColorMatrixFilter = new ColorMatrixFilter([0.3,0.59,0.11,0,0,0.3,0.59,0.11,0,0,0.3, 0.59, 0.11,0,0,0,0,0,1,0]);
		private static const COVLUTION:ConvolutionFilter = new ConvolutionFilter(5,5,[2,4,5,4,2,4,9,12,9,4,5,12,15,12,5,4,9,12,9,4,2,4,5,4,2],159);
		private static const SOBEL_X:ConvolutionFilter = new ConvolutionFilter(3,3,[-1,0,1,-2,0,2,-1,0,1]);
		private static const SOBEL_Y:ConvolutionFilter = new ConvolutionFilter(3,3,[1,2,1,0,0,0,-1,-2,-1]);
		private static const W:int = 320;
		private static const H:int = 240;
		private static const TH:int = 60;
		private static const TL:int = 30;
		private static const RECT:Rectangle = new Rectangle(0,0,W,H);
		private static const POINT:Point = new Point();
		private var source:BitmapData;
		private var canvas:BitmapData;
		private var dx:BitmapData;
		private var dy:BitmapData;
		private var video:Video;
		private var buffPower:Vector.<int>;
		private var buffTheta:Vector.<int>;
		private var buffEdge:Vector.<int>;
		private var buffTh:Vector.<int>;
		private var buffTl:Vector.<int>;

		public function Main()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			stage.quality = StageQuality.LOW;
			stage.frameRate = 10;

			var camera:Camera = Camera.getCamera();
			if(camera)
			{
				camera.setMode(W, H, 20);
				video = new Video(W, H) as Video;
				video.attachCamera(camera);

				source = new BitmapData(W, H);
				canvas = source.clone();
				dx  = source.clone();
				dy  = source.clone();

				var bmp1:Bitmap = addChild(new Bitmap(source)) as Bitmap;
				var bmp2:Bitmap = addChild(new Bitmap(canvas)) as Bitmap;
				bmp1.x = 40;
				bmp2.x = W + 50;
				bmp1.y = bmp2.y = 50;

				// 一時データを保存するVector
				buffPower = new Vector.<int>(W*H, true);
				buffTheta = new Vector.<int>(W*H, true);
				buffEdge = new Vector.<int>(W*H, true);
				buffTh=new Vector.<int>(W*H,true);
				buffTl=new Vector.<int>(W*H,true);

				// イベント追加
				addEventListener(Event.ENTER_FRAME, onFrame);
			}
		}

		private function onFrame(e:Event):void
		{
			var i:int, j:int, n:int, theta:int, cx:int, cy:int, s0:int, s1:int, s2:int, edge:int, tw:int, th:int;
			source.lock();
			canvas.lock();
			source.draw(video);

			// グレスケ化
			canvas.applyFilter(source, source.rect, POINT, COLOR_MATRIX);

			// ノイズ除去
			canvas.applyFilter(canvas, canvas.rect, POINT, COVLUTION);

			// sobel
			dx.applyFilter(canvas, RECT, POINT, SOBEL_X);
			dy.applyFilter(canvas, RECT, POINT, SOBEL_Y);

			// 勾配と強さを計算
			for(j=0 ; j<H ; j++)
			{
				for(i=0 ; i<W ; i++)
				{
					cx = int((dx.getPixel(i,j))&0xff);
					cy = int((dy.getPixel(i,j))&0xff);
					theta = (Math.atan2(cy, cx)*180/Math.PI)>>0;
					if(theta < 0) theta += 180;
					n = j*W + i;
					buffPower[n] = Math.sqrt(cx*cx + cy*cy)>>0;
					buffTheta[n] = theta;
				}
			}

			// エッジの線細化
			tw = W-1;
			th = H-1;
			for(j=1 ; j<th ; j++)
			{
				for(i=1 ; i<tw ; i++)
				{
					n = j*W + i;
					theta = buffTheta[n];
					s1 = buffPower[n];
					s0 = s2 = 0;

					if((theta >= 0 && theta <= 22) || (theta >= 157 && theta <= 180))
					{
						s0 = buffPower[n-1];
						s2 = buffPower[n+1];
					}
					else if((theta >= 22 && theta <= 67) )
					{
						s0 = buffPower[n-1+W];
						s2 = buffPower[n+1-W];
					}
					else if((theta >= 67 && theta <= 112))
					{
						s0 = buffPower[n+W];
						s2 = buffPower[n-W];
					}
					else if((theta >= 112 && theta <= 157))
					{
						s0 = buffPower[n+W+1];
						s2 = buffPower[n-W-1];
					}

					if(s1 > s0 && s1 > s2) buffEdge[n] = s1;
					else buffEdge[n] = 0;
				}
			}

			// 閾値処理
			for(j=1 ; j<th ; j++)
			{
				for(i=1 ; i<tw ; i++)
				{
					n = j*W + i;
					edge = buffEdge[n]>>0;
					buffTh[n] = (TH <= edge)?(255):(0);
					buffTl[n] = (TL <= edge)?(255):(0);
				}
			}

			// 閾値処理で出来た2つの画像を使って出力画像を作成
			tw = W-2;
			th = H-2;
			for(j=2 ; j<th ; j++)
			{
				for(i=2 ; i<tw ; i++)
				{
					n = j*W + i;
					if(buffTh[n] == 0 && buffTl[n]==255)
					{
						if(buffTh[n-1] == 255) 			buffTh[n] = 255;
						else if(buffTh[n-1-W] == 255)	buffTh[n] = 255;
						else if(buffTh[n-W] == 255)		buffTh[n] = 255;
						else if(buffTh[n-W+1] == 255)	buffTh[n] = 255;
						else if(buffTh[n+1] == 255)		buffTh[n] = 255;
						else if(buffTh[n+1+W] == 255) 	buffTh[n] = 255;
						else if(buffTh[n+W] == 255) 	buffTh[n] = 255;
						else if(buffTh[n-1+W] == 255)	buffTh[n] = 255;
						else if(buffTh[n-2] == 255) 	buffTh[n] = buffTh[n-1]	   =255;
						else if(buffTh[n-2-W*2] == 255)buffTh[n] = buffTh[n-1-W]  =255;
						else if(buffTh[n-W*2] == 255)	buffTh[n] = buffTh[n-W]    =255;
						else if(buffTh[n-W*2+2] == 255)buffTh[n] = buffTh[n-W+1]  =255;
						else if(buffTh[n+2] == 255)		buffTh[n] = buffTh[n+2]    =255;
						else if(buffTh[n+2+W*2] == 255)	buffTh[n] = buffTh[n+2+W*2]=255;
						else if(buffTh[n+W*2] == 255)	buffTh[n] = buffTh[n+W*2]  =255;
						else if(buffTh[n-2+W*2] == 255)	buffTh[n] = buffTh[n-2+W*2]=255;
					}
					canvas.setPixel(i,j,uint((buffTh[n]<<16)|(buffTh[n]<<8)|(buffTh[n])));
				}
			}

			source.unlock();
			canvas.unlock();
		}
	}
}

Post a Comment

Your email is never published nor shared.