/*
* PROJECT: NyARToolkitAS3
* --------------------------------------------------------------------------------
* This work is based on the original ARToolKit developed by
* Hirokazu Kato
* Mark Billinghurst
* HITLab, University of Washington, Seattle
* http://www.hitl.washington.edu/artoolkit/
*
* The NyARToolkitAS3 is AS3 edition ARToolKit class library.
* Copyright (C)2010 Ryo Iizuka
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* For further information please contact.
* http://nyatla.jp/nyatoolkit/
* or
*
*/
package jp.nyatla.nyartoolkit.as3.processor
{
import jp.nyatla.nyartoolkit.as3.core.param.*;
import jp.nyatla.nyartoolkit.as3.core.match.*;
import jp.nyatla.nyartoolkit.as3.core.pickup.*;
import jp.nyatla.nyartoolkit.as3.core.squaredetect.*;
import jp.nyatla.nyartoolkit.as3.core.transmat.*;
import jp.nyatla.nyartoolkit.as3.core.raster.*;
import jp.nyatla.nyartoolkit.as3.core.raster.rgb.*;
import jp.nyatla.nyartoolkit.as3.core.*;
import jp.nyatla.nyartoolkit.as3.core.rasterfilter.rgb2bin.*;
import jp.nyatla.nyartoolkit.as3.core.types.*;
import jp.nyatla.nyartoolkit.as3.*;
import jp.nyatla.nyartoolkit.as3.core.analyzer.raster.*;
import jp.nyatla.nyartoolkit.as3.core.analyzer.raster.threshold.*;
import jp.nyatla.as3utils.*;
/**
* このクラスは、同時に1個のマーカを処理することのできる、アプリケーションプロセッサです。
* マーカの出現・移動・消滅を、イベントで通知することができます。
* クラスには複数のマーカを登録できます。一つのマーカが見つかると、プロセッサは継続して同じマーカを
* 1つだけ認識し続け、見失うまでの間は他のマーカを認識しません。
*
* イベントは、 OnEnter→OnUpdate[n]→OnLeaveの順で発生します。
* マーカが見つかるとまずOnEnterが1度発生して、何番のマーカが発見されたかがわかります。
* 次にOnUpdateにより、現在の変換行列が連続して渡されます。最後にマーカを見失うと、OnLeave
* イベントが発生します。
*
*/
public class SingleARMarkerProcesser
{
/**オーナーが自由に使えるタグ変数です。
*/
public var tag:Object;
private var _lost_delay_count:int = 0;
private var _lost_delay:int = 5;
private var _square_detect:NyARSquareContourDetector;
protected var _transmat:INyARTransMat;
private var _offset:NyARRectOffset;
private var _threshold:int = 110;
// [AR]検出結果の保存用
private var _bin_raster:NyARBinRaster;
private var _tobin_filter:NyARRasterFilter_ARToolkitThreshold;
protected var _current_arcode_index:int = -1;
private var _threshold_detect:NyARRasterThresholdAnalyzer_SlidePTile;
public function SingleARMarkerProcesser()
{
return;
}
private var _initialized:Boolean=false;
protected function initInstance(i_param:NyARParam,i_raster_type:int):void
{
//初期化済?
NyAS3Utils.assert(this._initialized==false);
var scr_size:NyARIntSize = i_param.getScreenSize();
// 解析オブジェクトを作る
this._square_detect = new NyARSquareContourDetector_Rle(scr_size);
this._transmat = new NyARTransMat(i_param);
this._tobin_filter=new NyARRasterFilter_ARToolkitThreshold(110,i_raster_type);
// 2値画像バッファを作る
this._bin_raster = new NyARBinRaster(scr_size.w, scr_size.h);
this._threshold_detect=new NyARRasterThresholdAnalyzer_SlidePTile(15,i_raster_type,4);
this._initialized=true;
//コールバックハンドラ
this._detectmarker_cb=new DetectSquareCB(i_param);
this._offset=new NyARRectOffset();
return;
}
/*自動・手動の設定が出来ないので、コメントアウト
public void setThreshold(int i_threshold)
{
this._threshold = i_threshold;
return;
}*/
/**検出するマーカコードの配列を指定します。 検出状態でこの関数を実行すると、
* オブジェクト状態に強制リセットがかかります。
*/
public function setARCodeTable(i_ref_code_table:Vector.,i_code_resolution:int,i_marker_width:Number):void
{
if (this._current_arcode_index != -1) {
// 強制リセット
reset(true);
}
//検出するマーカセット、情報、検出器を作り直す。(1ピクセル4ポイントサンプリング,マーカのパターン領域は50%)
this._detectmarker_cb.setNyARCodeTable(i_ref_code_table,i_code_resolution);
this._offset.setSquare(i_marker_width);
return;
}
public function reset(i_is_force:Boolean):void
{
if (this._current_arcode_index != -1 && i_is_force == false) {
// 強制書き換えでなければイベントコール
this.onLeaveHandler();
}
// カレントマーカをリセット
this._current_arcode_index = -1;
return;
}
private var _detectmarker_cb:DetectSquareCB;
public function detectMarker(i_raster:INyARRgbRaster):void
{
// サイズチェック
NyAS3Utils.assert(this._bin_raster.getSize().isEqualSize_int(i_raster.getSize().w, i_raster.getSize().h));
//BINイメージへの変換
this._tobin_filter.setThreshold(this._threshold);
this._tobin_filter.doFilter(i_raster, this._bin_raster);
// スクエアコードを探す
this._detectmarker_cb.init(i_raster,this._current_arcode_index);
this._square_detect.detectMarkerCB(this._bin_raster,this._detectmarker_cb);
// 認識状態を更新
var is_id_found:Boolean=updateStatus(this._detectmarker_cb.square,this._detectmarker_cb.code_index);
//閾値フィードバック(detectExistMarkerにもあるよ)
if(!is_id_found){
//マーカがなければ、探索+DualPTailで基準輝度検索
var th:int=this._threshold_detect.analyzeRaster(i_raster);
this._threshold=(this._threshold+th)/2;
}
return;
}
/**
*
* @param i_new_detect_cf
* @param i_exist_detect_cf
*/
public function setConfidenceThreshold(i_new_cf:Number,i_exist_cf:Number):void
{
this._detectmarker_cb.cf_threshold_exist=i_exist_cf;
this._detectmarker_cb.cf_threshold_new=i_new_cf;
}
private var __NyARSquare_result:NyARTransMatResult = new NyARTransMatResult();
/** オブジェクトのステータスを更新し、必要に応じてハンドル関数を駆動します。
* 戻り値は、「実際にマーカを発見する事ができたか」です。クラスの状態とは異なります。
*/
private function updateStatus(i_square:NyARSquare,i_code_index:int):Boolean
{
var result:NyARTransMatResult = this.__NyARSquare_result;
if (this._current_arcode_index < 0) {// 未認識中
if (i_code_index < 0) {// 未認識から未認識の遷移
// なにもしないよーん。
return false;
} else {// 未認識から認識の遷移
this._current_arcode_index = i_code_index;
// イベント生成
// OnEnter
this.onEnterHandler(i_code_index);
// 変換行列を作成
this._transmat.transMat(i_square, this._offset, result);
// OnUpdate
this.onUpdateHandler(i_square, result);
this._lost_delay_count = 0;
return true;
}
} else {// 認識中
if (i_code_index < 0) {// 認識から未認識の遷移
this._lost_delay_count++;
if (this._lost_delay < this._lost_delay_count) {
// OnLeave
this._current_arcode_index = -1;
this.onLeaveHandler();
}
return false;
} else if (i_code_index == this._current_arcode_index) {// 同じARCodeの再認識
// イベント生成
// 変換行列を作成
this._transmat.transMatContinue(i_square, this._offset, result);
// OnUpdate
this.onUpdateHandler(i_square, result);
this._lost_delay_count = 0;
return true;
} else {// 異なるコードの認識→今はサポートしない。
throw new NyARException();
}
}
}
protected function onEnterHandler(i_code:int):void
{
throw new NyARException("onEnterHandler not implemented.");
}
protected function onLeaveHandler():void
{
throw new NyARException("onLeaveHandler not implemented.");
}
protected function onUpdateHandler(i_square:NyARSquare, result:NyARTransMatResult):void
{
throw new NyARException("onUpdateHandler not implemented.");
}
}
}
import jp.nyatla.nyartoolkit.as3.core.param.*;
import jp.nyatla.nyartoolkit.as3.core.match.*;
import jp.nyatla.nyartoolkit.as3.core.pickup.*;
import jp.nyatla.nyartoolkit.as3.core.squaredetect.*;
import jp.nyatla.nyartoolkit.as3.core.transmat.*;
import jp.nyatla.nyartoolkit.as3.core.raster.*;
import jp.nyatla.nyartoolkit.as3.core.raster.rgb.*;
import jp.nyatla.nyartoolkit.as3.core.*;
import jp.nyatla.nyartoolkit.as3.core.rasterfilter.rgb2bin.*;
import jp.nyatla.nyartoolkit.as3.core.types.*;
import jp.nyatla.nyartoolkit.as3.*;
/**
* detectMarkerのコールバック関数
*/
class DetectSquareCB implements NyARSquareContourDetector_IDetectMarkerCallback
{
//公開プロパティ
public var square:NyARSquare=new NyARSquare();
public var confidence:Number=0.0;
public var code_index:int=-1;
public var cf_threshold_new:Number = 0.50;
public var cf_threshold_exist:Number = 0.30;
//参照
private var _ref_raster:INyARRgbRaster;
//所有インスタンス
private var _inst_patt:INyARColorPatt;
private var _deviation_data:NyARMatchPattDeviationColorData;
private var _match_patt:Vector.;
private var __detectMarkerLite_mr:NyARMatchPattResult=new NyARMatchPattResult();
private var _coordline:NyARCoord2Linear;
public function DetectSquareCB(i_param:NyARParam)
{
this._match_patt=null;
this._coordline=new NyARCoord2Linear(i_param.getScreenSize(),i_param.getDistortionFactor());
return;
}
public function setNyARCodeTable(i_ref_code:Vector.,i_code_resolution:int):void
{
/*unmanagedで実装するときは、ここでリソース解放をすること。*/
this._deviation_data=new NyARMatchPattDeviationColorData(i_code_resolution,i_code_resolution);
this._inst_patt=new NyARColorPatt_Perspective_O2(i_code_resolution,i_code_resolution,4,25);
this._match_patt = new Vector.(i_ref_code.length);
for(var i:int=0;i=NyARIntPoint2d.createArray(4);
private var _target_id:int;
/**
* Initialize call back handler.
*/
public function init(i_raster:INyARRgbRaster,i_target_id:int):void
{
this._ref_raster = i_raster;
this._target_id=i_target_id;
this.code_index=-1;
this.confidence = Number.MIN_VALUE;
}
/**
* 矩形が見付かるたびに呼び出されます。
* 発見した矩形のパターンを検査して、方位を考慮した頂点データを確保します。
*/
public function onSquareDetect(i_sender:NyARSquareContourDetector,i_coordx:Vector.,i_coordy:Vector.,i_coor_num:int,i_vertex_index:Vector.):void
{
if (this._match_patt==null) {
return;
}
//輪郭座標から頂点リストに変換
var vertex:Vector.=this.__tmp_vertex;
vertex[0].x=i_coordx[i_vertex_index[0]];
vertex[0].y=i_coordy[i_vertex_index[0]];
vertex[1].x=i_coordx[i_vertex_index[1]];
vertex[1].y=i_coordy[i_vertex_index[1]];
vertex[2].x=i_coordx[i_vertex_index[2]];
vertex[2].y=i_coordy[i_vertex_index[2]];
vertex[3].x=i_coordx[i_vertex_index[3]];
vertex[3].y=i_coordy[i_vertex_index[3]];
//画像を取得
if (!this._inst_patt.pickFromRaster(this._ref_raster,vertex)){
return;//取得失敗
}
//取得パターンをカラー差分データに変換して評価する。
this._deviation_data.setRaster(this._inst_patt);
//code_index,dir,c1にデータを得る。
var mr:NyARMatchPattResult=this.__detectMarkerLite_mr;
var lcode_index:int = 0;
var dir:int = 0;
var c1:Number = 0;
var i:int;
for (i = 0; i < this._match_patt.length; i++) {
this._match_patt[i].evaluate(this._deviation_data,mr);
var c2:Number = mr.confidence;
if (c1 < c2) {
lcode_index = i;
c1 = c2;
dir = mr.direction;
}
}
//認識処理
if (this._target_id == -1) { // マーカ未認識
//現在は未認識
if (c1 < this.cf_threshold_new) {
return;
}
if (this.confidence > c1) {
// 一致度が低い。
return;
}
//認識しているマーカIDを保存
this.code_index=lcode_index;
}else{
//現在はマーカ認識中
// 現在のマーカを認識したか?
if (lcode_index != this._target_id) {
// 認識中のマーカではないので無視
return;
}
//認識中の閾値より大きいか?
if (c1 < this.cf_threshold_exist) {
return;
}
//現在の候補よりも一致度は大きいか?
if (this.confidence>c1) {
return;
}
this.code_index=this._target_id;
}
//新しく認識、または継続認識中に更新があったときだけ、Square情報を更新する。
//ココから先はこの条件でしか実行されない。
//一致率の高い矩形があれば、方位を考慮して頂点情報を作成
this.confidence=c1;
var sq:NyARSquare=this.square;
//directionを考慮して、squareを更新する。
for(i=0;i<4;i++){
var idx:int=(i+4 - dir) % 4;
this._coordline.coord2Line(i_vertex_index[idx],i_vertex_index[(idx+1)%4],i_coordx,i_coordy,i_coor_num,sq.line[i]);
}
for (i = 0; i < 4; i++) {
//直線同士の交点計算
if(!NyARLinear.crossPos(sq.line[i],sq.line[(i + 3) % 4],sq.sqvertex[i])){
throw new NyARException();//ここのエラー復帰するならダブルバッファにすればOK
}
}
}
}