OSDN Git Service

Merge branch 'git-svn'
[nyartoolkit-and/nyartoolkit-and.git] / tags / 2.2.0 / sample / sandbox / jp / nyatla / nyartoolkit / sandbox / qrcode / NyARQrCodeSymbolBinder.java
1 package jp.nyatla.nyartoolkit.sandbox.qrcode;\r
2 \r
3 import jp.nyatla.nyartoolkit.core.*;\r
4 import jp.nyatla.nyartoolkit.core.types.*;\r
5 import jp.nyatla.nyartoolkit.core.param.*;\r
6 /**\r
7  * QRコードのシンボルを結びつける偉いクラス\r
8  * アルゴリズムはこんな感じ。\r
9  * 1.3シンボルの位置関係から中間のシンボルを探す。\r
10  * 2.中間シンボルの内角点を探す\r
11  * 3.残りの2シンボル間の最短距離の頂点セットを見つけて、それぞれの内角点を探す\r
12  * 4.3個の内角点が決まったら、各シンボルごとに外角点(反対側の頂点)を特定する。\r
13  * 5.対角のシンボルの外角頂点から伸びる線分を合成して、矩形を決める。\r
14  * 6.矩形が決まったら、方程式を解いて交点を出して、頂点にする。\r
15  * 7.交点と中央のシンボルの位置関係から、正しい計算が行われたかを判定(まだ実装してない)\r
16  * \r
17  * \r
18  * この方法は浅い角度でシンボル集合を見たときに、1や3の手順が高い確率で失敗する。\r
19  * その場合計算が途中で破綻するのでわかる(はず)\r
20  * 他の方法もあるけど、それはまた今度。\r
21  */\r
22 public class NyARQrCodeSymbolBinder\r
23 {\r
24         private NyARCameraDistortionFactor _distfactor;\r
25 \r
26         public NyARQrCodeSymbolBinder(NyARCameraDistortionFactor i_ref_distortion)\r
27         {\r
28                 this._distfactor=i_ref_distortion;\r
29                 return;\r
30         }\r
31         /**\r
32          * 最小の三角形を構成する頂点セットを得る\r
33          * @param i_s0\r
34          * @param i_s1\r
35          * @param i_s2\r
36          * @param o_vertex\r
37          */\r
38         private static void getMinimumTriangleVertex(NyARSquare[] i_sqare,int[] o_vertex_id)\r
39         {\r
40                 //辺の長さが最小になる頂点の組合せを探す\r
41                 int d;\r
42                 int x,y;\r
43                 int dmax=0x7fffffff;\r
44                 final NyARIntPoint[] vertex0=i_sqare[0].imvertex;\r
45                 final NyARIntPoint[] vertex1=i_sqare[1].imvertex;\r
46                 final NyARIntPoint[] vertex2=i_sqare[2].imvertex;\r
47                 for(int i=0;i<4;i++)\r
48                 {\r
49                         for(int i2=0;i2<4;i2++)\r
50                         {\r
51                                 for(int i3=0;i3<4;i3++){\r
52                                         x=vertex0[i].x-vertex2[i3].x;\r
53                                         y=vertex0[i].y-vertex2[i3].y;\r
54                                         d=x*x+y*y;\r
55                                         x=vertex1[i2].x-vertex2[i3].x;\r
56                                         y=vertex1[i2].y-vertex2[i3].y;\r
57                                         d+=x*x+y*y;\r
58                                         x=vertex1[i2].x-vertex0[i].x;\r
59                                         y=vertex1[i2].y-vertex0[i].y;\r
60                                         d+=x*x+y*y;\r
61                                         if(d<dmax){\r
62                                                 dmax=d;\r
63                                                 o_vertex_id[0]=i;                                       \r
64                                                 o_vertex_id[1]=i2;\r
65                                                 o_vertex_id[2]=i3;\r
66                                         }\r
67                                 }\r
68                         }\r
69                 }\r
70                 return;\r
71         }\r
72         /**\r
73          * 2矩形の頂点距離が最低の組合せを探す\r
74          * @param i_sqare\r
75          * @param o_vertex_id\r
76          */\r
77         private static void getMinimumLineVertex(NyARIntPoint[] i_sqare0,NyARIntPoint[] i_sqare1,int[] o_vertex_id)\r
78         {\r
79                 //辺の長さが最小になる頂点の組合せを探す\r
80                 int d;\r
81                 int x,y;\r
82                 int dmax=0x7fffffff;\r
83                 for(int i=0;i<4;i++)\r
84                 {\r
85                         for(int i2=0;i2<4;i2++)\r
86                         {\r
87                                 x=i_sqare1[i2].x-i_sqare0[i].x;\r
88                                 y=i_sqare1[i2].y-i_sqare0[i].y;\r
89                                 d=x*x+y*y;\r
90                                 if(d<dmax){\r
91                                         dmax=d;\r
92                                         o_vertex_id[0]=i;                                       \r
93                                         o_vertex_id[1]=i2;\r
94                                 }\r
95                         }\r
96                 }\r
97                 return;\r
98         }\r
99         /**\r
100          * シンボルグループの重心を計算する\r
101          * @param i_sqare\r
102          * @param i_center\r
103          */\r
104         private void getSymbolGroupCenter(NyARSquare[] i_sqare,NyARIntPoint i_center)\r
105         {\r
106                 //シンボルグループの重心を計算\r
107                 int cx,cy;\r
108                 cx=cy=0;\r
109                 for(int i=0;i<3;i++)\r
110                 {\r
111                         final NyARIntPoint[] sq_ptr=i_sqare[i].imvertex;\r
112                         cx+=sq_ptr[0].x;                        \r
113                         cx+=sq_ptr[1].x;                        \r
114                         cx+=sq_ptr[2].x;                        \r
115                         cx+=sq_ptr[3].x;                        \r
116                         cy+=sq_ptr[0].y;                        \r
117                         cy+=sq_ptr[1].y;                        \r
118                         cy+=sq_ptr[2].y;                        \r
119                         cy+=sq_ptr[3].y;                        \r
120                 }\r
121                 i_center.x=cx/12;\r
122                 i_center.y=cy/12;       \r
123                 return;\r
124         }\r
125         /**\r
126          * キーシンボルのインデックスを得る\r
127          * @param i_sqare\r
128          * @param i_vertex_id\r
129          * 最小三角形の頂点IDセット\r
130          * @return\r
131          */\r
132         private static int getKeySymble(NyARSquare[] i_sqare,NyARIntPoint i_center,int[] i_vertex_id)\r
133         {\r
134                 //シンボルグループの重心を計算\r
135                 final int cx=i_center.x;\r
136                 final int cy=i_center.y;        \r
137                 //前段で探した頂点候補のうち、最も重心に近いものが中心シンボルの内対角点\r
138                 int key_symble_idx=0;\r
139                 int x=i_sqare[0].imvertex[i_vertex_id[0]].x-cx;\r
140                 int y=i_sqare[0].imvertex[i_vertex_id[0]].y-cy;\r
141                 int dmax=x*x+y*y;\r
142                 for(int i=1;i<3;i++){\r
143                         x=i_sqare[i].imvertex[i_vertex_id[i]].x-cx;\r
144                         y=i_sqare[i].imvertex[i_vertex_id[i]].y-cy;\r
145                         final int d=x*x+y*y;\r
146                         if(d<dmax){\r
147                                 dmax=d;\r
148                                 key_symble_idx=i;\r
149                         }\r
150                 }\r
151                 return key_symble_idx;\r
152         }\r
153         private NyARDoublePoint2d __bindSquare_ideal_vertex=new NyARDoublePoint2d();\r
154         /**\r
155          * 2つの対角にある矩形から、それらを対角とする矩形を作る。\r
156          * @param i_sq1\r
157          * @param i_lv1\r
158          * @param i_sq2\r
159          * @param i_lv2\r
160          */\r
161         private void bindSquare(NyARSquare i_sq1,int i_lv1,NyARSquare i_sq2,int i_lv2,NyARSquare o_qr_square)\r
162         {\r
163                 //4辺の式を計算\r
164                 o_qr_square.line[0].copyFrom(i_sq1.line[(i_lv1+3)%4]);\r
165                 o_qr_square.line[1].copyFrom(i_sq1.line[(i_lv1+0)%4]);\r
166                 o_qr_square.line[2].copyFrom(i_sq2.line[(i_lv2+3)%4]);\r
167                 o_qr_square.line[3].copyFrom(i_sq2.line[(i_lv2+0)%4]);\r
168                 //歪み無しの座標系を計算\r
169                 final NyARDoublePoint2d[] l_sqvertex = o_qr_square.sqvertex;\r
170                 final NyARIntPoint[] imvertex_ptr = o_qr_square.imvertex;\r
171 \r
172                 final NyARLinear[] l_line = o_qr_square.line;\r
173                 final NyARDoublePoint2d ideal_vertex=this.__bindSquare_ideal_vertex;\r
174                 for (int i = 0; i < 4; i++) {\r
175                         final NyARLinear l_line_i = l_line[i];\r
176                         final NyARLinear l_line_2 = l_line[(i + 3) % 4];\r
177                         final double w1 = l_line_2.run * l_line_i.rise - l_line_i.run * l_line_2.rise;\r
178                         if (w1 == 0.0) {\r
179                                 return;\r
180                         }\r
181                         l_sqvertex[i].x = (l_line_2.rise * l_line_i.intercept - l_line_i.rise * l_line_2.intercept) / w1;\r
182                         l_sqvertex[i].y = (l_line_i.run * l_line_2.intercept - l_line_2.run * l_line_i.intercept) / w1;\r
183                         _distfactor.ideal2Observ(l_sqvertex[i], ideal_vertex);\r
184                         //Ideal→observに変換して、画面上の座標とする。\r
185                         imvertex_ptr[i].x=(int)l_sqvertex[i].x;\r
186                         imvertex_ptr[i].y=(int)l_sqvertex[i].y;\r
187                 }       \r
188 //              Graphics g=this.bimg.getGraphics();\r
189 //              g.setColor(Color.red);\r
190 //              int[] x=new int[4];\r
191 //              int[] y=new int[4];\r
192 //              for(int i=0;i<4;i++){\r
193 //                      x[i]=(int)l_sqvertex[i].x;\r
194 //                      y[i]=(int)l_sqvertex[i].y;\r
195 //              }\r
196 //              g.drawPolygon(x,y,4);\r
197                 return;\r
198                 //基準点はVertexをそのまま採用\r
199                 //2個の想定点は座標を逆変換して設定\r
200         }\r
201         /**\r
202          * directionはキーシンボルのインデックスでARToolKitの頂点座標じゃないので注意すること。\r
203          * @param i_sq\r
204          * @param o_sq\r
205          * @return\r
206          */\r
207         public boolean composeSquare(NyARSquare[] i_sq,NyARSquare o_sq)\r
208         {\r
209                 int[] minimum_triangle_vertex=new int[3];\r
210                 int[] minimum_line_vertex=new int[2];\r
211                 \r
212                 NyARIntPoint center=new NyARIntPoint();\r
213 \r
214                 //辺の長さが最小になる頂点の組合せを探す\r
215                 getMinimumTriangleVertex(i_sq,minimum_triangle_vertex);\r
216                 \r
217                 //中心位置を計算する。\r
218                 getSymbolGroupCenter(i_sq,center);\r
219                 \r
220                 //キーシンボルのインデクス番号を得る\r
221                 int key_simble_idx=getKeySymble(i_sq,center,minimum_triangle_vertex);\r
222                 \r
223                 //対角シンボルのインデックス番号を決める\r
224                 int symbol_e1_idx=(key_simble_idx+1)%3;\r
225                 int symbol_e2_idx=(key_simble_idx+2)%3;\r
226                 \r
227                 //対角シンボル間で最短距離を取る頂点ペアを取る\r
228                 //(角度を低くするとエラーが出やすい。対角線との類似性を確認する方法のほうがいい。多分)\r
229                 getMinimumLineVertex(i_sq[symbol_e1_idx].imvertex,i_sq[symbol_e2_idx].imvertex,minimum_line_vertex);\r
230                 \r
231                 //内対角を外対角に変換\r
232                 int lv1=(minimum_line_vertex[0]+2)%4;\r
233                 int lv2=(minimum_line_vertex[1]+2)%4;\r
234                 int kv =(minimum_triangle_vertex[key_simble_idx]+2)%4;\r
235                 //矩形の合成\r
236                 bindSquare(i_sq[symbol_e1_idx],lv1,i_sq[symbol_e2_idx],lv2,o_sq);\r
237                 \r
238                 //方位判定          \r
239                 int direction=getDirection(o_sq,i_sq[key_simble_idx].imvertex[kv],center);\r
240                 if(direction==-1){\r
241                         return false;\r
242                 }\r
243                 o_sq.direction=direction;\r
244                 \r
245                 return true;\r
246         }       \r
247         /**\r
248          * この関数はあんまり頂点ズレがひどいと失敗する\r
249          * @param i_square\r
250          * @param i_vertex\r
251          * @param i_center\r
252          * @return\r
253          */\r
254         private int getDirection(NyARSquare i_square,NyARIntPoint i_vertex,NyARIntPoint i_center)\r
255         {\r
256                 //開始点(中央シンボル)までの頂点のシフト数を決める\r
257                 int x,y;\r
258                 x=i_square.imvertex[0].x-i_vertex.x;\r
259                 y=i_square.imvertex[0].y-i_vertex.y;\r
260                 int v1=x*x+y*y;\r
261                 x=i_square.imvertex[2].x-i_vertex.x;\r
262                 y=i_square.imvertex[2].y-i_vertex.y;\r
263                 int v2=x*x+y*y;\r
264                 int shift;\r
265                 int v;\r
266                 if(v1<v2){\r
267                         shift=0;\r
268                         v=v1;\r
269                 }else{\r
270                         shift=2;\r
271                         v=v2;\r
272                 }\r
273                 //小さい方の対角線が64(8x8)より大きくずれてたら認識ミスとみなす\r
274                 if(v>64){\r
275                         return -1;\r
276                 }\r
277                 //シンボルがどの象限にあるか確認する\r
278                 x=i_vertex.x=i_center.x;\r
279                 y=i_vertex.y=i_center.y;\r
280                 int dir;\r
281                 if(x<0){\r
282                         dir=2;//dir=y<0?1:2;\r
283                 }else{\r
284                         dir=4;//dir=y<0?3:4;\r
285                 }\r
286                 return (dir+shift)%4;\r
287         }\r
288         \r
289         \r
290         \r
291 }