/* * PROJECT: FLARToolKit * -------------------------------------------------------------------------------- * This work is based on the NyARToolKit developed by * R.Iizuka (nyatla) * http://nyatla.jp/nyatoolkit/ * * The FLARToolKit is ActionScript 3.0 version ARToolkit class library. * Copyright (C)2008 Saqoosha * * 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 2 * 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 framework; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * For further information please contact. * http://www.libspark.org/wiki/saqoosha/FLARToolKit * * */ package org.libspark.flartoolkit.core { import org.libspark.flartoolkit.core.labeling.FLARLabelingImageBitmapData; import org.libspark.flartoolkit.core.labeling.FLARLabelingLabel; import org.libspark.flartoolkit.core.labeling.FLARLabelingLabelStack; import org.libspark.flartoolkit.core.labeling.FLARLabeling_BitmapData; import org.libspark.flartoolkit.core.labeling.IFLARLabeling; import org.libspark.flartoolkit.core.labeling.IFLARLabelingImage; import org.libspark.flartoolkit.core.param.FLARCameraDistortionFactor; import org.libspark.flartoolkit.core.raster.IFLARRaster; import org.libspark.flartoolkit.core.types.FLARIntSize; import org.libspark.flartoolkit.core.types.FLARLinear; import org.libspark.flartoolkit.utils.ArrayUtil; /** * イメージから正方形候補を検出するクラス。 * このクラスは、arDetectMarker2.cとの置き換えになります。 * */ public class student_ar_org_libspark_flartoolkit_core_FLARSquareDetector implements Istudent_ar_org_libspark_flartoolkit_core_FLARSquareDetector { private static const VERTEX_FACTOR:Number = 1.0; // 線検出のファクタ private static const AR_AREA_MAX:int = 100000; // #define AR_AREA_MAX 100000 private static const AR_AREA_MIN:int = 70; // #define AR_AREA_MIN 70 private var _width:int; private var _height:int; private var _labeling:IFLARLabeling; private var _limage:IFLARLabelingImage; private var _overlap_checker:OverlapChecker = new OverlapChecker(); private var _dist_factor_ref:FLARCameraDistortionFactor; /** * 最大i_squre_max個のマーカーを検出するクラスを作成する。 * * @param i_param */ public function student_ar_org_libspark_flartoolkit_core_FLARSquareDetector(i_dist_factor_ref:FLARCameraDistortionFactor, i_size:FLARIntSize) { this._width = i_size.w; this._height = i_size.h; this._dist_factor_ref = i_dist_factor_ref; // this._labeling = new FLARLabeling_ARToolKit(); this._labeling = new FLARLabeling_BitmapData(); // this._limage = new FLARLabelingImage(this._width, this._height); this._limage = new FLARLabelingImageBitmapData(this._width, this._height); this._labeling.attachDestination(this._limage); // 輪郭の最大長は画面に映りうる最大の長方形サイズ。 var number_of_coord:int = (this._width + this._height) * 2; // 輪郭バッファは頂点変換をするので、輪郭バッファの2倍取る。 this._max_coord = number_of_coord; this._xcoord = new Array(number_of_coord * 2);//new int[number_of_coord * 2]; this._ycoord = new Array(number_of_coord * 2);//new int[number_of_coord * 2]; } private var _max_coord:int; private var _xcoord:Array; // int[] private var _ycoord:Array; // int[] /** * @param i_coord_x int[] * @param i_coord_y int[] */ private function normalizeCoord(i_coord_x:Array, i_coord_y:Array, i_index:int, i_coord_num:int):void { // vertex1を境界にして、後方に配列を連結 ArrayUtil.copy(i_coord_x, 1, i_coord_x, i_coord_num, i_index); ArrayUtil.copy(i_coord_y, 1, i_coord_y, i_coord_num, i_index); // System.arraycopy(i_coord_x, 1, i_coord_x, i_coord_num, i_index); // System.arraycopy(i_coord_y, 1, i_coord_y, i_coord_num, i_index); } // private final int[] __detectMarker_mkvertex = new int[5]; private var __detectMarker_mkvertex:Array = new Array(5); /** * ARMarkerInfo2 *arDetectMarker2( ARInt16 *limage, int label_num, int *label_ref,int *warea, double *wpos, int *wclip,int area_max, int area_min, double * factor, int *marker_num ) 関数の代替品 ラベリング情報からマーカー一覧を作成してo_marker_listを更新します。 関数はo_marker_listに重なりを除外したマーカーリストを作成します。 * * @param i_raster * 解析する2値ラスタイメージを指定します。 * @param o_square_stack * 抽出した正方形候補を格納するリスト * @throws FLARException */ public function detectMarker(i_raster:IFLARRaster, o_square_stack:FLARSquareStack):void { const labeling_proc:IFLARLabeling = this._labeling; const limage:IFLARLabelingImage = this._limage; // 初期化 // マーカーホルダをリセット o_square_stack.clear(); // ラベリング labeling_proc.labeling(i_raster); // ラベル数が0ならここまで const label_num:int = limage.getLabelStack().getLength(); if (label_num < 1) { return; } const stack:FLARLabelingLabelStack = limage.getLabelStack(); const labels:Array = stack.getArray(); // FLARLabelingLabel[] // ラベルを大きい順に整列 stack.sortByArea(); // デカいラベルを読み飛ばし var i:int; for (i = 0;i < label_num; i++) { // 検査対象内のラベルサイズになるまで無視 if (labels[i].area <= AR_AREA_MAX) { break; } } const xsize:int = this._width; const ysize:int = this._height; const xcoord:Array = this._xcoord; // int[] const ycoord:Array = this._ycoord; // int[] const coord_max:int = this._max_coord; const mkvertex:Array = this.__detectMarker_mkvertex; // int[] const overlap:OverlapChecker = this._overlap_checker; var coord_num:int; var label_area:int; var label_pt:FLARLabelingLabel; //重なりチェッカの最大数を設定 overlap.reset(label_num); var vertex1:int; var square_ptr:FLARSquare; for (;i < label_num; i++) { label_pt = labels[i]; label_area = label_pt.area; // 検査対象サイズよりも小さくなったら終了 if (label_area < AR_AREA_MIN) { break; } // クリップ領域が画面の枠に接していれば除外 if (label_pt.clip_l == 1 || label_pt.clip_r == xsize - 2) { // if(wclip[i*4+0] == 1 || wclip[i*4+1] ==xsize-2){ continue; } if (label_pt.clip_t == 1 || label_pt.clip_b == ysize - 2) { // if( wclip[i*4+2] == 1 || wclip[i*4+3] ==ysize-2){ continue; } // 既に検出された矩形との重なりを確認 if (!overlap.check(label_pt)) { // 重なっているようだ。 continue; } // 輪郭を取得 coord_num = limage.getContour(i, coord_max, xcoord, ycoord); if (coord_num == coord_max) { // 輪郭が大きすぎる。 continue; } //頂点候補のインデクスを取得 vertex1 = scanVertex(xcoord, ycoord, coord_num); // 頂点候補(vertex1)を先頭に並べなおした配列を作成する。 normalizeCoord(xcoord, ycoord, vertex1, coord_num); // 領域を準備する。 square_ptr = o_square_stack.prePush() as FLARSquare; // 頂点情報を取得 if (!getSquareVertex(xcoord, ycoord, vertex1, coord_num, label_area, mkvertex)) { o_square_stack.pop(); // 頂点の取得が出来なかったので破棄 continue; } if (!getSquareLine(mkvertex, xcoord, ycoord, square_ptr)) { // 矩形が成立しなかった。 o_square_stack.pop(); continue; } // 検出済の矩形の属したラベルを重なりチェックに追加する。 overlap.push(label_pt); // 後から参照できるように square_ptr.label = label_pt; } return; } /** * 辺からの対角線が最長になる点を対角線候補として返す。 * * @param i_xcoord int[] * @param i_ycoord int[] * @param i_coord_num int * @return */ private function scanVertex(i_xcoord:Array, i_ycoord:Array, i_coord_num:int):int { const sx:int = i_xcoord[0]; const sy:int = i_ycoord[0]; var d:int = 0; var w:int; var x:int; var y:int; var ret:int = 0; var i:int; for (i = 1; i < i_coord_num; i++) { x = i_xcoord[i] - sx; y = i_ycoord[i] - sy; w = x * x + y * y; if (w > d) { d = w; ret = i; } // ここでうまく終了条件入れられないかな。 } return ret; } private const __getSquareVertex_wv1:FLARVertexCounter = new FLARVertexCounter(); private const __getSquareVertex_wv2:FLARVertexCounter = new FLARVertexCounter(); /** * static int arDetectMarker2_check_square( int area, ARMarkerInfo2 *marker_info2, double factor ) 関数の代替関数 OPTIMIZED STEP [450->415] o_squareに頂点情報をセットします。 * * @param i_x_coord int[] * @param i_y_coord int[] * @param i_vertex1_index * @param i_coord_num * @param i_area * @param o_vertex int[] * 要素数はint[4]である事 * @return */ private function getSquareVertex(i_x_coord:Array, i_y_coord:Array, i_vertex1_index:int, i_coord_num:int, i_area:int, o_vertex:Array):Boolean { const wv1:FLARVertexCounter = this.__getSquareVertex_wv1; const wv2:FLARVertexCounter = this.__getSquareVertex_wv2; const end_of_coord:int = i_vertex1_index + i_coord_num - 1; const sx:int = i_x_coord[i_vertex1_index]; // sx = marker_info2->x_coord[0]; const sy:int = i_y_coord[i_vertex1_index]; // sy = marker_info2->y_coord[0]; var dmax:int = 0; var v1:int = i_vertex1_index; var d:int; var i:int; for (i = 1 + i_vertex1_index; i < end_of_coord; i++) { // for(i=1;icoord_num-1;i++) // { d = (i_x_coord[i] - sx) * (i_x_coord[i] - sx) + (i_y_coord[i] - sy) * (i_y_coord[i] - sy); if (d > dmax) { dmax = d; v1 = i; } } const thresh:Number = (i_area / 0.75) * 0.01 * VERTEX_FACTOR; o_vertex[0] = i_vertex1_index; if (!wv1.getVertex(i_x_coord, i_y_coord, i_vertex1_index, v1, thresh)) { // if(get_vertex(marker_info2->x_coord,marker_info2->y_coord,0,v1,thresh,wv1,&wvnum1)< // 0 ) { return false; } if (!wv2.getVertex(i_x_coord, i_y_coord, v1, end_of_coord, thresh)) { // if(get_vertex(marker_info2->x_coord,marker_info2->y_coord,v1,marker_info2->coord_num-1,thresh,wv2,&wvnum2) // < 0) { return false; } var v2:int; if (wv1.number_of_vertex == 1 && wv2.number_of_vertex == 1) { // if(wvnum1 == 1 && wvnum2== 1) { o_vertex[1] = wv1.vertex[0]; o_vertex[2] = v1; o_vertex[3] = wv2.vertex[0]; } else if (wv1.number_of_vertex > 1 && wv2.number_of_vertex == 0) { // }else if( wvnum1 > 1 && wvnum2== 0) { //頂点位置を、起点から対角点の間の1/2にあると予想して、検索する。 v2 = (v1 - i_vertex1_index) / 2 + i_vertex1_index; if (!wv1.getVertex(i_x_coord, i_y_coord, i_vertex1_index, v2, thresh)) { return false; } if (!wv2.getVertex(i_x_coord, i_y_coord, v2, v1, thresh)) { return false; } if (wv1.number_of_vertex == 1 && wv2.number_of_vertex == 1) { o_vertex[1] = wv1.vertex[0]; o_vertex[2] = wv2.vertex[0]; o_vertex[3] = v1; } else { return false; } } else if (wv1.number_of_vertex == 0 && wv2.number_of_vertex > 1) { //v2 = (v1-i_vertex1_index+ end_of_coord-i_vertex1_index) / 2+i_vertex1_index; v2 = (v1 + end_of_coord) / 2; if (!wv1.getVertex(i_x_coord, i_y_coord, v1, v2, thresh)) { return false; } if (!wv2.getVertex(i_x_coord, i_y_coord, v2, end_of_coord, thresh)) { return false; } if (wv1.number_of_vertex == 1 && wv2.number_of_vertex == 1) { o_vertex[1] = v1; o_vertex[2] = wv1.vertex[0]; o_vertex[3] = wv2.vertex[0]; } else { return false; } } else { return false; } o_vertex[4] = end_of_coord; return true; } private const __getSquareLine_input:FLARMat = new FLARMat(1, 2); private const __getSquareLine_evec:FLARMat = new FLARMat(2, 2); private const __getSquareLine_ev:FLARVec = new FLARVec(2); private const __getSquareLine_mean:FLARVec = new FLARVec(2); /** * arGetLine(int x_coord[], int y_coord[], int coord_num,int vertex[], double line[4][3], double v[4][2]) arGetLine2(int x_coord[], int y_coord[], int * coord_num,int vertex[], double line[4][3], double v[4][2], double *dist_factor) の2関数の合成品です。 マーカーのvertex,lineを計算して、結果をo_squareに保管します。 * Optimize:STEP[424->391] * * @param i_mkvertex int[] * @param i_xcoord int[] * @param i_ycoord int[] * @param o_square * @return * @throws FLARException */ private function getSquareLine(i_mkvertex:Array, i_xcoord:Array, i_ycoord:Array, o_square:FLARSquare):Boolean { const l_line:Array = o_square.line; // FLARLinear[] const ev:FLARVec = this.__getSquareLine_ev; // matrixPCAの戻り値を受け取る const mean:FLARVec = this.__getSquareLine_mean; // matrixPCAの戻り値を受け取る const mean_array:Array = mean.getArray(); // double[] const dist_factor:FLARCameraDistortionFactor = this._dist_factor_ref; const input:FLARMat = this.__getSquareLine_input; // 次処理で初期化される。 const evec:FLARMat = this.__getSquareLine_evec; // アウトパラメータを受け取るから初期化不要//new FLARMat(2,2); const evec_array:Array = evec.getArray(); // double[][] var i:int; var w1:Number; var st:int; var ed:int; var n:int; var l_line_i:FLARLinear; for (i = 0; i < 4; i++) { w1 = (i_mkvertex[i + 1] - i_mkvertex[i] + 1) * 0.05 + 0.5; st = (i_mkvertex[i] + w1); ed = (i_mkvertex[i + 1] - w1); n = ed - st + 1; if (n < 2) { // nが2以下でmatrix.PCAを計算することはできないので、エラー return false; } // pcaの準備 input.realloc(n, 2); // バッチ取得 dist_factor.observ2IdealBatch(i_xcoord, i_ycoord, st, n, input.getArray()); // 主成分分析 input.matrixPCA(evec, ev, mean); l_line_i = l_line[i]; l_line_i.run = evec_array[0][1]; // line[i][0] = evec->m[1]; l_line_i.rise = -evec_array[0][0]; // line[i][1] = -evec->m[0]; l_line_i.intercept = -(l_line_i.run * mean_array[0] + l_line_i.rise * mean_array[1]);// line[i][2] = -(line[i][0]*mean->v[0] + line[i][1]*mean->v[1]); } const l_sqvertex:Array = o_square.sqvertex; // FLARDoublePoint2d[] const l_imvertex:Array = o_square.imvertex; // FLARIntPoint[] var l_line_2:FLARLinear; for (i = 0; i < 4; i++) { l_line_i = l_line[i]; l_line_2 = l_line[(i + 3) % 4]; w1 = l_line_2.run * l_line_i.rise - l_line_i.run * l_line_2.rise; if (w1 == 0.0) { return false; } l_sqvertex[i].x = (l_line_2.rise * l_line_i.intercept - l_line_i.rise * l_line_2.intercept) / w1; l_sqvertex[i].y = (l_line_i.run * l_line_2.intercept - l_line_2.run * l_line_i.intercept) / w1; // 頂点インデクスから頂点座標を得て保存 l_imvertex[i].x = i_xcoord[i_mkvertex[i]]; l_imvertex[i].y = i_ycoord[i_mkvertex[i]]; } return true; } } } import org.libspark.flartoolkit.core.labeling.FLARLabelingLabel; /** * get_vertex関数を切り離すためのクラス * */ class FLARVertexCounter { public const vertex:Array = new Array(10); // = new int[10];// 5まで削れる public var number_of_vertex:int; private var thresh:Number; private var x_coord:Array; // int[] private var y_coord:Array; // int[] /** * @param i_x_coord int[] * @param i_y_coord int[] * @param st * @param ed * @param i_thresh */ public function getVertex(i_x_coord:Array, i_y_coord:Array, st:int, ed:int, i_thresh:Number):Boolean { this.number_of_vertex = 0; this.thresh = i_thresh; this.x_coord = i_x_coord; this.y_coord = i_y_coord; return get_vertex(st, ed); } /** * static int get_vertex( int x_coord[], int y_coord[], int st, int ed,double thresh, int vertex[], int *vnum) 関数の代替関数 * * @param x_coord * @param y_coord * @param st * @param ed * @param thresh * @return */ private function get_vertex(st:int, ed:int):Boolean { var v1:int = 0; const lx_coord:Array = this.x_coord; // int[] const ly_coord:Array = this.y_coord; // int[] const a:Number = ly_coord[ed] - ly_coord[st]; const b:Number = lx_coord[st] - lx_coord[ed]; const c:Number = lx_coord[ed] * ly_coord[st] - ly_coord[ed] * lx_coord[st]; var dmax:Number = 0; var i:int; var d:Number; for (i = st + 1; i < ed; i++) { d = a * lx_coord[i] + b * ly_coord[i] + c; if (d * d > dmax) { dmax = d * d; v1 = i; } } if (dmax / (a * a + b * b) > thresh) { if (!get_vertex(st, v1)) { return false; } if (number_of_vertex > 5) { return false; } vertex[number_of_vertex] = v1; // vertex[(*vnum)] = v1; number_of_vertex++; // (*vnum)++; if (!get_vertex(v1, ed)) { return false; } } return true; } } /** * ラベル同士の重なり(内包関係)を調べるクラスです。 ラベルリストに内包するラベルを蓄積し、それにターゲットのラベルが内包されているか を確認します。 */ class OverlapChecker { private var _labels:Array = new Array(32); // new FLARLabelingLabel[32]; private var _length:int; /** * 最大i_max_label個のラベルを蓄積できるようにオブジェクトをリセットする * * @param i_max_label */ public function reset(i_max_label:int):void { if (i_max_label > this._labels.length) { // this._labels = new FLARLabelingLabel[i_max_label]; this._labels = new Array(i_max_label); var n:int = i_max_label; while (n--) { this._labels[n] = new FLARLabelingLabel(); } } this._length = 0; } /** * チェック対象のラベルを追加する。 * * @param i_label_ref */ public function push(i_label_ref:FLARLabelingLabel):void { this._labels[this._length] = i_label_ref; this._length++; } /** * 現在リストにあるラベルと重なっているかを返す。 * * @param i_label * @return 何れかのラベルの内側にあるならばfalse,独立したラベルである可能性が高ければtrueです. */ public function check(i_label:FLARLabelingLabel):Boolean { // 重なり処理かな? const label_pt:Array = this._labels; // FLARLabelingLabel[] const px1:int = int(i_label.pos_x); const py1:int = int(i_label.pos_y); var i:int; var px2:int; var py2:int; var d:int; for (i = this._length - 1;i >= 0; i--) { px2 = int(label_pt[i].pos_x); py2 = int(label_pt[i].pos_y); d = (px1 - px2) * (px1 - px2) + (py1 - py2) * (py1 - py2); if (d < label_pt[i].area / 4) { // 対象外 return false; } } // 対象 return true; } }