/** * 内部関数です。 * この関数は、thisの二次元矩形情報プロパティを更新します。 * @param i_coord * @param i_vertex_index * @ */ protected internal void updateSquareInfo(NyARIntCoordinates i_coord, int[] i_vertex_index) { NyARMatchPattResult mr = this.__detectMarkerLite_mr; //輪郭座標から頂点リストに変換 NyARIntPoint2d[] vertex = this.__ref_vertex; //C言語ならポインタ扱いで実装 vertex[0] = i_coord.items[i_vertex_index[0]]; vertex[1] = i_coord.items[i_vertex_index[1]]; vertex[2] = i_coord.items[i_vertex_index[2]]; vertex[3] = i_coord.items[i_vertex_index[3]]; //画像を取得 if (!this._inst_patt.pickFromRaster(this._last_input_raster, vertex)) { return; } //取得パターンをカラー差分データに変換して評価する。 this._deviation_data.setRaster(this._inst_patt); if (!this._match_patt.evaluate(this._deviation_data, mr)) { return; } //現在の一致率より低ければ終了 if (this._confidence > mr.confidence) { return; } //一致率の高い矩形があれば、方位を考慮して頂点情報を作成 NyARSquare sq = this._square; this._confidence = mr.confidence; //directionを考慮して、squareを更新する。 for (int i = 0; i < 4; i++) { int idx = (i + 4 - mr.direction) % 4; this._coordline.coord2Line(i_vertex_index[idx], i_vertex_index[(idx + 1) % 4], i_coord, sq.line[i]); } //ちょっと、ひっくり返してみようか。 for (int i = 0; i < 4; i++) { //直線同士の交点計算 if (!sq.line[i].crossPos(sq.line[(i + 3) % 4], sq.sqvertex[i])) { throw new NyARException();//ここのエラー復帰するならダブルバッファにすればOK } } }
/** * コンストラクタです。 * 入力画像のサイズを指定して、インスタンスを生成します。 * @param i_size * 入力画像のサイズ */ public NyARSquareContourDetector_ARToolKit(NyARIntSize i_size) { this._width = i_size.w; this._height = i_size.h; this._labeling = new NyARLabeling_ARToolKit(); this._limage = new NyARLabelingImage(this._width, this._height); // 輪郭の最大長は画面に映りうる最大の長方形サイズ。 int number_of_coord = (this._width + this._height) * 2; // 輪郭バッファは頂点変換をするので、輪郭バッファの2倍取る。 this._coord = new NyARIntCoordinates(number_of_coord); return; }
public bool traceConture(int i_th, NyARIntPoint2d i_entry, VecLinearCoordinates o_coord) { NyARIntCoordinates coord = this._coord_buf; // Robertsラスタから輪郭抽出 if (!this._cpickup.getContour(this._ref_rob_raster, i_th, i_entry.x, i_entry.y, coord)) { // 輪郭線MAXならなにもできないね。 return(false); } // 輪郭線のベクトル化 return(traceConture(coord, this._rob_resolution, this._rob_resolution * 2, o_coord)); }
public bool traceLine(NyARDoublePoint2d i_pos1, NyARDoublePoint2d i_pos2, int i_edge, VecLinearCoordinates o_coord) { NyARIntCoordinates coord = this._coord_buf; NyARIntSize base_s = this._ref_base_raster.getSize(); // (i_area*2)の矩形が範囲内に収まるように線を引く // 移動量 // 点間距離を計算 int dist = (int)Math.Sqrt(i_pos1.sqDist(i_pos2)); // 最低AREA*2以上の大きさが無いなら、ラインのトレースは不可能。 if (dist < 4) { return(false); } // dist最大数の決定 if (dist > 12) { dist = 12; } // サンプリングサイズを決定(移動速度とサイズから) int s = i_edge * 2 + 1; int dx = (int)(i_pos2.x - i_pos1.x); int dy = (int)(i_pos2.y - i_pos1.y); int r = base_s.w - s; int b = base_s.h - s; // 最大24点を定義して、そのうち両端の2個を除いた点を使用する。 for (int i = 1; i < dist - 1; i++) { int x = (int)(i * dx / dist + i_pos1.x - i_edge); int y = (int)(i * dy / dist + i_pos1.y - i_edge); // limit coord.items[i - 1].x = x < 0 ? 0 : (x >= r ? r : x); coord.items[i - 1].y = y < 0 ? 0 : (y >= b ? b : y); } coord.length = dist - 2; // 点数は10点程度を得る。 return(traceConture(coord, 1, s, o_coord)); }
/** * 輪郭線を取得します。 * 取得アルゴリズムは、以下の通りです。 * 1.輪郭座標(n)の画素周辺の画素ベクトルを取得。 * 2.輪郭座標(n+1)周辺の画素ベクトルと比較。 * 3.差分が一定以下なら、座標と強度を保存 * 4.3点以上の集合になったら、最小二乗法で直線を計算。 * 5.直線の加重値を個々の画素ベクトルの和として返却。 */ public bool traceConture(NyARIntCoordinates i_coord, int i_pos_mag, int i_cell_size, VecLinearCoordinates o_coord) { VecLinearCoordinates.VecLinearCoordinatePoint[] pos = this._tmp_coord_pos; // ベクトル化 int MAX_COORD = o_coord.items.Length; int i_coordlen = i_coord.length; NyARIntPoint2d[] coord = i_coord.items; VecLinearCoordinates.VecLinearCoordinatePoint pos_ptr; //0個目のライン探索 int number_of_data = 0; int sq; long sq_sum = 0; //0番目のピクセル pos[0].scalar = sq = this.getAreaVector33(coord[0].x * i_pos_mag, coord[0].y * i_pos_mag, i_cell_size, i_cell_size, pos[0]); sq_sum += (int)sq; //[2]に0を保管 //1点目だけは前方と後方、両方に探索をかける。 //前方探索の終点 int coord_last_edge = i_coordlen; //後方探索 int sum = 1; double ave_dx = pos[0].dx; double ave_dy = pos[0].dy; for (int i = i_coordlen - 1; i > 0; i--) { // ベクトル取得 pos_ptr = pos[sum]; pos_ptr.scalar = sq = this.getAreaVector33(coord[i].x * i_pos_mag, coord[i].y * i_pos_mag, i_cell_size, i_cell_size, pos_ptr); sq_sum += (int)sq; // 類似度判定 if (checkVecCos(pos[sum], pos[sum - 1], ave_dx, ave_dy)) { //相関なし->前方探索へ。 ave_dx = pos_ptr.dx; ave_dy = pos_ptr.dy; coord_last_edge = i; break; } else { //相関あり- 点の蓄積 ave_dx += pos_ptr.dx; ave_dy += pos_ptr.dy; sum++; } } //前方探索 for (int i = 1; i < coord_last_edge; i++) { // ベクトル取得 pos_ptr = pos[sum]; pos_ptr.scalar = sq = this.getAreaVector33(coord[i].x * i_pos_mag, coord[i].y * i_pos_mag, i_cell_size, i_cell_size, pos_ptr); sq_sum += (int)sq; if (sq == 0) { continue; } //if (pos_ptr.getAbsVecCos(pos[sum-1]) < NyARMath.COS_DEG_5 && pos_ptr.getAbsVecCos(ave_dx,ave_dy)<NyARMath.COS_DEG_20) { if (checkVecCos(pos[sum], pos[sum - 1], ave_dx, ave_dy)) { //相関なし->新しい要素を作る。 if (this.leastSquaresWithNormalize(pos, sum, o_coord.items[number_of_data], sq_sum / (sum * 5))) { number_of_data++; } ave_dx = pos_ptr.dx; ave_dy = pos_ptr.dy; //獲得した値を0へ移動 pos[0].setValue(pos[sum]); sq_sum = 0; sum = 1; } else { //相関あり- 点の蓄積 ave_dx += pos_ptr.dx; ave_dy += pos_ptr.dy; sum++; } // 輪郭中心を出すための計算 if (number_of_data == MAX_COORD) { // 輪郭ベクトルバッファの最大を超えたら失敗 return(false); } } if (this.leastSquaresWithNormalize(pos, sum, o_coord.items[number_of_data], sq_sum / (sum * 5))) { number_of_data++; } // ベクトル化2:最後尾と先頭の要素が似ていれば連結する。 // sq_distの合計を計算 o_coord.length = number_of_data; return(true); }
public void detectMarkerCallback(NyARIntCoordinates i_coord, int[] i_vertex_index) { this._parent.updateSquareInfo(i_coord, i_vertex_index); }
/** * この関数は、輪郭点集合からay+bx+c=0の直線式を計算します。 * @param i_st * 直線計算の対象とする、輪郭点の開始インデックス * @param i_ed * 直線計算の対象とする、輪郭点の終了インデックス * @param i_coord * 輪郭点集合のオブジェクト。 * @param o_line * 直線式を受け取るオブジェクト * @return * 直線式の計算に成功すると、trueを返します。 * @ */ public bool coord2Line(int i_st, int i_ed, NyARIntCoordinates i_coord, NyARLinear o_line) { //頂点を取得 int n, st, ed; double w1; int cood_num = i_coord.length; //探索区間の決定 if (i_ed >= i_st) { //頂点[i]から頂点[i+1]までの輪郭が、1区間にあるとき w1 = (double)(i_ed - i_st + 1) * 0.05 + 0.5; //探索区間の決定 st = (int)(i_st + w1); ed = (int)(i_ed - w1); } else { //頂点[i]から頂点[i+1]までの輪郭が、2区間に分かれているとき w1 = (double)((i_ed + cood_num - i_st + 1) % cood_num) * 0.05 + 0.5; //探索区間の決定 st = ((int)(i_st + w1)) % cood_num; ed = ((int)(i_ed + cood_num - w1)) % cood_num; } //探索区間数を確認 if (st <= ed) { //探索区間は1区間 n = ed - st + 1; if (this._dist_factor != null) { this._dist_factor.observ2IdealBatch(i_coord.items, st, n, this._xpos, this._ypos, 0); } } else { //探索区間は2区間 n = ed + 1 + cood_num - st; if (this._dist_factor != null) { this._dist_factor.observ2IdealBatch(i_coord.items, st, cood_num - st, this._xpos, this._ypos, 0); this._dist_factor.observ2IdealBatch(i_coord.items, 0, ed + 1, this._xpos, this._ypos, cood_num - st); } } //要素数の確認 if (n < 2) { // nが2以下でmatrix.PCAを計算することはできないので、エラー return(false); } //主成分分析する。 NyARDoubleMatrix22 evec = this.__getSquareLine_evec; double[] mean = this.__getSquareLine_mean; this._pca.pca(this._xpos, this._ypos, n, evec, this.__getSquareLine_ev, mean); o_line.a = evec.m01; // line[i][0] = evec->m[1]; o_line.b = -evec.m00; // line[i][1] = -evec->m[0]; o_line.c = -(o_line.a * mean[0] + o_line.b * mean[1]); // line[i][2] = -(line[i][0]*mean->v[0] + line[i][1]*mean->v[1]); return(true); }
/** * 矩形が見付かるたびに呼び出されます。 * 発見した矩形のパターンを検査して、方位を考慮した頂点データを確保します。 */ public void detectMarkerCallback(NyARIntCoordinates i_coord, int[] i_vertex_index) { //既に発見済なら終了 if (this.marker_data != null) { return; } //輪郭座標から頂点リストに変換 NyARIntPoint2d[] vertex = this.__ref_vertex; vertex[0] = i_coord.items[i_vertex_index[0]]; vertex[1] = i_coord.items[i_vertex_index[1]]; vertex[2] = i_coord.items[i_vertex_index[2]]; vertex[3] = i_coord.items[i_vertex_index[3]]; NyIdMarkerParam param = this._marker_param; NyIdMarkerPattern patt_data = this._marker_data; // 評価基準になるパターンをイメージから切り出す if (!this._id_pickup.pickFromRaster(this._ref_raster.getGsPixelDriver(), vertex, patt_data, param)) { return; } //エンコード if (!this._encoder.encode(patt_data, this._data_temp)) { return; } //継続認識要求されている? if (this._prev_data == null) { //継続認識要求なし this._current_data.copyFrom(this._data_temp); } else { //継続認識要求あり if (!this._prev_data.isEqual((this._data_temp))) { return;//認識請求のあったIDと違う。 } } //新しく認識、または継続認識中に更新があったときだけ、Square情報を更新する。 //ココから先はこの条件でしか実行されない。 NyARSquare sq = this.square; //directionを考慮して、squareを更新する。 for (int i = 0; i < 4; i++) { int idx = (i + 4 - param.direction) % 4; this._coordline.coord2Line(i_vertex_index[idx], i_vertex_index[(idx + 1) % 4], i_coord, sq.line[i]); } for (int i = 0; i < 4; i++) { //直線同士の交点計算 if (!sq.line[i].crossPos(sq.line[(i + 3) % 4], sq.sqvertex[i])) { throw new NyARException();//ここのエラー復帰するならダブルバッファにすればOK } } this.threshold = param.threshold; this.marker_data = this._current_data;//みつかった。 }
/** * 矩形が見付かるたびに呼び出されます。 * 発見した矩形のパターンを検査して、方位を考慮した頂点データを確保します。 */ public void detectMarkerCallback(NyARIntCoordinates i_coord, int[] i_vertex_index) { NyARMatchPattResult mr = this.__detectMarkerLite_mr; //輪郭座標から頂点リストに変換 NyARIntPoint2d[] vertex = this.__ref_vertex; vertex[0] = i_coord.items[i_vertex_index[0]]; vertex[1] = i_coord.items[i_vertex_index[1]]; vertex[2] = i_coord.items[i_vertex_index[2]]; vertex[3] = i_coord.items[i_vertex_index[3]]; //画像を取得 if (!this._inst_patt.pickFromRaster(this._ref_raster, vertex)) { return; } //取得パターンをカラー差分データに変換して評価する。 this._deviation_data.setRaster(this._inst_patt); //最も一致するパターンを割り当てる。 int square_index, direction; double confidence; this._match_patt[0].evaluate(this._deviation_data, mr); square_index = 0; direction = mr.direction; confidence = mr.confidence; //2番目以降 for (int i = 1; i < this._match_patt.Length; i++) { this._match_patt[i].evaluate(this._deviation_data, mr); if (confidence > mr.confidence) { continue; } // もっと一致するマーカーがあったぽい square_index = i; direction = mr.direction; confidence = mr.confidence; } //最も一致したマーカ情報を、この矩形の情報として記録する。 NyARDetectMarkerResult result = this.result_stack.prePush(); result.arcode_id = square_index; result.confidence = confidence; NyARSquare sq = result.square; //directionを考慮して、squareを更新する。 for (int i = 0; i < 4; i++) { int idx = (i + 4 - direction) % 4; this._coordline.coord2Line(i_vertex_index[idx], i_vertex_index[(idx + 1) % 4], i_coord, sq.line[i]); } for (int i = 0; i < 4; i++) { //直線同士の交点計算 if (!sq.line[i].crossPos(sq.line[(i + 3) % 4], sq.sqvertex[i])) { throw new NyARException();//ここのエラー復帰するならダブルバッファにすればOK } } }
/** * この関数は、ラスタから矩形を検出して、自己コールバック関数{@link #onSquareDetect}で通知します。 * 実装クラスでは、矩形検出処理をして、結果を通知する処理を実装してください。 * @param i_raster * 検出元のラスタ画像 * @ */ public void detectMarker(NyARBinRaster i_raster, NyARSquareContourDetector.CbHandler i_cb) { NyARLabelingImage limage = this._limage; // ラベル数が0ならここまで int label_num = this._labeling.labeling(i_raster, this._limage); if (label_num < 1) { return; } NyARLabelingLabelStack stack = limage.getLabelStack(); //ラベルをソートしておく stack.sortByArea(); // NyARLabelingLabel[] labels = stack.getArray(); // デカいラベルを読み飛ばし int i; for (i = 0; i < label_num; i++) { // 検査対象内のラベルサイズになるまで無視 if (labels[i].area <= AR_AREA_MAX) { break; } } int xsize = this._width; int ysize = this._height; NyARIntCoordinates coord = this._coord; int[] mkvertex = this.__detectMarker_mkvertex; NyARLabelOverlapChecker <NyARLabelingLabel> overlap = this._overlap_checker; //重なりチェッカの最大数を設定 overlap.setMaxLabels(label_num); for (; i < label_num; i++) { NyARLabelingLabel label_pt = labels[i]; int 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; } // 輪郭を取得 if (!this._cpickup.getContour(limage, limage.getTopClipTangentX(label_pt), label_pt.clip_t, coord)) { continue; } //輪郭線をチェックして、矩形かどうかを判定。矩形ならばmkvertexに取得 if (!this._coord2vertex.getVertexIndexes(coord, label_area, mkvertex)) { // 頂点の取得が出来なかった continue; } //矩形を発見したことをコールバック関数で通知 i_cb.detectMarkerCallback(coord, mkvertex); // 検出済の矩形の属したラベルを重なりチェックに追加する。 overlap.push(label_pt); } return; }
/** * この関数は、輪郭点集合からay+bx+c=0の直線式を計算します。 * @param i_st * 直線計算の対象とする、輪郭点の開始インデックス * @param i_ed * 直線計算の対象とする、輪郭点の終了インデックス * @param i_coord * 輪郭点集合のオブジェクト。 * @param o_line * 直線式を受け取るオブジェクト * @return * 直線式の計算に成功すると、trueを返します。 * @ */ public bool coord2Line(int i_st, int i_ed, NyARIntCoordinates i_coord, NyARLinear o_line) { //頂点を取得 int n, st, ed; double w1; int cood_num = i_coord.length; //探索区間の決定 if (i_ed >= i_st) { //頂点[i]から頂点[i+1]までの輪郭が、1区間にあるとき w1 = (double)(i_ed - i_st + 1) * 0.05 + 0.5; //探索区間の決定 st = (int)(i_st + w1); ed = (int)(i_ed - w1); } else { //頂点[i]から頂点[i+1]までの輪郭が、2区間に分かれているとき w1 = (double)((i_ed + cood_num - i_st + 1) % cood_num) * 0.05 + 0.5; //探索区間の決定 st = ((int)(i_st + w1)) % cood_num; ed = ((int)(i_ed + cood_num - w1)) % cood_num; } //探索区間数を確認 if (st <= ed) { //探索区間は1区間 n = ed - st + 1; if (this._dist_factor != null) { this._dist_factor.observ2IdealBatch(i_coord.items, st, n, this._xpos, this._ypos, 0); } } else { //探索区間は2区間 n = ed + 1 + cood_num - st; if (this._dist_factor != null) { this._dist_factor.observ2IdealBatch(i_coord.items, st, cood_num - st, this._xpos, this._ypos, 0); this._dist_factor.observ2IdealBatch(i_coord.items, 0, ed + 1, this._xpos, this._ypos, cood_num - st); } } //要素数の確認 if (n < 2) { // nが2以下でmatrix.PCAを計算することはできないので、エラー return false; } //主成分分析する。 NyARDoubleMatrix22 evec = this.__getSquareLine_evec; double[] mean = this.__getSquareLine_mean; this._pca.pca(this._xpos, this._ypos, n, evec, this.__getSquareLine_ev, mean); o_line.a = evec.m01;// line[i][0] = evec->m[1]; o_line.b = -evec.m00;// line[i][1] = -evec->m[0]; o_line.c = -(o_line.a * mean[0] + o_line.b * mean[1]);// line[i][2] = -(line[i][0]*mean->v[0] + line[i][1]*mean->v[1]); return true; }
protected override void onSquareDetect(NyARIntCoordinates i_coord, int[] i_vertex_index) { this._parent.updateSquareInfo(i_coord, i_vertex_index); }
/** * この関数は、ラスタの指定点を基点に、輪郭線を抽出します。 * 開始点は、輪郭の一部である必要があります。 * 通常は、ラべリングの結果の上辺クリップとX軸エントリポイントを開始点として入力します。 * @param i_raster * 輪郭線を抽出するラスタを指定します。 * @param i_entry_x * 輪郭抽出の開始点です。 * @param i_entry_y * 輪郭抽出の開始点です。 * @param o_coord * 輪郭点を格納するオブジェクトを指定します。 * @return * 輪郭線がo_coordの長さを超えた場合、falseを返します。 * @ */ public bool getContour(NyARLabelingImage i_raster, int i_entry_x, int i_entry_y, NyARIntCoordinates o_coord) { int[] xdir = _getContour_xdir;// static int xdir[8] = { 0, 1, 1, 1, 0,-1,-1,-1}; int[] ydir = _getContour_ydir;// static int ydir[8] = {-1,-1, 0, 1, 1, 1, 0,-1}; int[] i_buf = (int[])i_raster.getBuffer(); int width = i_raster.getWidth(); int height = i_raster.getHeight(); NyARIntPoint2d[] coord = o_coord.items; int i_array_size = o_coord.items.Length; //クリップ領域の上端に接しているポイントを得る。 int sx = i_entry_x; int sy = i_entry_y; int coord_num = 1; coord[0].x = sx; coord[0].y = sy; int dir = 5; int c = coord[0].x; int r = coord[0].y; for (; ; ) { dir = (dir + 5) % 8;//dirの正規化 //ここは頑張ればもっと最適化できると思うよ。 //4隅以外の境界接地の場合に、境界チェックを省略するとかね。 if (c >= 1 && c < width - 1 && r >= 1 && r < height - 1) { for (; ; ) {//gotoのエミュレート用のfor文 //境界に接していないとき if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } dir++; if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } dir++; if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } dir++; if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } dir++; if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } dir++; if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } dir++; if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } dir++; if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } //8方向全て調べたけどラベルが無いよ? throw new NyARException(); } } else { //境界に接しているとき int i; for (i = 0; i < 8; i++) { int x = c + xdir[dir]; int y = r + ydir[dir]; //境界チェック if (x >= 0 && x < width && y >= 0 && y < height) { if (i_buf[(y) * width + (x)] > 0) { break; } } dir++;//倍長テーブルを参照するので問題なし } if (i == 8) { //8方向全て調べたけどラベルが無いよ? throw new NyARException();// return(-1); } } dir = dir % 8;//dirの正規化 // xcoordとycoordをc,rにも保存 c = c + xdir[dir]; r = r + ydir[dir]; coord[coord_num].x = c; coord[coord_num].y = r; // 終了条件判定 if (c == sx && r == sy) { coord_num++; break; } coord_num++; if (coord_num == i_array_size) { //輪郭が末端に達した return false; } } o_coord.length = coord_num; return true; }
/** * この関数は、座標集合から頂点候補になりそうな場所を4箇所探して、そのインデクス番号を返します。 * @param i_coord * 輪郭点集合を格納したオブジェクト。 * @param i_area * 矩形判定のヒント値。矩形の大きさを、そのラベルを構成するピクセルの数で指定します。 * (注)このパラメータは、マーカノデザイン、枠の大きさが影響等、ラベルの大きさに影響を受けます。 * @param o_vertex * 4頂点のインデクスを受け取る配列です。4要素以上の配列を指定してください。 * @return * 頂点が見つかるとtrueを返します。 */ public bool getVertexIndexes(NyARIntCoordinates i_coord, int i_area, int[] o_vertex) { NyARVertexCounter wv1 = this.__getSquareVertex_wv1; NyARVertexCounter wv2 = this.__getSquareVertex_wv2; int i_coord_num = i_coord.length; int vertex1_index = getFarPoint(i_coord.items, i_coord_num, 0); int prev_vertex_index = (vertex1_index + i_coord_num) % i_coord_num; int v1 = getFarPoint(i_coord.items, i_coord_num, vertex1_index); double thresh = (i_area / 0.75) * 0.01 * VERTEX_FACTOR; o_vertex[0] = vertex1_index; if (!wv1.getVertex(i_coord.items, i_coord_num, vertex1_index, v1, thresh)) { return false; } if (!wv2.getVertex(i_coord.items, i_coord_num, v1, prev_vertex_index, thresh)) { return false; } int v2; if (wv1.number_of_vertex == 1 && wv2.number_of_vertex == 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) { //頂点位置を、起点から対角点の間の1/2にあると予想して、検索する。 if (v1 >= vertex1_index) { v2 = (v1 - vertex1_index) / 2 + vertex1_index; } else { v2 = ((v1 + i_coord_num - vertex1_index) / 2 + vertex1_index) % i_coord_num; } if (!wv1.getVertex(i_coord.items, i_coord_num, vertex1_index, v2, thresh)) { return false; } if (!wv2.getVertex(i_coord.items, i_coord_num, 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+ end_of_coord)/2; if (v1 <= prev_vertex_index) { v2 = (v1 + prev_vertex_index) / 2; } else { v2 = ((v1 + i_coord_num + prev_vertex_index) / 2) % i_coord_num; } if (!wv1.getVertex(i_coord.items, i_coord_num, v1, v2, thresh)) { return false; } if (!wv2.getVertex(i_coord.items, i_coord_num, v2, prev_vertex_index, 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; } return true; }
public void detectMarkerCallback(NyARIntCoordinates i_coord, int[] i_vertex_index) { //とりあえずSquareスタックを予約 SquareStack.Item sq_tmp = this._sq_stack.prePush(); //確保できない(1つのdetectorが複数の候補を得る場合(同じARマーカが多くある場合など)に発生することがある。) if (sq_tmp == null) { return; } //観測座標点の記録 for (int i2 = 0; i2 < 4; i2++) { sq_tmp.ob_vertex[i2].setValue(i_coord.items[i_vertex_index[i2]]); } //頂点分布を計算 sq_tmp.vertex_area.setAreaRect(sq_tmp.ob_vertex, 4); //頂点座標の中心を計算 sq_tmp.center2d.setCenterPos(sq_tmp.ob_vertex, 4); //矩形面積 sq_tmp.rect_area = sq_tmp.vertex_area.w * sq_tmp.vertex_area.h; bool is_target_marker = false; for (;;) { //トラッキング対象か確認する。 if (this._ref_tracking_list.update(sq_tmp)) { //トラッキング対象ならブレーク is_target_marker = true; break; } //@todo 複数マーカ時に、トラッキング済のarmarkerを探索対象外に出来ない? //nyIdマーカの特定(IDマーカの特定はここで完結する。) if (this._ref_idmk_list.Count > 0) { if (this._ref_idmk_list.update(this._ref_input_gs, sq_tmp)) { is_target_marker = true; break;//idマーカを特定 } } //PSARマーカの特定(IDマーカの特定はここで完結する。) if (this._ref_psmk_list.Count > 0) { if (this._ref_psmk_list.update(this._ref_input_gs, sq_tmp)) { is_target_marker = true; break;//idマーカを特定 } } //ARマーカの特定 if (this._ref_armk_list.Count > 0) { //敷居値により1個のマーカに対して複数の候補が見つかることもある。 if (this._ref_armk_list.update(this._ref_input_rfb, sq_tmp)) { is_target_marker = true; break; } } break; } //この矩形が検出対象なら、矩形情報を精密に再計算 if (is_target_marker) { //矩形は検出対象にマークされている。 for (int i2 = 0; i2 < 4; i2++) { this._coordline.coord2Line(i_vertex_index[i2], i_vertex_index[(i2 + 1) % 4], i_coord, sq_tmp.line[i2]); } for (int i2 = 0; i2 < 4; i2++) { //直線同士の交点計算 if (!sq_tmp.line[i2].crossPos(sq_tmp.line[(i2 + 3) % 4], sq_tmp.sqvertex[i2])) { throw new NyARRuntimeException();//まずない。ありえない。 } } } else { //この矩形は検出対象にマークされなかったので、解除 this._sq_stack.pop(); } }
/** * 矩形が見付かるたびに呼び出されます。 * 発見した矩形のパターンを検査して、方位を考慮した頂点データを確保します。 */ public void detectMarkerCallback(NyARIntCoordinates i_coord, int[] i_vertex_index) { if (this._match_patt == null) { return; } //輪郭座標から頂点リストに変換 NyARIntPoint2d[] vertex = this.__ref_vertex; vertex[0] = i_coord.items[i_vertex_index[0]]; vertex[1] = i_coord.items[i_vertex_index[1]]; vertex[2] = i_coord.items[i_vertex_index[2]]; vertex[3] = i_coord.items[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にデータを得る。 NyARMatchPattResult mr = this.__detectMarkerLite_mr; int lcode_index = 0; int dir = 0; double c1 = 0; for (int i = 0; i < this._match_patt.Length; i++) { this._match_patt[i].evaluate(this._deviation_data, mr); double c2 = 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; NyARSquare sq = this.square; //directionを考慮して、squareを更新する。 for (int i = 0; i < 4; i++) { int idx = (i + 4 - dir) % 4; this._coordline.coord2Line(i_vertex_index[idx], i_vertex_index[(idx + 1) % 4], i_coord, sq.line[i]); } for (int i = 0; i < 4; i++) { //直線同士の交点計算 if (!sq.line[i].crossPos(sq.line[(i + 3) % 4], sq.sqvertex[i])) { throw new NyARException();//ここのエラー復帰するならダブルバッファにすればOK } } }
/** * この関数は、ラスタの指定点を基点に、輪郭線を抽出します。 * 開始点は、輪郭の一部である必要があります。 * 通常は、ラべリングの結果の上辺クリップとX軸エントリポイントを開始点として入力します。 * @param i_raster * 輪郭線を抽出するラスタを指定します。 * @param i_entry_x * 輪郭抽出の開始点です。 * @param i_entry_y * 輪郭抽出の開始点です。 * @param o_coord * 輪郭点を格納するオブジェクトを指定します。 * @return * 輪郭線がo_coordの長さを超えた場合、falseを返します。 * @ */ public bool getContour(NyARLabelingImage i_raster, int i_entry_x, int i_entry_y, NyARIntCoordinates o_coord) { int[] xdir = _getContour_xdir; // static int xdir[8] = { 0, 1, 1, 1, 0,-1,-1,-1}; int[] ydir = _getContour_ydir; // static int ydir[8] = {-1,-1, 0, 1, 1, 1, 0,-1}; int[] i_buf = (int[])i_raster.getBuffer(); int width = i_raster.getWidth(); int height = i_raster.getHeight(); NyARIntPoint2d[] coord = o_coord.items; int i_array_size = o_coord.items.Length; //クリップ領域の上端に接しているポイントを得る。 int sx = i_entry_x; int sy = i_entry_y; int coord_num = 1; coord[0].x = sx; coord[0].y = sy; int dir = 5; int c = coord[0].x; int r = coord[0].y; for (; ;) { dir = (dir + 5) % 8;//dirの正規化 //ここは頑張ればもっと最適化できると思うよ。 //4隅以外の境界接地の場合に、境界チェックを省略するとかね。 if (c >= 1 && c < width - 1 && r >= 1 && r < height - 1) { for (; ;) {//gotoのエミュレート用のfor文 //境界に接していないとき if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } dir++; if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } dir++; if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } dir++; if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } dir++; if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } dir++; if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } dir++; if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } dir++; if (i_buf[(r + ydir[dir]) * width + (c + xdir[dir])] > 0) { break; } //8方向全て調べたけどラベルが無いよ? throw new NyARException(); } } else { //境界に接しているとき int i; for (i = 0; i < 8; i++) { int x = c + xdir[dir]; int y = r + ydir[dir]; //境界チェック if (x >= 0 && x < width && y >= 0 && y < height) { if (i_buf[(y) * width + (x)] > 0) { break; } } dir++;//倍長テーブルを参照するので問題なし } if (i == 8) { //8方向全て調べたけどラベルが無いよ? throw new NyARException();// return(-1); } } dir = dir % 8;//dirの正規化 // xcoordとycoordをc,rにも保存 c = c + xdir[dir]; r = r + ydir[dir]; coord[coord_num].x = c; coord[coord_num].y = r; // 終了条件判定 if (c == sx && r == sy) { coord_num++; break; } coord_num++; if (coord_num == i_array_size) { //輪郭が末端に達した return(false); } } o_coord.length = coord_num; return(true); }
/** * この関数は、座標集合から頂点候補になりそうな場所を4箇所探して、そのインデクス番号を返します。 * @param i_coord * 輪郭点集合を格納したオブジェクト。 * @param i_area * 矩形判定のヒント値。矩形の大きさを、そのラベルを構成するピクセルの数で指定します。 * (注)このパラメータは、マーカノデザイン、枠の大きさが影響等、ラベルの大きさに影響を受けます。 * @param o_vertex * 4頂点のインデクスを受け取る配列です。4要素以上の配列を指定してください。 * @return * 頂点が見つかるとtrueを返します。 */ public bool getVertexIndexes(NyARIntCoordinates i_coord, int i_area, int[] o_vertex) { NyARVertexCounter wv1 = this.__getSquareVertex_wv1; NyARVertexCounter wv2 = this.__getSquareVertex_wv2; int i_coord_num = i_coord.length; int vertex1_index = getFarPoint(i_coord.items, i_coord_num, 0); int prev_vertex_index = (vertex1_index + i_coord_num) % i_coord_num; int v1 = getFarPoint(i_coord.items, i_coord_num, vertex1_index); double thresh = (i_area / 0.75) * 0.01 * VERTEX_FACTOR; o_vertex[0] = vertex1_index; if (!wv1.getVertex(i_coord.items, i_coord_num, vertex1_index, v1, thresh)) { return(false); } if (!wv2.getVertex(i_coord.items, i_coord_num, v1, prev_vertex_index, thresh)) { return(false); } int v2; if (wv1.number_of_vertex == 1 && wv2.number_of_vertex == 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) { //頂点位置を、起点から対角点の間の1/2にあると予想して、検索する。 if (v1 >= vertex1_index) { v2 = (v1 - vertex1_index) / 2 + vertex1_index; } else { v2 = ((v1 + i_coord_num - vertex1_index) / 2 + vertex1_index) % i_coord_num; } if (!wv1.getVertex(i_coord.items, i_coord_num, vertex1_index, v2, thresh)) { return(false); } if (!wv2.getVertex(i_coord.items, i_coord_num, 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+ end_of_coord)/2; if (v1 <= prev_vertex_index) { v2 = (v1 + prev_vertex_index) / 2; } else { v2 = ((v1 + i_coord_num + prev_vertex_index) / 2) % i_coord_num; } if (!wv1.getVertex(i_coord.items, i_coord_num, v1, v2, thresh)) { return(false); } if (!wv2.getVertex(i_coord.items, i_coord_num, v2, prev_vertex_index, 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); } return(true); }