OSDN Git Service

git-svn-id: http://svn.sourceforge.jp/svnroot/nyartoolkit/NyARToolkit/trunk@808 7cac0...
[nyartoolkit-and/nyartoolkit-and.git] / lib / src.rpf / jp / nyatla / nyartoolkit / rpf / tracker / nyartk / status / NyARRectTargetStatus.java
1 package jp.nyatla.nyartoolkit.rpf.tracker.nyartk.status;\r
2 \r
3 \r
4 import jp.nyatla.nyartoolkit.NyARException;\r
5 import jp.nyatla.nyartoolkit.core.types.*;\r
6 import jp.nyatla.nyartoolkit.core.utils.NyARMath;\r
7 import jp.nyatla.nyartoolkit.rpf.sampler.lrlabel.LowResolutionLabelingSamplerOut;\r
8 import jp.nyatla.nyartoolkit.rpf.tracker.nyartk.INyARVectorReader;\r
9 import jp.nyatla.nyartoolkit.rpf.utils.VecLinearCoordinates;\r
10 \r
11 \r
12 \r
13 /**\r
14  * このクラスは、RECTステータスのターゲットステータスを格納します。\r
15  * RECTステータスは、ラベルから推定した矩形の4点座標と、そこから計算できるパラメータを持ちます。\r
16  */\r
17 public class NyARRectTargetStatus extends NyARTargetStatus\r
18 {\r
19         private NyARRectTargetStatusPool _ref_my_pool;\r
20         \r
21         \r
22         /** [read only]矩形の頂点情報。4要素です。*/\r
23         public NyARDoublePoint2d[] vertex=NyARDoublePoint2d.createArray(4);\r
24 \r
25         /** [read only]頂点予想値から計算した、頂点速度の二乗値の合計*/\r
26         public int estimate_sum_sq_vertex_velocity_ave;\r
27 \r
28         /** [read only]頂点予測値のクリップ領域。*/\r
29         public NyARIntRect estimate_rect=new NyARIntRect();\r
30 \r
31         /** [read only]頂点予測位置。4要素です。*/\r
32         public NyARDoublePoint2d[] estimate_vertex=NyARDoublePoint2d.createArray(4);\r
33 \r
34         /** [read only]どのような手法で矩形を検出したかを示す値。DT_XXXの値を取ります。*/\r
35         public int detect_type;\r
36         \r
37         /** 定数値。初期矩形検出に成功し、値を更新したことを示す。*/\r
38         public static final int DT_SQINIT=0;\r
39 \r
40         /** 定数値。ラベル情報を元にした検出に成功し、値を更新したことを示す。*/\r
41         public static final int DT_SQDAILY=1;\r
42         \r
43         /** 定数値。直線トレース検出に成功し、値を更新したことを示す。*/\r
44         public static final int DT_LIDAILY=2;\r
45 \r
46         /** 定数値。検出できず、過去のデータをそのまま引き継いだ事を示す。*/\r
47         public static final int DT_FAILED=-1;\r
48         \r
49         //\r
50         //制御部\r
51         \r
52         /**\r
53          * コンストラクタです。\r
54          * この関数は、所有されるプールオブジェクトが使います。ユーザは使いません。\r
55          * @param i_pool\r
56          * プールオブジェクトのコントロールインタフェイス\r
57          */     \r
58         public NyARRectTargetStatus(NyARRectTargetStatusPool i_pool)\r
59         {\r
60                 super(i_pool._op_interface);\r
61                 this._ref_my_pool=i_pool;\r
62                 this.detect_type=DT_SQINIT;\r
63         }\r
64 \r
65         /**\r
66          * 前回のステータスと予想パラメータを計算してセットします。\r
67          * @param i_prev_param\r
68          */\r
69         private final void setEstimateParam(NyARRectTargetStatus i_prev_param)\r
70         {\r
71                 NyARDoublePoint2d[] vc_ptr=this.vertex;\r
72                 NyARDoublePoint2d[] ve_ptr=this.estimate_vertex;\r
73                 int sum_of_vertex_sq_dist=0;\r
74                 if(i_prev_param!=null){\r
75                         //差分パラメータをセット\r
76                         NyARDoublePoint2d[] vp=i_prev_param.vertex;\r
77                         //頂点速度の計測\r
78                         for(int i=3;i>=0;i--){\r
79                                 int x=(int)((vc_ptr[i].x-vp[i].x));\r
80                                 int y=(int)((vc_ptr[i].y-vp[i].y));\r
81                                 //予想位置\r
82                                 ve_ptr[i].x=(int)vc_ptr[i].x+x;\r
83                                 ve_ptr[i].y=(int)vc_ptr[i].y+y;\r
84                                 sum_of_vertex_sq_dist+=x*x+y*y;\r
85                         }\r
86                 }else{\r
87                         //頂点速度のリセット\r
88                         for(int i=3;i>=0;i--){\r
89                                 ve_ptr[i].x=(int)vc_ptr[i].x;\r
90                                 ve_ptr[i].y=(int)vc_ptr[i].y;\r
91                         }\r
92                 }\r
93                 //頂点予測と範囲予測\r
94                 this.estimate_sum_sq_vertex_velocity_ave=sum_of_vertex_sq_dist/4;\r
95                 this.estimate_rect.setAreaRect(ve_ptr,4);\r
96 //              this.estimate_rect.clip(i_left, i_top, i_right, i_bottom);\r
97                 return;\r
98         }\r
99         /**\r
100          * この関数は、輪郭ステータスから矩形パラメータを推定して、インスタンスにセットします。\r
101          * 関数の成否にかかわらず、入力オブジェクトとインスタンスの状態が変化することに注意してください。\r
102          * 関数が成功すると、{@link #DT_SQINIT}を{@link #detect_type}にセットします。\r
103          * @param i_contour_status\r
104          * 輪郭ステータスを格納したオブジェクト。\r
105          * 関数を実行すると、この内容は変更されます。\r
106          * @param i_sample_area\r
107          * ラベル情報から得たラベルのクリップ範囲です。\r
108          * @return\r
109          * 関数が成功するとtrueを返します。\r
110          * @throws NyARException\r
111          */\r
112         public boolean setValueWithInitialCheck(NyARContourTargetStatus i_contour_status,NyARIntRect i_sample_area) throws NyARException\r
113         {\r
114                 //ベクトルのマージ(マージするときに、3,4象限方向のベクトルは1,2象限のベクトルに変換する。)\r
115                 i_contour_status.vecpos.limitQuadrantTo12();\r
116                 this._ref_my_pool._vecpos_op.margeResembleCoords(i_contour_status.vecpos);\r
117                 if(i_contour_status.vecpos.length<4){\r
118                         return false;\r
119                 }\r
120                 \r
121                 //キーベクトルを取得\r
122                 i_contour_status.vecpos.getKeyCoord(this._ref_my_pool._indexbuf);\r
123                 //点に変換\r
124                 NyARDoublePoint2d[] this_vx=this.vertex;\r
125                 if(!this._ref_my_pool._line_detect.line2SquareVertex(this._ref_my_pool._indexbuf,this_vx)){\r
126                         return false;\r
127                 }\r
128                 \r
129 //              //点から直線を再計算\r
130 //              for(int i=3;i>=0;i--){\r
131 //                      this_sq.line[i].makeLinearWithNormalize(this_sq.sqvertex[i],this_sq.sqvertex[(i+1)%4]);\r
132 //              }\r
133                 this.setEstimateParam(null);\r
134                 if(!checkInitialRectCondition(i_sample_area))\r
135                 {\r
136                         return false;\r
137                 }\r
138                 this.detect_type=DT_SQINIT;\r
139                 return true;\r
140         }\r
141         /**\r
142          * この関数は、ラベル情報から矩形パラメータを推定して、インスタンスにセットします。\r
143          * 関数の成否にかかわらず、インスタンスの状態が変化することに注意してください。\r
144          * @param i_vec_reader\r
145          * @param i_source\r
146          * @param i_prev_status\r
147          * @return\r
148          * @throws NyARException\r
149          */\r
150         private boolean setValueWithDeilyCheck(INyARVectorReader i_vec_reader,LowResolutionLabelingSamplerOut.Item i_source,NyARRectTargetStatus i_prev_status) throws NyARException\r
151         {\r
152                 VecLinearCoordinates vecpos=this._ref_my_pool._vecpos;\r
153                 //輪郭線を取る\r
154                 if(!i_vec_reader.traceConture(i_source.lebeling_th,i_source.entry_pos,vecpos)){\r
155                         return false;\r
156                 }\r
157                 //3,4象限方向のベクトルは1,2象限のベクトルに変換する。\r
158                 vecpos.limitQuadrantTo12();\r
159                 //ベクトルのマージ\r
160                 this._ref_my_pool._vecpos_op.margeResembleCoords(vecpos);\r
161                 if(vecpos.length<4){\r
162                         return false;\r
163                 }\r
164                 //キーベクトルを取得\r
165                 vecpos.getKeyCoord(this._ref_my_pool._indexbuf);\r
166                 //点に変換\r
167                 NyARDoublePoint2d[] this_vx=this.vertex;\r
168                 if(!this._ref_my_pool._line_detect.line2SquareVertex(this._ref_my_pool._indexbuf,this_vx)){\r
169                         return false;\r
170                 }\r
171                 //頂点並び順の調整\r
172                 rotateVertexL(this.vertex,checkVertexShiftValue(i_prev_status.vertex,this.vertex));     \r
173 \r
174                 //パラメタチェック\r
175                 if(!checkDeilyRectCondition(i_prev_status)){\r
176                         return false;\r
177                 }\r
178                 //次回の予測\r
179                 setEstimateParam(i_prev_status);\r
180                 return true;\r
181         }\r
182         /**\r
183          * この関数は、前回の位置情報と直線検出器で矩形パラメータを推定して、インスタンスにセットします。\r
184          * 関数の成否にかかわらず、インスタンスの状態が変化することに注意してください。\r
185          * @param i_vec_reader\r
186          * @param i_prev_status\r
187          * @return\r
188          * @throws NyARException\r
189          */\r
190         private boolean setValueByLineLog(INyARVectorReader i_vec_reader,NyARRectTargetStatus i_prev_status) throws NyARException\r
191         {\r
192                 //検出範囲からカーネルサイズの2乗値を計算。検出領域の二乗距離の1/(40*40) (元距離の1/40)\r
193                 int d=((int)i_prev_status.estimate_rect.getDiagonalSqDist()/(NyARMath.SQ_40));\r
194                 //二乗移動速度からカーネルサイズを計算。\r
195                 int v_ave_limit=i_prev_status.estimate_sum_sq_vertex_velocity_ave;\r
196                 //\r
197                 if(v_ave_limit>d){\r
198                         //移動カーネルサイズより、検出範囲カーネルのほうが大きかったらエラー(動きすぎ)\r
199                         return false;\r
200                 }\r
201                 d=(int)Math.sqrt(d);\r
202                 //最低でも2だよね。\r
203                 if(d<2){\r
204                         d=2;\r
205                 }\r
206                 //最大カーネルサイズ(5)を超える場合は5にする。\r
207                 if(d>5){\r
208                         d=5;\r
209                 }\r
210                 \r
211                 //ライントレースの試行\r
212 \r
213                 NyARLinear[] sh_l=this._ref_my_pool._line;\r
214                 if(!traceSquareLine(i_vec_reader,d,i_prev_status,sh_l)){\r
215                         return false;\r
216                 }else{\r
217                 }\r
218                 //4点抽出\r
219                 for(int i=3;i>=0;i--){\r
220                         if(!sh_l[i].crossPos(sh_l[(i + 3) % 4],this.vertex[i])){\r
221                                 //四角が作れない。\r
222                                 return false;\r
223                         }\r
224                 }               \r
225 \r
226                 //頂点並び順の調整\r
227                 rotateVertexL(this.vertex,checkVertexShiftValue(i_prev_status.vertex,this.vertex));     \r
228                 //差分パラメータのセット\r
229                 setEstimateParam(i_prev_status);\r
230                 return true;\r
231         }\r
232         /**\r
233          * この関数は、2回目以降の矩形検出で、状況に応じた矩形検出処理を実行して、新しい値をインスタンスにセットします。\r
234          * 関数が成功すると、{@link #detect_type}に値をセットします。\r
235          * @param i_vec_reader\r
236          * 画素ベクトルを読みだすためのオブジェクト。\r
237          * @param i_source\r
238          * ラべリングから得られたサンプル情報。(存在しないときはNULL)\r
239          * @param i_prev_status\r
240          * 前回の状態を格納したステータスオブジェクト。\r
241          * @return\r
242          * 値のセットに成功するとtrueを返します。\r
243          * @throws NyARException\r
244          */\r
245         public boolean setValueByAutoSelect(INyARVectorReader i_vec_reader,LowResolutionLabelingSamplerOut.Item i_source,NyARRectTargetStatus i_prev_status) throws NyARException\r
246         {\r
247                 int current_detect_type=DT_SQDAILY;\r
248                 //移動速度による手段の切り替え\r
249                 int sq_v_ave_limit=i_prev_status.estimate_sum_sq_vertex_velocity_ave/4;\r
250                 //速度が小さい時か、前回LineLogが成功したときはDT_LIDAILY\r
251                 if(((sq_v_ave_limit<10) && (i_prev_status.detect_type==DT_SQDAILY)) || (i_prev_status.detect_type==DT_LIDAILY)){\r
252                         current_detect_type=DT_LIDAILY;\r
253                 }\r
254                 \r
255                 //前回の動作ログによる手段の切り替え\r
256                 switch(current_detect_type)\r
257                 {\r
258                 case DT_LIDAILY:\r
259                         //LineLog->\r
260                         if(setValueByLineLog(i_vec_reader,i_prev_status))\r
261                         {\r
262                                 //うまくいった。\r
263                                 this.detect_type=DT_LIDAILY;\r
264                                 return true;\r
265                         }\r
266                         if(i_source!=null){\r
267                                 if(setValueWithDeilyCheck(i_vec_reader,i_source,i_prev_status))\r
268                                 {\r
269                                         //うまくいった\r
270                                         this.detect_type=DT_SQDAILY;\r
271                                         return true;\r
272                                 }\r
273                         }\r
274                         break;\r
275                 case DT_SQDAILY:\r
276                         if(i_source!=null){\r
277                                 if(setValueWithDeilyCheck(i_vec_reader,i_source,i_prev_status))\r
278                                 {\r
279                                         this.detect_type=DT_SQDAILY;\r
280                                         return true;\r
281                                 }\r
282                         }\r
283                         break;\r
284                 default:\r
285                         break;\r
286                 }\r
287                 //前回の動作ログを書き換え\r
288                 i_prev_status.detect_type=DT_FAILED;\r
289                 return false;\r
290         }\r
291         \r
292 \r
293         /**\r
294          * このデータが初期チェック(CoordからRectへの遷移)をパスするかチェックします。\r
295          * 条件は、\r
296          *  1.検出四角形の対角点は元の検出矩形内か?\r
297          *  2.一番長い辺と短い辺の比は、0.1~10の範囲か?\r
298          *  3.位置倍長い辺、短い辺が短すぎないか?\r
299          * @param i_sample_area\r
300          * この矩形を検出するために使った元データの範囲(ターゲット検出範囲)\r
301          */\r
302         private boolean checkInitialRectCondition(NyARIntRect i_sample_area)\r
303         {\r
304                 NyARDoublePoint2d[] this_vx=this.vertex;\r
305 \r
306                 //検出した四角形の対角点が検出エリア内か?\r
307                 int cx=(int)(this_vx[0].x+this_vx[1].x+this_vx[2].x+this_vx[3].x)/4;\r
308                 int cy=(int)(this_vx[0].y+this_vx[1].y+this_vx[2].y+this_vx[3].y)/4;\r
309                 if(!i_sample_area.isInnerPoint(cx,cy)){\r
310                         return false;\r
311                 }\r
312 \r
313                 \r
314                 //一番長い辺と短い辺の比を確認(10倍の比があったらなんか変)\r
315                 int max=Integer.MIN_VALUE;\r
316                 int min=Integer.MAX_VALUE;\r
317                 for(int i=0;i<4;i++){\r
318                         int t=(int)this_vx[i].sqDist(this_vx[(i+1)%4]);\r
319                         if(t>max){max=t;}\r
320                         if(t<min){min=t;}\r
321                 }\r
322                 //比率係数の確認\r
323                 if(max<(5*5) ||min<(5*5)){\r
324                         return false;\r
325                 }\r
326                 //10倍スケールの2乗\r
327                 if((10*10)*min/max<(3*3)){\r
328                         return false;\r
329                 }\r
330                 return true;\r
331         }\r
332         /**\r
333          * 2回目以降の履歴を使ったデータチェック。\r
334          * 条件は、\r
335          *  1.一番長い辺と短い辺の比は、0.1~10の範囲か?\r
336          *  2.位置倍長い辺、短い辺が短すぎないか?\r
337          *  3.移動距離が極端に大きなものは無いか?(他の物の3倍動いてたらおかしい)\r
338 \r
339          * @param i_sample_area\r
340          */\r
341         private boolean checkDeilyRectCondition(NyARRectTargetStatus i_prev_st)\r
342         {\r
343                 NyARDoublePoint2d[] this_vx=this.vertex;\r
344 \r
345                 //一番長い辺と短い辺の比を確認(10倍の比があったらなんか変)\r
346                 int max=Integer.MIN_VALUE;\r
347                 int min=Integer.MAX_VALUE;\r
348                 for(int i=0;i<4;i++){\r
349                         int t=(int)this_vx[i].sqDist(this_vx[(i+1)%4]);\r
350                         if(t>max){max=t;}\r
351                         if(t<min){min=t;}\r
352                 }\r
353                 //比率係数の確認\r
354                 if(max<(5*5) ||min<(5*5)){\r
355                         return false;\r
356                 }\r
357                 //10倍スケールの2乗\r
358                 if((10*10)*min/max<(3*3)){\r
359                         return false;\r
360                 }\r
361                 //移動距離平均より大きく剥離した点が無いか確認\r
362                 return this._ref_my_pool.checkLargeDiff(this_vx,i_prev_st.vertex);\r
363         }\r
364 \r
365         /**\r
366          * 予想位置を基準に四角形をトレースして、一定の基準をクリアするかを評価します。\r
367          * @param i_reader\r
368          * @param i_edge_size\r
369          * @param i_prevsq\r
370          * @return\r
371          * @throws NyARException\r
372          */\r
373         private boolean traceSquareLine(INyARVectorReader i_reader,int i_edge_size,NyARRectTargetStatus i_prevsq,NyARLinear[] o_line) throws NyARException\r
374         {\r
375                 NyARDoublePoint2d p1,p2;\r
376                 VecLinearCoordinates vecpos=this._ref_my_pool._vecpos;\r
377                 //NyARIntRect i_rect\r
378                 p1=i_prevsq.estimate_vertex[0];\r
379                 int dist_limit=i_edge_size*i_edge_size;\r
380                 //強度敷居値(セルサイズ-1)\r
381 //              int min_th=i_edge_size*2+1;\r
382 //              min_th=(min_th*min_th);\r
383                 for(int i=0;i<4;i++)\r
384                 {\r
385                         p2=i_prevsq.estimate_vertex[(i+1)%4];\r
386                         \r
387                         //クリップ付きで予想位置周辺の直線のトレース\r
388                         i_reader.traceLineWithClip(p1,p2,i_edge_size,vecpos);\r
389 \r
390                         //クラスタリングして、傾きの近いベクトルを探す。(限界は10度)\r
391                         this._ref_my_pool._vecpos_op.margeResembleCoords(vecpos);\r
392                         //基本的には1番でかいベクトルだよね。だって、直線状に取るんだもの。\r
393 \r
394                         int vid=vecpos.getMaxCoordIndex();\r
395                         //データ品質規制(強度が多少強くないと。)\r
396 //                      if(vecpos.items[vid].sq_dist<(min_th)){\r
397 //                              return false;\r
398 //                      }\r
399 //@todo:パラメタ調整\r
400                         //角度規制(元の線分との角度を確認)\r
401                         if(vecpos.items[vid].getAbsVecCos(i_prevsq.vertex[i],i_prevsq.vertex[(i+1)%4])<NyARMath.COS_DEG_5){\r
402                                 //System.out.println("CODE1");\r
403                                 return false;\r
404                         }\r
405 //@todo:パラメタ調整\r
406                         //予想点からさほど外れていない点であるか。(検出点の移動距離を計算する。)\r
407                         double dist;\r
408                         dist=vecpos.items[vid].sqDistBySegmentLineEdge(i_prevsq.vertex[i],i_prevsq.vertex[i]);\r
409                         if(dist<dist_limit){\r
410                                 o_line[i].setVectorWithNormalize(vecpos.items[vid]);\r
411                         }else{\r
412                                 //System.out.println("CODE2:"+dist+","+dist_limit);\r
413                                 return false;\r
414                         }\r
415                         //頂点ポインタの移動\r
416                         p1=p2;\r
417                 }\r
418                 return true;\r
419         }\r
420     /**\r
421      * 頂点同士の距離から、頂点のシフト量を返します。この関数は、よく似た2つの矩形の頂点同士の対応を取るために使用します。\r
422      * @param i_square\r
423      * 比較対象の矩形\r
424      * @return\r
425      * シフト量を数値で返します。\r
426      * シフト量はthis-i_squareです。1の場合、this.sqvertex[0]とi_square.sqvertex[1]が対応点になる(shift量1)であることを示します。\r
427      */\r
428     private final static int checkVertexShiftValue(NyARDoublePoint2d[] i_vertex1,NyARDoublePoint2d[] i_vertex2)\r
429     {\r
430         assert(i_vertex1.length==4 && i_vertex2.length==4);\r
431         //3-0番目\r
432         int min_dist=Integer.MAX_VALUE;\r
433         int min_index=0;\r
434         int xd,yd;\r
435         for(int i=3;i>=0;i--){\r
436                 int d=0;\r
437                 for(int i2=3;i2>=0;i2--){\r
438                         xd= (int)(i_vertex1[i2].x-i_vertex2[(i2+i)%4].x);\r
439                         yd= (int)(i_vertex1[i2].y-i_vertex2[(i2+i)%4].y);\r
440                         d+=xd*xd+yd*yd;\r
441                 }\r
442                 if(min_dist>d){\r
443                         min_dist=d;\r
444                         min_index=i;\r
445                 }\r
446         }\r
447         return min_index;\r
448     }\r
449     /**\r
450      * 4とnの最大公約数テーブル\r
451      */\r
452     private final static int[] _gcd_table4={-1,1,2,1};\r
453     /**\r
454      * 頂点を左回転して、矩形を回転させます。\r
455      * @param i_shift\r
456      */\r
457     private final static void rotateVertexL(NyARDoublePoint2d[] i_vertex,int i_shift)\r
458     {\r
459         assert(i_shift<4);\r
460         NyARDoublePoint2d vertext;\r
461         if(i_shift==0){\r
462                 return;\r
463         }\r
464         int t1,t2;\r
465         int d, i, j, mk;\r
466             int ll=4-i_shift;\r
467             d = _gcd_table4[ll];//NyMath.gcn(4,ll);\r
468             mk = (4-ll) % 4;\r
469             for (i = 0; i < d; i++) {\r
470                 vertext=i_vertex[i];\r
471                 for (j = 1; j < 4/d; j++) {\r
472                     t1=(i + (j-1)*mk) % 4;\r
473                     t2=(i + j*mk) % 4;\r
474                     i_vertex[t1]=i_vertex[t2];\r
475                 }\r
476                 t1=(i + ll) % 4;\r
477                 i_vertex[t1]=vertext;\r
478             }\r
479     }\r
480     /**\r
481      * この関数は、矩形の頂点情報をARToolKitのdirectionモデルに従って回転します。\r
482      * マーカパターンから方位値を得た後に、頂点順序を調整するために使います。\r
483      * @param i_dir\r
484      * ARToolKitのdirection値\r
485      */\r
486     public void shiftByArtkDirection(int i_dir)\r
487     {\r
488         rotateVertexL(this.estimate_vertex,i_dir);\r
489         rotateVertexL(this.vertex,i_dir);\r
490     }\r
491 }