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