Marilenaを使って顔検知してみる


AS3で顔検知のできるMarilenaというライブラリを使ってみた。
*要WebCam

OpenCVっていうC/C++用のライブラリがあって、画像処理まわりの便利関数がたくさんある。その中の特に顔検知に必要な物体検出(Object Detection)まわりの処理をAS3に移植したのが、今回使ったMarilena。移植したのはwonderfl作った人だ。凄いもんだ。

Get Adobe Flash player


ここでは、ウェブカムから取得した顔に、絵柄を乗っけるようにしてみた。やはり1枚あたりの処理時間がかかるので、スムーズな動画生成は厳しい。
ただ、1枚の画像の加工だったら演出によってはなんとかなるかもね。例えば「写真を読み込み/WebCamで撮影→ジャカジャカジャカ、ジャン!→はいできました~」みたいなのとか。
なんにしても、活用できたら楽しそう。

▼ActionScript AS3(FP9)
[sourcecode language=”as3″]
/*
* Marilenaを使って顔検知してみる。
* http://www.libspark.org/wiki/mash/Marilena
*
* WebCamから画像を取得して、MarilenaのObjectDetectorに
* 投げているだけ。
*
* */

package
{
import flash.display.StageScaleMode;
import flash.display.StageAlign;
import flash.display.Sprite;
import flash.display.Graphics;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Rectangle;

public class Main extends Sprite {
private var _faceDetect:FaceDetect;
private var _faceRectContainer:Sprite;
private var _bitmap:Bitmap;
private var _snapShot:SnapShot;

public function Main() {
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;

_bitmap = new Bitmap(new BitmapData(320, 240));
this.addChild(_bitmap);

_faceRectContainer = new Sprite();
this.addChild(_faceRectContainer);

_snapShot = new SnapShot();
_snapShot.onStart = onStart;
_snapShot.y = 240;
}
private function onStart():void {
_faceDetect = new FaceDetect();
_faceDetect.setBitmap(_snapShot.getBitmap());
_faceDetect.onComplete = onComplete;
}

private function onComplete(rects:*):void {
_bitmap.bitmapData.draw(_faceDetect.getBitmap());
if( rects ){
var g:Graphics = _faceRectContainer.graphics;
g.clear();

rects.forEach(function(r:Rectangle, idx:int, arr:Array):void {

g.beginFill(0xFF6666, 0.8);
g.drawEllipse(r.x – r.width * 0.15, r.y – r.height * 0.28, r.width * 1.3, r.height * 1.3);
g.drawEllipse(r.x, r.y, r.width, r.height);

g.beginFill(0,0);
g.lineStyle(1, 0xFF0000);
g.drawCircle(r.x + r.width / 3.6, r.y + r.height / 3, r.width/7);
g.drawCircle(r.x + r.width – r.width / 3.6, r.y + r.height / 3, r.width / 7);
g.endFill();

g.lineStyle(1, 0x000000);
g.moveTo(r.x + r.width / 3.6, r.y + r.height / 1.4);
g.lineTo(r.x – r.width / 20, r.y + r.height / 1.5);
g.moveTo(r.x + r.width / 3.6, r.y + r.height / 1.3);
g.lineTo(r.x – r.width / 10, r.y + r.height / 1.32);
g.moveTo(r.x + r.width / 3.6,r.y + r.height / 1.25);
g.lineTo(r.x, r.y + r.height / 1.1);

g.moveTo(r.x + r.width – r.width / 3.6, r.y + r.height / 1.4);
g.lineTo(r.x + r.width + r.width / 20, r.y + r.height / 1.5);
g.moveTo(r.x + r.width – r.width / 3.6, r.y + r.height / 1.3);
g.lineTo(r.x + r.width + r.width / 10, r.y + r.height / 1.32);
g.moveTo(r.x + r.width – r.width / 3.6, r.y + r.height / 1.2);
g.lineTo(r.x + r.width, r.y + r.height / 1.1);
g.endFill();

g.lineStyle(0,0,0);
g.beginFill(0xFF0000, 1);
g.drawCircle(r.x + r.width / 2, r.y + r.height / 1.8, r.width / 12);
g.endFill();
});
}
_faceDetect.setBitmap(_snapShot.getBitmap());
}
}
}

//webカムからbitmapを取得するクラス
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.ActivityEvent;
import flash.media.Camera;
import flash.media.Video;
class SnapShot extends Sprite {
private var _video:Video;
private var _camera:Camera;
public var onStart:Function = function():void { };
public function SnapShot() {
_camera = Camera.getCamera();
_camera.setMode(320, 240, 10);

if (_camera != null) {
_video = new Video(320, 240);
_video.attachCamera(_camera);
this.addChild(_video);
} else {
trace("You need a camera.");
}
_camera.addEventListener(ActivityEvent.ACTIVITY, _onActivity);
}

private var _isStart:Boolean;
//カメラから画像が取得できたら、一度だけ動く
private function _onActivity(event:ActivityEvent):void {
_camera.removeEventListener(ActivityEvent.ACTIVITY, _onActivity);
if (!_isStart) {
_isStart = true;
onStart();
}
}

//カメラの画像をBitmapとして取り出す。
public function getBitmap():Bitmap {
var bitmap:Bitmap = new Bitmap(new BitmapData(_video.width, _video.height));
bitmap.bitmapData.draw(_video);
return bitmap;
}
}

//Bitmapを投げると、顔の範囲をあれば返すクラス。
import flash.display.Bitmap;
import jp.maaash.ObjectDetection.ObjectDetector;
import jp.maaash.ObjectDetection.ObjectDetectorOptions;
import jp.maaash.ObjectDetection.ObjectDetectorEvent;
class FaceDetect {
private var _detector:ObjectDetector;
public var onComplete:Function = function(rects:*):void { };
private var _bitmap:Bitmap;
public function FaceDetect() {
_detector = new ObjectDetector();
_detector.options = getDetectorOptions();
_detector.addEventListener(ObjectDetectorEvent.DETECTION_COMPLETE, DETECTION_COMPLETE);
};
private function DETECTION_COMPLETE(event:ObjectDetectorEvent):void {
onComplete(event.rects);
}
public function setBitmap(bitmap:Bitmap):void {
_detector.loadHaarCascades("face.zip");
//アップされているswfではblogに乗せるように↓にしている。
//_detector.loadHaarCascades("http://www.mztm.jp/wp/wp-content/uploads/2010/04/face.zip");
_detector.detect(bitmap);
_bitmap = bitmap;
}
public function getBitmap():Bitmap {
return _bitmap;
}
private function getDetectorOptions() :ObjectDetectorOptions {
var options:ObjectDetectorOptions = new ObjectDetectorOptions();
options.min_size = 50;
options.startx = ObjectDetectorOptions.INVALID_POS;
options.starty = ObjectDetectorOptions.INVALID_POS;
options.endx = ObjectDetectorOptions.INVALID_POS;
options.endy = ObjectDetectorOptions.INVALID_POS;
return options;
}
}
[/sourcecode]