/* * PROJECT: NyARToolkit(Extension) * -------------------------------------------------------------------------------- * The NyARToolkit is Java edition ARToolKit class library. * Copyright (C)2008-2009 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 <http://www.gnu.org/licenses/>. * * For further information please contact. * http://nyatla.jp/nyatoolkit/ * <airmail(at)ebony.plala.or.jp> or <nyatla(at)nyatla.jp> * */ package jp.nyatla.nyartoolkit.core.utils; import jp.nyatla.nyartoolkit.core.types.NyARIntPoint2d; /** * このクラスは、距離マップを利用した、頂点集合同士のマッチング処理機能を提供します。 * 2つの点集合同士を比較して、集合の各点同士の距離が最も近くになる組み合わせを計算します。 * <p>アルゴリズム - * 2つの点集合の総当たりの距離マップを作り、その距離が小さいのものから順に抽出することで、 * 其々の点の移動距離が最小になる組み合わせを計算します。 * </p> * <p>使い方 - * このクラスは、まず距離マップを作るために距離値をセットして、次に組み合わせを得る手順で使います。 * 距離マップには行と列があります。列を基準値、行を比較値として、その距離値を格納します。 * 行と列の距離マップを作り終えたら、組合せを計算します。 * <ol> * <li>{@link #setMapSize}関数で、マップサイズ(比較する頂点数)を設定する。 * <li>{@link #setDist},または{@link #setPointDists}で、距離マップに全ての値を書き込む。 * <li> * </ol> * </p> */ public class NyARDistMap { /** 処理用のデータ型*/ protected class DistItem { public int row; public int col; public int dist; } /** 距離マップ用の配列*/ protected DistItem[] _map; /** ワーク変数*/ protected int _min_dist; /** ワーク変数*/ protected int _min_dist_index; /** ワーク変数*/ protected int _size_row; /** ワーク変数*/ protected int _size_col; /** * コンストラクタです。 * マップの最大サイズを指定して、インスタンスを作成します。 * @param i_max_col * マップの最大列数 * @param i_max_row * マップの最大行数 */ public NyARDistMap(int i_max_col,int i_max_row) { this._min_dist=Integer.MAX_VALUE; this._min_dist_index=0; this._size_col=i_max_col; this._size_row=i_max_row; this._map=new DistItem[i_max_col*i_max_row]; for(int i=0;i<i_max_col*i_max_row;i++){ this._map[i]=new DistItem(); } } /** * この関数は、マップのサイズを指定します。 * 値は初期化されません。 * @param i_col * 新しい列数。 * @param i_row * 新しい行数。 */ public void setMapSize(int i_col,int i_row) { this._size_row=i_row; this._size_col=i_col; } /** * この関数は、列と行を指定して、距離値1個をマップにセットします。 * <p>注意 - * このAPIは低速です。性能が必要な時は、{@link #setPointDists}を参考に、一括書込みする関数を検討してください。 * </p> * @param i_col * 列のインデクスを指定します。 * @param i_row * 行のインデクスを指定します。 * @param i_dist * その行と列の距離値を指定します。 */ public void setDist(int i_col,int i_row,int i_dist) { this._min_dist_index=this._size_col*i_row+i_col; DistItem item=this._map[this._min_dist_index]; item.col=i_col; item.row=i_row; item.dist=i_dist; //最小値・最大値の再計算 if(i_dist<this._min_dist){ this._min_dist=i_dist; } return; } /** * この関数は、2つの座標点配列同士の距離値を一括してマップにセットします。 * <p> * 実装メモ - * 点のフォーマットが合わない実装されている場合は、この関数参考にオーバーロードしてください。 * </p> * @param i_vertex_r * 比較する頂点群を格納した配列。 * @param i_row_len * i_vertex_rの有効な要素数 * @param i_vertex_c * 基準となる頂点群を格納した配列 * @param i_col_len * i_vertex_cの有効な要素数 */ public void setPointDists(NyARIntPoint2d[] i_vertex_r,int i_row_len,NyARIntPoint2d[] i_vertex_c,int i_col_len) { DistItem[] map=this._map; //distortionMapを作成。ついでに最小値のインデクスも取得 int min_index=0; int min_dist =Integer.MAX_VALUE; int idx=0; for(int r=0;r<i_row_len;r++){ for(int c=0;c<i_col_len;c++){ map[idx].col=c; map[idx].row=r; int d=i_vertex_r[r].sqDist(i_vertex_c[c]); map[idx].dist=d; if(min_dist>d){ min_index=idx; min_dist=d; } idx++; } } this._min_dist=min_dist; this._min_dist_index=min_index; this._size_col=i_col_len; this._size_row=i_row_len; return; } /** * この関数は、現在の距離マップから、col要素に対するrow要素の組合せを計算します。 * colに対して適したrow要素が見つからない場合は、o_rowindexの該当要素に-1を設定します。 * この関数は内部データを不可逆に変更します。計算後は、距離マップの再セットが必要です。 * @param o_rowindex * 組合せを受け取る配列です。 * col[n]に対するrow[m]のインデクス番号mを、o_rowindex[n]に返します。 */ public void getMinimumPair(int[] o_rowindex) { DistItem[] map=this._map; int map_length=this._size_col*this._size_row; int col_len=this._size_col; //[0]と差し替え DistItem temp_map; temp_map=map[0]; map[0]=map[this._min_dist_index]; map[this._min_dist_index]=temp_map; for(int i=0;i<o_rowindex.length;i++){ o_rowindex[i]=-1; } if(map_length==0){ return; } //値の保管 o_rowindex[map[0].col]=map[0].row; //ソートして、0番目以降のデータを探す for(int i=1;i<col_len;i++){ int min_index=0; //r,cのものを除外しながら最小値を得る。 int reject_c=map[i-1].col; int reject_r=map[i-1].row; int min_dist=Integer.MAX_VALUE; if(1>=map_length-col_len){ break; } for(int i2=i;i2<map_length;){ //除外条件? if(map[i2].col==reject_c || map[i2].row==reject_r){ //非検索対象→インスタンスの差し替えと、検索長の減算 temp_map=map[i2]; map[i2]=map[map_length-1]; map[map_length-1]=temp_map; map_length--; }else{ int d=map[i2].dist; if(min_dist>d){ min_index=i2; min_dist=d; } i2++; } } //[i]の値の差し替え temp_map=map[i]; map[i]=map[min_index]; map[min_index]=temp_map; //値の保管 o_rowindex[map[i].col]=map[i].row; } return; } }