OSDN Git Service

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