OSDN Git Service

[TAG]NyARToolkit for Java 2.4.2
[nyartoolkit-and/nyartoolkit-and.git] / tags / 2.4.2 / 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個のマーカを処理することのできる、アプリケーションプロセッサです。\r
42  * マーカの出現・移動・消滅を、イベントで通知することができます。\r
43  * クラスには複数のマーカを登録できます。一つのマーカが見つかると、プロセッサは継続して同じマーカを\r
44  * 1つだけ認識し続け、見失うまでの間は他のマーカを認識しません。\r
45  * \r
46  * イベントは、 OnEnter→OnUpdate[n]→OnLeaveの順で発生します。\r
47  * マーカが見つかるとまずOnEnterが1度発生して、何番のマーカが発見されたかがわかります。\r
48  * 次にOnUpdateにより、現在の変換行列が連続して渡されます。最後にマーカを見失うと、OnLeave\r
49  * イベントが発生します。\r
50  * \r
51  */\r
52 public abstract class SingleARMarkerProcesser\r
53 {\r
54         /**\r
55          * detectMarkerのコールバック関数\r
56          */\r
57         private class DetectSquareCB implements INyARSquareContourDetector.DetectMarkerCallback\r
58         {\r
59                 //公開プロパティ\r
60                 public final NyARSquare square=new NyARSquare();\r
61                 public double confidence=0.0;\r
62                 public int code_index=-1;               \r
63                 public double cf_threshold_new = 0.30;\r
64                 public double cf_threshold_exist = 0.15;\r
65                 \r
66                 //参照\r
67                 private INyARRgbRaster _ref_raster;\r
68                 //所有インスタンス\r
69                 private INyARColorPatt _inst_patt;\r
70                 private NyARMatchPattDeviationColorData _deviation_data;\r
71                 private NyARMatchPatt_Color_WITHOUT_PCA[] _match_patt;\r
72                 private final NyARMatchPattResult __detectMarkerLite_mr=new NyARMatchPattResult();\r
73                 private Coord2Linear _coordline;\r
74                 \r
75                 public DetectSquareCB(NyARParam i_param)\r
76                 {\r
77                         this._match_patt=null;\r
78                         this._coordline=new Coord2Linear(i_param.getScreenSize(),i_param.getDistortionFactor());\r
79                         return;\r
80                 }\r
81                 public void setNyARCodeTable(NyARCode[] i_ref_code,int i_code_resolution)\r
82                 {\r
83                         /*unmanagedで実装するときは、ここでリソース解放をすること。*/\r
84                         this._deviation_data=new NyARMatchPattDeviationColorData(i_code_resolution,i_code_resolution);\r
85                         this._inst_patt=new NyARColorPatt_Perspective_O2(i_code_resolution,i_code_resolution,4,25);\r
86                         this._match_patt = new NyARMatchPatt_Color_WITHOUT_PCA[i_ref_code.length];\r
87                         for(int i=0;i<i_ref_code.length;i++){\r
88                                 this._match_patt[i]=new NyARMatchPatt_Color_WITHOUT_PCA(i_ref_code[i]);\r
89                         }\r
90                 }\r
91                 private NyARIntPoint2d[] __tmp_vertex=NyARIntPoint2d.createArray(4);\r
92                 /**\r
93                  * Initialize call back handler.\r
94                  */\r
95                 public void init(INyARRgbRaster i_raster)\r
96                 {\r
97                         this._ref_raster=i_raster;\r
98                         this.code_index=-1;\r
99                         this.confidence=Double.MIN_NORMAL;\r
100                 }\r
101 \r
102                 /**\r
103                  * 矩形が見付かるたびに呼び出されます。\r
104                  * 発見した矩形のパターンを検査して、方位を考慮した頂点データを確保します。\r
105                  */\r
106                 public void onSquareDetect(INyARSquareContourDetector i_sender,int[] i_coordx,int[] i_coordy,int i_coor_num,int[] i_vertex_index) throws NyARException\r
107                 {\r
108                         if (this._match_patt==null) {\r
109                                 return;\r
110                         }\r
111                         //輪郭座標から頂点リストに変換\r
112                         NyARIntPoint2d[] vertex=this.__tmp_vertex;\r
113                         vertex[0].x=i_coordx[i_vertex_index[0]];\r
114                         vertex[0].y=i_coordy[i_vertex_index[0]];\r
115                         vertex[1].x=i_coordx[i_vertex_index[1]];\r
116                         vertex[1].y=i_coordy[i_vertex_index[1]];\r
117                         vertex[2].x=i_coordx[i_vertex_index[2]];\r
118                         vertex[2].y=i_coordy[i_vertex_index[2]];\r
119                         vertex[3].x=i_coordx[i_vertex_index[3]];\r
120                         vertex[3].y=i_coordy[i_vertex_index[3]];\r
121                 \r
122                         //画像を取得\r
123                         if (!this._inst_patt.pickFromRaster(this._ref_raster,vertex)){\r
124                                 return;//取得失敗\r
125                         }\r
126                         //取得パターンをカラー差分データに変換して評価する。\r
127                         this._deviation_data.setRaster(this._inst_patt);\r
128 \r
129                         \r
130                         //code_index,dir,c1にデータを得る。\r
131                         final NyARMatchPattResult mr=this.__detectMarkerLite_mr;\r
132                         int lcode_index = 0;\r
133                         int dir = 0;\r
134                         double c1 = 0;\r
135                         for (int i = 0; i < this._match_patt.length; i++) {\r
136                                 this._match_patt[i].evaluate(this._deviation_data,mr);\r
137                                 double c2 = mr.confidence;\r
138                                 if (c1 < c2) {\r
139                                         lcode_index = i;\r
140                                         c1 = c2;\r
141                                         dir = mr.direction;\r
142                                 }\r
143                         }\r
144                         \r
145                         //認識処理\r
146                         if (this.code_index == -1) { // マーカ未認識\r
147                                 //現在は未認識\r
148                                 if (c1 < this.cf_threshold_new) {\r
149                                         return;\r
150                                 }\r
151                                 if (this.confidence > c1) {\r
152                                         // 一致度が低い。\r
153                                         return;\r
154                                 }\r
155                                 //認識しているマーカIDを保存\r
156                                 this.code_index=lcode_index;\r
157                         }else{\r
158                                 //現在はマーカ認識中                           \r
159                                 // 現在のマーカを認識したか?\r
160                                 if (lcode_index != this.code_index) {\r
161                                         // 認識中のマーカではないので無視\r
162                                         return;\r
163                                 }\r
164                                 //認識中の閾値より大きいか?\r
165                                 if (c1 < this.cf_threshold_exist) {\r
166                                         return;\r
167                                 }\r
168                                 //現在の候補よりも一致度は大きいか?\r
169                                 if (this.confidence>c1) {\r
170                                         return;\r
171                                 }\r
172                         }\r
173                         //新しく認識、または継続認識中に更新があったときだけ、Square情報を更新する。\r
174                         //ココから先はこの条件でしか実行されない。\r
175                         \r
176                         //一致率の高い矩形があれば、方位を考慮して頂点情報を作成\r
177                         this.confidence=c1;\r
178                         NyARSquare sq=this.square;\r
179                         //directionを考慮して、squareを更新する。\r
180                         for(int i=0;i<4;i++){\r
181                                 int idx=(i+4 - dir) % 4;\r
182                                 sq.imvertex[i].x=vertex[idx].x;\r
183                                 sq.imvertex[i].y=vertex[idx].y;\r
184                                 this._coordline.coord2Line(i_vertex_index[idx],i_vertex_index[(idx+1)%4],i_coordx,i_coordy,i_coor_num,sq.line[i]);\r
185                         }\r
186                         for (int i = 0; i < 4; i++) {\r
187                                 //直線同士の交点計算\r
188                                 if(!NyARLinear.crossPos(sq.line[i],sq.line[(i + 3) % 4],sq.sqvertex[i])){\r
189                                         throw new NyARException();//ここのエラー復帰するならダブルバッファにすればOK\r
190                                 }\r
191                         }\r
192                 }\r
193         }       \r
194         /**オーナーが自由に使えるタグ変数です。\r
195          */\r
196         public Object tag;\r
197 \r
198         private int _lost_delay_count = 0;\r
199 \r
200         private int _lost_delay = 5;\r
201 \r
202         private INyARSquareContourDetector _square_detect;\r
203 \r
204         protected INyARTransMat _transmat;\r
205 \r
206         private double _marker_width;\r
207         private int _threshold = 110;\r
208         // [AR]検出結果の保存用\r
209         private NyARBinRaster _bin_raster;\r
210 \r
211         private NyARRasterFilter_ARToolkitThreshold _tobin_filter;\r
212 \r
213         protected int _current_arcode_index = -1;\r
214 \r
215         private NyARRasterThresholdAnalyzer_SlidePTile _threshold_detect;\r
216         \r
217         protected SingleARMarkerProcesser()\r
218         {\r
219                 return;\r
220         }\r
221 \r
222         private boolean _initialized=false;\r
223 \r
224         protected void initInstance(NyARParam i_param,int i_raster_type) throws NyARException\r
225         {\r
226                 //初期化済?\r
227                 assert this._initialized==false;\r
228                 \r
229                 NyARIntSize scr_size = i_param.getScreenSize();\r
230                 // 解析オブジェクトを作る\r
231                 this._square_detect = new NyARSquareContourDetector_Rle(i_param.getDistortionFactor(), scr_size);\r
232                 this._transmat = new NyARTransMat(i_param);\r
233                 this._tobin_filter=new NyARRasterFilter_ARToolkitThreshold(110,i_raster_type);\r
234 \r
235                 // 2値画像バッファを作る\r
236                 this._bin_raster = new NyARBinRaster(scr_size.w, scr_size.h);\r
237                 this._threshold_detect=new NyARRasterThresholdAnalyzer_SlidePTile(15,i_raster_type,4);\r
238                 this._initialized=true;\r
239                 //コールバックハンドラ\r
240                 this._detectmarker_cb=new DetectSquareCB(i_param);\r
241                 \r
242                 return;\r
243         }\r
244 \r
245         /*自動・手動の設定が出来ないので、コメントアウト\r
246         public void setThreshold(int i_threshold)\r
247         {\r
248                 this._threshold = i_threshold;\r
249                 return;\r
250         }*/\r
251 \r
252         /**検出するマーカコードの配列を指定します。 検出状態でこの関数を実行すると、\r
253          * オブジェクト状態に強制リセットがかかります。\r
254          */\r
255         public void setARCodeTable(NyARCode[] i_ref_code_table, int i_code_resolution, double i_marker_width)\r
256         {\r
257                 if (this._current_arcode_index != -1) {\r
258                         // 強制リセット\r
259                         reset(true);\r
260                 }\r
261                 //検出するマーカセット、情報、検出器を作り直す。(1ピクセル4ポイントサンプリング,マーカのパターン領域は50%)\r
262                 this._detectmarker_cb.setNyARCodeTable(i_ref_code_table,i_code_resolution);\r
263                 this._marker_width = i_marker_width;\r
264                 return;\r
265         }\r
266 \r
267         public void reset(boolean i_is_force)\r
268         {\r
269                 if (this._current_arcode_index != -1 && i_is_force == false) {\r
270                         // 強制書き換えでなければイベントコール\r
271                         this.onLeaveHandler();\r
272                 }\r
273                 // カレントマーカをリセット\r
274                 this._current_arcode_index = -1;\r
275                 return;\r
276         }\r
277         private DetectSquareCB _detectmarker_cb;\r
278         public void detectMarker(INyARRgbRaster i_raster) throws NyARException\r
279         {\r
280                 // サイズチェック\r
281                 assert(this._bin_raster.getSize().isEqualSize(i_raster.getSize().w, i_raster.getSize().h));\r
282 \r
283                 //BINイメージへの変換\r
284                 this._tobin_filter.setThreshold(this._threshold);\r
285                 this._tobin_filter.doFilter(i_raster, this._bin_raster);\r
286 \r
287                 // スクエアコードを探す\r
288                 this._detectmarker_cb.init(i_raster);\r
289                 this._square_detect.detectMarkerCB(this._bin_raster,this._detectmarker_cb);\r
290                 \r
291                 // 認識状態を更新\r
292                 final boolean is_id_found=updateStatus(this._detectmarker_cb.square,this._detectmarker_cb.code_index);\r
293                 //閾値フィードバック(detectExistMarkerにもあるよ)\r
294                 if(!is_id_found){\r
295                         //マーカがなければ、探索+DualPTailで基準輝度検索\r
296                         int th=this._threshold_detect.analyzeRaster(i_raster);\r
297                         this._threshold=(this._threshold+th)/2;\r
298                 }\r
299                 \r
300                 \r
301                 return;\r
302         }\r
303 \r
304         private NyARTransMatResult __NyARSquare_result = new NyARTransMatResult();\r
305 \r
306         /**     オブジェクトのステータスを更新し、必要に応じてハンドル関数を駆動します。\r
307          *      戻り値は、「実際にマーカを発見する事ができたか」です。クラスの状態とは異なります。\r
308          */\r
309         private boolean updateStatus(NyARSquare i_square, int i_code_index)  throws NyARException\r
310         {\r
311                 NyARTransMatResult result = this.__NyARSquare_result;\r
312                 if (this._current_arcode_index < 0) {// 未認識中\r
313                         if (i_code_index < 0) {// 未認識から未認識の遷移\r
314                                 // なにもしないよーん。\r
315                                 return false;\r
316                         } else {// 未認識から認識の遷移\r
317                                 this._current_arcode_index = i_code_index;\r
318                                 // イベント生成\r
319                                 // OnEnter\r
320                                 this.onEnterHandler(i_code_index);\r
321                                 // 変換行列を作成\r
322                                 this._transmat.transMat(i_square, this._marker_width, result);\r
323                                 // OnUpdate\r
324                                 this.onUpdateHandler(i_square, result);\r
325                                 this._lost_delay_count = 0;\r
326                                 return true;\r
327                         }\r
328                 } else {// 認識中\r
329                         if (i_code_index < 0) {// 認識から未認識の遷移\r
330                                 this._lost_delay_count++;\r
331                                 if (this._lost_delay < this._lost_delay_count) {\r
332                                         // OnLeave\r
333                                         this._current_arcode_index = -1;\r
334                                         this.onLeaveHandler();\r
335                                 }\r
336                                 return false;\r
337                         } else if (i_code_index == this._current_arcode_index) {// 同じARCodeの再認識\r
338                                 // イベント生成\r
339                                 // 変換行列を作成\r
340                                 this._transmat.transMat(i_square, this._marker_width, result);\r
341                                 // OnUpdate\r
342                                 this.onUpdateHandler(i_square, result);\r
343                                 this._lost_delay_count = 0;\r
344                                 return true;\r
345                         } else {// 異なるコードの認識→今はサポートしない。\r
346                                 throw new  NyARException();\r
347                         }\r
348                 }\r
349         }\r
350 \r
351         protected abstract void onEnterHandler(int i_code);\r
352 \r
353         protected abstract void onLeaveHandler();\r
354 \r
355         protected abstract void onUpdateHandler(NyARSquare i_square, NyARTransMatResult result);\r
356 }\r