OSDN Git Service

[NyARToolKit for java]update document
[nyartoolkit-and/nyartoolkit-and.git] / lib / src / jp / nyatla / nyartoolkit / processor / SingleARMarkerProcesser.java
1 /* \r
2  * Capture Test NyARToolkitCSサンプルプログラム\r
3  * --------------------------------------------------------------------------------\r
4  * The NyARToolkit is Java edition ARToolKit class library.\r
5  * Copyright (C)2008-2009 Ryo Iizuka\r
6  *\r
7  * This program is free software: you can redistribute it and/or modify\r
8  * it under the terms of the GNU General Public License as published by\r
9  * the Free Software Foundation, either version 3 of the License, or\r
10  * (at your option) any later version.\r
11  * \r
12  * This program is distributed in the hope that it will be useful,\r
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15  * GNU General Public License for more details.\r
16  *\r
17  * You should have received a copy of the GNU General Public License\r
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.\r
19  * \r
20  * For further information please contact.\r
21  *      http://nyatla.jp/nyatoolkit/\r
22  *      <airmail(at)ebony.plala.or.jp> or <nyatla(at)nyatla.jp>\r
23  * \r
24  */\r
25 package jp.nyatla.nyartoolkit.processor;\r
26 \r
27 import jp.nyatla.nyartoolkit.NyARException;\r
28 import jp.nyatla.nyartoolkit.core.*;\r
29 import jp.nyatla.nyartoolkit.core.analyzer.raster.threshold.*;\r
30 import jp.nyatla.nyartoolkit.core.match.*;\r
31 import jp.nyatla.nyartoolkit.core.param.*;\r
32 import jp.nyatla.nyartoolkit.core.pickup.*;\r
33 import jp.nyatla.nyartoolkit.core.raster.*;\r
34 import jp.nyatla.nyartoolkit.core.raster.rgb.*;\r
35 import jp.nyatla.nyartoolkit.core.transmat.*;\r
36 import jp.nyatla.nyartoolkit.core.rasterfilter.rgb2bin.NyARRasterFilter_ARToolkitThreshold;\r
37 import jp.nyatla.nyartoolkit.core.types.*;\r
38 import jp.nyatla.nyartoolkit.core.squaredetect.*;\r
39 \r
40 /**\r
41  * このクラスは、1個のARマーカを検出する処理を、イベントドリブンにするシーケンスを定義します。\r
42  * マーカの出現・移動・消滅を、自己コールバック関数で通知することができます。\r
43  * クラスにはマーカパターンテーブルがあり、そこに複数種類のマーカを登録することができます。\r
44  * 一つのマーカが見つかると、プロセッサは継続して同じマーカを1つだけ認識し続け、見失うまでの間は他のマーカを認識しません。\r
45  * <p>イベントの説明-\r
46  * このクラスには、3個の自己コールバック関数があります。{@link SingleARMarkerProcesser}は、以下のタイミングでこれらを呼び出します。\r
47  * ユーザは継承クラスでこれらの関数に実装を行い、イベント駆動のアプリケーションを作成できます。\r
48  * <ul>\r
49  * <li> {@link #onEnterHandler} - 登録したマーカが初めて見つかった時に呼び出されます。ここに、発見したマーカに対応した初期処理を書きます。\r
50  * <li> {@link #onLeaveHandler} - 検出中のマーカが消失した時に呼び出されます。ここに、マーカの終期処理を書きます。\r
51  * <li> {@link #onUpdateHandler}- 検出中のマーカの位置姿勢が更新されたときに呼び出されます。ここに、マーカ位置の更新処理を書きます。\r
52  * </ul>\r
53  * <p>特性-\r
54  * <ul>\r
55  * <li>自動敷居値調整を行うため、環境光の変化に耐性があります。\r
56  * <li>複数のマーカが画像にある場合は、一番初めに認識したマーカを優先して認識します。\r
57  * <li>複数の同一パターンマーカが画像にある場合は、区別できません。\r
58  * </ul>\r
59  * </p>\r
60 \r
61  */\r
62 public abstract class SingleARMarkerProcesser\r
63 {\r
64         /**\r
65          * detectMarkerのコールバック関数\r
66          */\r
67         private class DetectSquare extends NyARSquareContourDetector_Rle\r
68         {\r
69                 //公開プロパティ\r
70                 public final NyARSquare square=new NyARSquare();\r
71                 public double confidence=0.0;\r
72                 public int code_index=-1;               \r
73                 public double cf_threshold_new = 0.50;\r
74                 public double cf_threshold_exist = 0.30;\r
75                 \r
76                 //参照\r
77                 private INyARRgbRaster _ref_raster;\r
78                 //所有インスタンス\r
79                 private INyARColorPatt _inst_patt;\r
80                 private NyARMatchPattDeviationColorData _deviation_data;\r
81                 private NyARMatchPatt_Color_WITHOUT_PCA[] _match_patt;\r
82                 private final NyARMatchPattResult __detectMarkerLite_mr=new NyARMatchPattResult();\r
83                 private NyARCoord2Linear _coordline;\r
84                 private int _raster_type;\r
85                 \r
86                 public DetectSquare(NyARParam i_param,int i_raster_type) throws NyARException\r
87                 {\r
88                         super(i_param.getScreenSize());\r
89                         this._match_patt=null;\r
90                         this._coordline=new NyARCoord2Linear(i_param.getScreenSize(),i_param.getDistortionFactor());\r
91                         this._raster_type=i_raster_type;\r
92                         return;\r
93                 }\r
94                 public void setNyARCodeTable(NyARCode[] i_ref_code,int i_code_resolution)\r
95                 {\r
96                         /*unmanagedで実装するときは、ここでリソース解放をすること。*/\r
97                         this._deviation_data=new NyARMatchPattDeviationColorData(i_code_resolution,i_code_resolution);\r
98                         this._inst_patt=new NyARColorPatt_Perspective_O2(i_code_resolution,i_code_resolution,4,25,this._raster_type);\r
99                         this._match_patt = new NyARMatchPatt_Color_WITHOUT_PCA[i_ref_code.length];\r
100                         for(int i=0;i<i_ref_code.length;i++){\r
101                                 this._match_patt[i]=new NyARMatchPatt_Color_WITHOUT_PCA(i_ref_code[i]);\r
102                         }\r
103                 }\r
104                 private NyARIntPoint2d[] __ref_vertex=new NyARIntPoint2d[4];\r
105                 private int _target_id;\r
106                 /**\r
107                  * Initialize call back handler.\r
108                  */\r
109                 public void init(INyARRgbRaster i_raster,int i_target_id)\r
110                 {\r
111                         this._ref_raster=i_raster;\r
112                         this._target_id=i_target_id;\r
113                         this.code_index=-1;\r
114                         this.confidence=Double.MIN_VALUE;\r
115                 }\r
116 \r
117                 /**\r
118                  * 矩形が見付かるたびに呼び出されます。\r
119                  * 発見した矩形のパターンを検査して、方位を考慮した頂点データを確保します。\r
120                  */\r
121                 protected void onSquareDetect(NyARIntCoordinates i_coord,int[] i_vertex_index)  throws NyARException\r
122                 {\r
123                         if (this._match_patt==null) {\r
124                                 return;\r
125                         }\r
126                         //輪郭座標から頂点リストに変換\r
127                         NyARIntPoint2d[] vertex=this.__ref_vertex;\r
128                         vertex[0]=i_coord.items[i_vertex_index[0]];\r
129                         vertex[1]=i_coord.items[i_vertex_index[1]];\r
130                         vertex[2]=i_coord.items[i_vertex_index[2]];\r
131                         vertex[3]=i_coord.items[i_vertex_index[3]];\r
132                 \r
133                         //画像を取得\r
134                         if (!this._inst_patt.pickFromRaster(this._ref_raster,vertex)){\r
135                                 return;//取得失敗\r
136                         }\r
137                         //取得パターンをカラー差分データに変換して評価する。\r
138                         this._deviation_data.setRaster(this._inst_patt);\r
139 \r
140                         \r
141                         //code_index,dir,c1にデータを得る。\r
142                         final NyARMatchPattResult mr=this.__detectMarkerLite_mr;\r
143                         int lcode_index = 0;\r
144                         int dir = 0;\r
145                         double c1 = 0;\r
146                         for (int i = 0; i < this._match_patt.length; i++) {\r
147                                 this._match_patt[i].evaluate(this._deviation_data,mr);\r
148                                 double c2 = mr.confidence;\r
149                                 if (c1 < c2) {\r
150                                         lcode_index = i;\r
151                                         c1 = c2;\r
152                                         dir = mr.direction;\r
153                                 }\r
154                         }\r
155                         \r
156                         //認識処理\r
157                         if (this._target_id == -1) { // マーカ未認識\r
158                                 //現在は未認識\r
159                                 if (c1 < this.cf_threshold_new) {\r
160                                         return;\r
161                                 }\r
162                                 if (this.confidence > c1) {\r
163                                         // 一致度が低い。\r
164                                         return;\r
165                                 }\r
166                                 //認識しているマーカIDを保存\r
167                                 this.code_index=lcode_index;\r
168                         }else{\r
169                                 //現在はマーカ認識中                           \r
170                                 // 現在のマーカを認識したか?\r
171                                 if (lcode_index != this._target_id) {\r
172                                         // 認識中のマーカではないので無視\r
173                                         return;\r
174                                 }\r
175                                 //認識中の閾値より大きいか?\r
176                                 if (c1 < this.cf_threshold_exist) {\r
177                                         return;\r
178                                 }\r
179                                 //現在の候補よりも一致度は大きいか?\r
180                                 if (this.confidence>c1) {\r
181                                         return;\r
182                                 }\r
183                                 this.code_index=this._target_id;\r
184                         }\r
185                         //新しく認識、または継続認識中に更新があったときだけ、Square情報を更新する。\r
186                         //ココから先はこの条件でしか実行されない。\r
187                         \r
188                         //一致率の高い矩形があれば、方位を考慮して頂点情報を作成\r
189                         this.confidence=c1;\r
190                         NyARSquare sq=this.square;\r
191                         //directionを考慮して、squareを更新する。\r
192                         for(int i=0;i<4;i++){\r
193                                 int idx=(i+4 - dir) % 4;\r
194                                 this._coordline.coord2Line(i_vertex_index[idx],i_vertex_index[(idx+1)%4],i_coord,sq.line[i]);\r
195                         }\r
196                         for (int i = 0; i < 4; i++) {\r
197                                 //直線同士の交点計算\r
198                                 if(!sq.line[i].crossPos(sq.line[(i + 3) % 4],sq.sqvertex[i])){\r
199                                         throw new NyARException();//ここのエラー復帰するならダブルバッファにすればOK\r
200                                 }\r
201                         }\r
202                 }\r
203         }       \r
204         /** ユーザーが自由に使えるタグ変数です。*/\r
205         public Object tag;\r
206 \r
207         private int _lost_delay_count = 0;\r
208         private int _lost_delay = 5;\r
209         /** 姿勢変換行列の計算オブジェクト*/\r
210         protected INyARTransMat _transmat;\r
211 \r
212         private NyARRectOffset _offset; \r
213         private int _threshold = 110;\r
214         // [AR]検出結果の保存用\r
215         private NyARBinRaster _bin_raster;\r
216 \r
217         private NyARRasterFilter_ARToolkitThreshold _tobin_filter;\r
218 \r
219         protected int _current_arcode_index = -1;\r
220 \r
221         private NyARRasterThresholdAnalyzer_SlidePTile _threshold_detect;\r
222 \r
223         /**\r
224          * デフォルトコンストラクタ。\r
225          * クラスを継承するときは、このコンストラクタを呼び出した後に、{@link #initInstance}関数でインスタンスの初期化処理を実装します。\r
226          */\r
227         protected SingleARMarkerProcesser()\r
228         {\r
229                 return;\r
230         }\r
231 \r
232         private boolean _initialized=false;\r
233         /**\r
234          * この関数は、インスタンスを初期化します。\r
235          * 継承先のクラスから呼び出してください。\r
236          * @param i_param\r
237          * カメラパラメータオブジェクト。このサイズは、{@link #detectMarker}に入力する画像と同じサイズである必要があります。\r
238          * @param i_raster_type\r
239          * {@link #detectMarker}関数に入力する画像のフォーマット。\r
240          * この値には、{@link INyARRgbRaster#getBufferType}関数の戻り値を利用します。\r
241          * @throws NyARException\r
242          */\r
243         protected void initInstance(NyARParam i_param,int i_raster_type) throws NyARException\r
244         {\r
245                 //初期化済?\r
246                 assert(this._initialized==false);\r
247                 \r
248                 NyARIntSize scr_size = i_param.getScreenSize();\r
249                 // 解析オブジェクトを作る\r
250                 this._transmat = new NyARTransMat(i_param);\r
251                 this._tobin_filter=new NyARRasterFilter_ARToolkitThreshold(110,i_raster_type);\r
252 \r
253                 // 2値画像バッファを作る\r
254                 this._bin_raster = new NyARBinRaster(scr_size.w, scr_size.h);\r
255                 this._threshold_detect=new NyARRasterThresholdAnalyzer_SlidePTile(15,i_raster_type,4);\r
256                 this._initialized=true;\r
257                 //コールバックハンドラ\r
258                 this._detectmarker=new DetectSquare(i_param,i_raster_type);\r
259                 this._offset=new NyARRectOffset();\r
260                 return;\r
261         }\r
262 \r
263         /*自動・手動の設定が出来ないので、コメントアウト\r
264         public void setThreshold(int i_threshold)\r
265         {\r
266                 this._threshold = i_threshold;\r
267                 return;\r
268         }*/\r
269 \r
270         /**\r
271          * この関数は、検出するマーカパターンテーブルの配列を指定します。 \r
272          * マーカパターンには、配列の先頭から、0から始まるID番号を割り当てられます。\r
273          * このIDは、{@link #onEnterHandler}イベントハンドラに通知されるID番号に対応し、マーカパターンの識別に使います。\r
274          * @param i_ref_code_table\r
275          * マーカパターンテーブルにセットする配列です。配列にあるマーカパターンの解像度は、i_code_resolutionに一致している必要があります。\r
276          * @param i_code_resolution\r
277          * マーカパターン縦横解像度です。\r
278          * @param i_marker_width\r
279          * <p>メモ:\r
280          * マーカを検出している状態で関数を実行すると、イベント通知なしに、認識中のマーカを見失います。\r
281          * </p>\r
282          */\r
283         public void setARCodeTable(NyARCode[] i_ref_code_table, int i_code_resolution, double i_marker_width)\r
284         {\r
285                 if (this._current_arcode_index != -1) {\r
286                         // 強制リセット\r
287                         reset(true);\r
288                 }\r
289                 //検出するマーカセット、情報、検出器を作り直す。(1ピクセル4ポイントサンプリング,マーカのパターン領域は50%)\r
290                 this._detectmarker.setNyARCodeTable(i_ref_code_table,i_code_resolution);\r
291                 this._offset.setSquare(i_marker_width);\r
292                 return;\r
293         }\r
294         /**\r
295          * この関数は、インスタンスの状態をリセットします。\r
296          * 状態をリセットすると、もしマーカを認識している場合には、{@link #onLeaveHandler}イベントハンドラがコールされ、未認識状態になります。\r
297          * @param i_is_force\r
298          * 強制フラグ。trueにすると、イベント通知なしにマーカ認識状態をリセットします。\r
299          */\r
300         public void reset(boolean i_is_force)\r
301         {\r
302                 if (this._current_arcode_index != -1 && i_is_force == false) {\r
303                         // 強制書き換えでなければイベントコール\r
304                         this.onLeaveHandler();\r
305                 }\r
306                 // カレントマーカをリセット\r
307                 this._current_arcode_index = -1;\r
308                 return;\r
309         }\r
310         private DetectSquare _detectmarker;\r
311         /**\r
312          * この関数は、画像を処理して、適切なマーカ検出イベントハンドラを呼び出します。\r
313          * イベントハンドラの呼び出しは、この関数を呼び出したスレッドが、この関数が終了するまでに行います。\r
314          * @param i_raster\r
315          * 検出処理をする画像を指定します。\r
316          * @throws NyARException\r
317          */\r
318         public void detectMarker(INyARRgbRaster i_raster) throws NyARException\r
319         {\r
320                 // サイズチェック\r
321                 assert(this._bin_raster.getSize().isEqualSize(i_raster.getSize().w, i_raster.getSize().h));\r
322 \r
323                 //BINイメージへの変換\r
324                 this._tobin_filter.setThreshold(this._threshold);\r
325                 this._tobin_filter.doFilter(i_raster, this._bin_raster);\r
326 \r
327                 // スクエアコードを探す\r
328                 this._detectmarker.init(i_raster,this._current_arcode_index);\r
329                 this._detectmarker.detectMarker(this._bin_raster);\r
330                 \r
331                 // 認識状態を更新\r
332                 final boolean is_id_found=this.updateStatus(this._detectmarker.square,this._detectmarker.code_index);\r
333                 //閾値フィードバック(detectExistMarkerにもあるよ)\r
334                 if(!is_id_found){\r
335                         //マーカがなければ、探索+DualPTailで基準輝度検索\r
336                         int th=this._threshold_detect.analyzeRaster(i_raster);\r
337                         this._threshold=(this._threshold+th)/2;\r
338                 }\r
339                 return;\r
340         }\r
341         /**\r
342          * この関数は、マーカパターンの一致率の敷居値を設定します。\r
343          * 敷居値は、0.0&lt;n&lt;1.0の範囲で指定します。\r
344          * @param i_new_cf\r
345          * 新しくマーカを発見するときの閾値です。\r
346          * @param i_exist_cf\r
347          * 継続してマーカを追跡するときの閾値です。\r
348          * i_new_cfの6割程度の値を指定すると良いでしょう。\r
349          */\r
350         public void setConfidenceThreshold(double i_new_cf,double i_exist_cf)\r
351         {\r
352                 this._detectmarker.cf_threshold_exist=i_exist_cf;\r
353                 this._detectmarker.cf_threshold_new=i_new_cf;\r
354         }\r
355 \r
356         private NyARTransMatResult __NyARSquare_result = new NyARTransMatResult();\r
357 \r
358         /**     オブジェクトのステータスを更新し、必要に応じて自己コールバック関数を駆動します。\r
359          *      戻り値は、「実際にマーカを発見する事ができたか」を示す真偽値です。クラスの状態とは異なります。\r
360          */\r
361         private boolean updateStatus(NyARSquare i_square, int i_code_index)  throws NyARException\r
362         {\r
363                 NyARTransMatResult result = this.__NyARSquare_result;\r
364                 if (this._current_arcode_index < 0) {// 未認識中\r
365                         if (i_code_index < 0) {// 未認識から未認識の遷移\r
366                                 // なにもしないよーん。\r
367                                 return false;\r
368                         } else {// 未認識から認識の遷移\r
369                                 this._current_arcode_index = i_code_index;\r
370                                 // イベント生成\r
371                                 // OnEnter\r
372                                 this.onEnterHandler(i_code_index);\r
373                                 // 変換行列を作成\r
374                                 this._transmat.transMat(i_square, this._offset, result);\r
375                                 // OnUpdate\r
376                                 this.onUpdateHandler(i_square, result);\r
377                                 this._lost_delay_count = 0;\r
378                                 return true;\r
379                         }\r
380                 } else {// 認識中\r
381                         if (i_code_index < 0) {// 認識から未認識の遷移\r
382                                 this._lost_delay_count++;\r
383                                 if (this._lost_delay < this._lost_delay_count) {\r
384                                         // OnLeave\r
385                                         this._current_arcode_index = -1;\r
386                                         this.onLeaveHandler();\r
387                                 }\r
388                                 return false;\r
389                         } else if (i_code_index == this._current_arcode_index) {// 同じARCodeの再認識\r
390                                 // イベント生成\r
391                                 // 変換行列を作成\r
392                                 this._transmat.transMatContinue(i_square, this._offset, result,result);\r
393                                 // OnUpdate\r
394                                 this.onUpdateHandler(i_square, result);\r
395                                 this._lost_delay_count = 0;\r
396                                 return true;\r
397                         } else {// 異なるコードの認識→今はサポートしない。\r
398                                 throw new  NyARException();\r
399                         }\r
400                 }\r
401         }\r
402         /**\r
403          * 自己コールバック関数です。\r
404          * 継承したクラスで、マーカ発見時の処理を実装してください。\r
405          * @param i_code\r
406          * 検出したマーカパターンのID番号です。ID番号については、{@link #setARCodeTable}の説明を参照してください。\r
407          */\r
408         protected abstract void onEnterHandler(int i_code);\r
409         /**\r
410          * 自己コールバック関数です。\r
411          * 継承したクラスで、マーカ消失時の処理を実装してください。\r
412          */\r
413         protected abstract void onLeaveHandler();\r
414         /**\r
415          * 自己コールバック関数です。\r
416          * 継承したクラスで、マーカ更新時の処理を実装してください。\r
417          * 引数の値の有効期間は、関数が終了するまでです。\r
418          * @param i_square\r
419          * 現在のマーカ検出位置です。\r
420          * @param result\r
421          * 現在の姿勢変換行列です。\r
422          */\r
423         protected abstract void onUpdateHandler(NyARSquare i_square, NyARTransMatResult result);\r
424 }\r