WebCamera+MRSS
「Webカメラ+MRSS」
*
* Webカメラの使用を許可してください☆
* ARとまではいかなくても2DのWebカメラ画像でももっと遊べるんじゃないかと思って作ってみました。
*
* ▼ 操作方法 ▼
* カメラの前で手などを振ってください。振る方向によって4種類の操作が可能です。
* 少し大げさに振るほうが確実ではありますが手で扇ぐような動作でもいけるかも?
* 精度はイマイチのような。
*
* 右から左に振る: 次の画像に移動
* 左から右に振る: 前の画像に移動
* 下から上に振る: モーション確認用のドットを表示する
* 上から下に振る: モーション確認用のドットを非表示にする
*
* 動作テストしすぎて手がだるい・・・(>_<。)
*
/**
* Copyright takishiki ( http://wonderfl.net/user/takishiki )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/kOLT
*/
// forked from Event's Simple MediaRSS Viewer
/*
* 「Webカメラ+MRSS」
*
* Webカメラの使用を許可してください☆
* ARとまではいかなくても2DのWebカメラ画像でももっと遊べるんじゃないかと思って作ってみました。
*
* ▼ 操作方法 ▼
* カメラの前で手などを振ってください。振る方向によって4種類の操作が可能です。
* 少し大げさに振るほうが確実ではありますが手で扇ぐような動作でもいけるかも?
* 精度はイマイチのような。
*
* 右から左に振る: 次の画像に移動
* 左から右に振る: 前の画像に移動
* 下から上に振る: モーション確認用のドットを表示する
* 上から下に振る: モーション確認用のドットを非表示にする
*
* 動作テストしすぎて手がだるい・・・(>_<。)
*
*/
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.display.Loader;
import flash.display.BlendMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.media.Camera;
import flash.media.Video;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.system.Security;
import flash.filters.ColorMatrixFilter;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.system.LoaderContext;
// SWFメタデータタグ
[SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "0x000000")]
public class Main extends Sprite
{
private const FEED :String = "http://api.flickr.com/services/feeds/photos_public.gne?format=rss_200&tags=";
private const MEDIA :Namespace = new Namespace("http://search.yahoo.com/mrss/");
private const DIV: int = 10; // 分割数
private var _tag :String = "kamakura";
private var _camera :Camera;
private var _video :Video;
private var _pastBmpd :BitmapData;
private var _nowBmpd :BitmapData;
private var _blackBmpd :BitmapData;
private var _bmpd2 :BitmapData;
private var _bmpd :BitmapData;
private var _bmp :Bitmap;
private var _sp :Sprite;
private var _imgBmp :Bitmap;
private var _imgSp :Sprite;
private var _divBmpd :BitmapData;
private var _map :Array;
private var _changeIndex:Array;
private var _changeFlag :Boolean;
private var _imgList :Array;
private var _dir:int;
private var _id:int;
// constructor
public function Main():void {
Security.loadPolicyFile("http://api.flickr.com/crossdomain.xml");
Security.loadPolicyFile("http://farm1.static.flickr.com/crossdomain.xml");
Security.loadPolicyFile("http://farm2.static.flickr.com/crossdomain.xml");
Security.loadPolicyFile("http://farm3.static.flickr.com/crossdomain.xml");
Security.loadPolicyFile("http://farm4.static.flickr.com/crossdomain.xml");
stage.frameRate = 30;
// カメラ
_camera = Camera.getCamera();
_camera.setMode(480, 320, _camera.fps);
if (_camera == null) {
return;
}
_id = 0;
_imgSp = new Sprite();
_imgBmp = new Bitmap(new BitmapData(10, 10));
_imgSp.x = stage.stageWidth / 2;
_imgSp.y = stage.stageHeight / 2;
_imgSp.addChild(_imgBmp);
this.addChild(_imgSp);
_sp = new Sprite();
_sp.blendMode = BlendMode.ADD;
this.addChild(_sp);
// ビデオ
_video = new Video(_camera.width, _camera.height);
_video.attachCamera(_camera);
_video.x = _video.y = 0;
_bmpd = new BitmapData(_video.width, _video.height, false, 0x000000);
_bmp = new Bitmap(_bmpd);
_bmp.width = stage.stageWidth;
_bmp.height = stage.stageHeight;
_sp.addChild(_bmp);
_bmpd2 = new BitmapData(_video.width, _video.height, false, 0x000000);
_blackBmpd = new BitmapData(_video.width, _video.height, false, 0x000000);
_pastBmpd = new BitmapData(_video.width, _video.height, false, 0x000000);
_nowBmpd = new BitmapData(_video.width, _video.height, false, 0x000000);
_pastBmpd.draw(_video);
_nowBmpd.draw(_video);
_divBmpd = new BitmapData(_video.width / DIV, _video.height / DIV, false, 0xFF0000);
var i:int;
var j:int;
_changeFlag = false;
_changeIndex = [];
_changeIndex[0] = [0, 0, 0, 0];
_changeIndex[1] = [0, 0, 0, 0];
_map = [];
for (i = 0; i < DIV; i++) {
_map[i] = [];
for (j = 0; j < DIV; j++) {
_map[i][j] = 0;
}
}
readMediaRSS(_tag);
}
// フレーム処理
private function onEnterFrame(event:Event):void {
detectMotionDirection();
}
// 動きから方向を検出
private function detectMotionDirection():void {
_bmpd.draw(_video);
_bmpd2.lock();
_nowBmpd.draw(_video);
// 前回との差分(変化領域)を取得
_nowBmpd.draw(_pastBmpd, new Matrix(), new ColorTransform(), BlendMode.DIFFERENCE);
//_nowBmpd = _nowBmpd.compare(_pastBmpd) as BitmapData;
// 黒一色(フレームレートの関係で前回と同じコマを取得してしまった場合)はスルー
if (_nowBmpd.compare(_blackBmpd) == 0) return;
_pastBmpd.copyPixels(_bmpd, _bmpd.rect, new Point());
_bmpd2.draw(_nowBmpd);
// グレースケール化
grayscale_filter(_bmpd2);
// 二値化
var mask:uint = 0x00FFFFFF;
var threshold:uint = 0xFF222222;
_bmpd2.threshold(_bmpd2, _bmpd2.rect, new Point(0, 0), ">=", threshold, 0xFFFFFFFF, mask, true);
_bmpd2.threshold(_bmpd2, _bmpd2.rect, new Point(0, 0), "<", threshold, 0xFF000000, mask, true);
// 10×10領域に分割して評価
var hist:Vector.<Vector.<Number>>;
var i:int;
var j:int;
var rect:Rectangle;
var pxMax:int = _divBmpd.width * _divBmpd.height;
var color:int;
var id:int;
var cnt:int = 0;
for (i = 0; i < DIV; i++) {
for (j = 0; j < DIV; j++) {
rect = new Rectangle(_bmpd2.width / DIV * j, _bmpd2.height / DIV * i, _bmpd2.width / DIV, _bmpd2.height / DIV);
_divBmpd.copyPixels(_bmpd2, rect, new Point());
hist = _divBmpd.histogram();
if (hist[0][255] / pxMax < 0.5) {
color = 0;
cnt++;
}else {
color = 1;
}
if (_map[i][j] != color) {
_map[i][j] = 1;
}else {
_map[i][j] = 0;
}
_bmpd2.fillRect(rect, 0x666666 * _map[i][j]);
}
}
if (cnt == DIV * DIV) {
// 変化なし
if (_changeFlag) {
for (i = 0; i < 4; i++) {
_changeIndex[1][i] = searchChangeEdge(i);
}
var w:int = Math.abs(_changeIndex[1][2] - _changeIndex[1][3]);
var h:int = Math.abs(_changeIndex[1][0] - _changeIndex[1][1]);
// 長さが5マス以上の場合、上下方向、左右方向の判定
if (Math.max(w, h) >= 5) {
if (w >= h) {
// 左右
if (Math.abs(_changeIndex[1][2] - _changeIndex[0][2]) >= Math.abs(_changeIndex[1][3] - _changeIndex[0][3])) {
// 左端のほうが変化が大きい=左方向への動き
_id = _id - 1 < 0 ? _imgList.length - 1 : _id - 1;
setImage(_id);
}else {
_id = (_id + 1) % _imgList.length;
setImage(_id);
}
}else {
// 上下
if (Math.abs(_changeIndex[1][0] - _changeIndex[0][0]) >= Math.abs(_changeIndex[1][1] - _changeIndex[0][1])) {
// 上端のほうが変化が大きい=上方向への動き
_sp.visible = true;
}else {
_sp.visible = false;
}
}
}
}
_changeFlag = false;
for (i = 0; i < DIV; i++) {
for (j = 0; j < DIV; j++) {
_map[i][j] = 0;
}
}
}else {
// 変化あり
if (_changeFlag == false) {
_changeFlag = true;
for (i = 0; i < 4; i++) {
_changeIndex[0][i] = searchChangeEdge(i);
}
}
}
_bmpd2.unlock();
_bmp.bitmapData = _bmpd2;
}
// 変化領域を取得
private function searchChangeEdge(dir:int):int {
var i:int;
var j:int;
var val:int = 0;
// 上下左右の順
switch(dir) {
case 0 :
for (i = 0; i < DIV; i++) {
if (_map[i].indexOf(1) != -1) {
val = i;
return val;
}
}
break;
case 1 :
for (i = 0; i < DIV; i++) {
if (_map[DIV - i - 1].indexOf(1) != -1) {
val = DIV - i - 1;
return val;
}
}
break;
case 2 :
for (i = 0; i < DIV; i++) {
for (j = 0; j < DIV; j++) {
if (_map[j][i] == 1) {
val = i;
return val;
}
}
}
break;
case 3 :
for (i = 0; i < DIV; i++) {
for (j = 0; j < DIV; j++) {
if (_map[j][DIV - i - 1] == 1) {
val = DIV - i - 1;
return val;
}
}
}
break;
}
return val;
}
// グレースケール化
public function grayscale_filter(bmpd:BitmapData):void {
var rAmp:Number;
var gAmp:Number;
var bAmp:Number;
//rAmp = gAmp = bAmp = 1 / 3;
rAmp = 0.299;
gAmp = 0.587;
bAmp = 0.114;
var cmf:ColorMatrixFilter = new ColorMatrixFilter([
rAmp, gAmp, bAmp, 0, 0,
rAmp, gAmp, bAmp, 0, 0,
rAmp, gAmp, bAmp, 0, 0,
0, 0, 0, 1, 0
]);
bmpd.applyFilter(bmpd, bmpd.rect, new Point(0, 0), cmf);
}
//
private function readMediaRSS(tag:String):void {
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, function _load(event:Event):void {
loader.removeEventListener(Event.COMPLETE, _load);
onImageLoaded(XML(loader.data)..MEDIA::thumbnail.@url.toXMLString().split('\n'));
});
loader.load(new URLRequest(FEED + tag));
}
//
private function onImageLoaded($images:Array):void {
_imgList = [];
var i:int;
var ldr:Loader;
for (i = 0; i < $images.length; i++) {
ldr = new Loader();
_imgList[i] = new URLRequest($images[i]);
}
setImage(0);
this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
//
private function setImage(id:int = 0):void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderComplete);
loader.load(_imgList[id], new LoaderContext(true));
}
//
private function loaderComplete(event:Event):void {
if (_imgSp.numChildren != 0) {
_imgSp.removeChildAt(0);
}
_imgBmp = event.target.content as Bitmap;
_imgBmp.width = _imgBmp.height = Math.min(stage.stageWidth, stage.stageHeight);
_imgBmp.x = -_imgBmp.width / 2;
_imgBmp.y = -_imgBmp.height / 2;
_imgSp.addChild(_imgBmp);
}
}
}