1 package jp.nyatla.nyartoolkit.sandbox.qrcode;
\r
3 import jp.nyatla.nyartoolkit.core.*;
\r
4 import jp.nyatla.nyartoolkit.core.types.*;
\r
5 import jp.nyatla.nyartoolkit.core.param.*;
\r
7 * QRコードのシンボルを結びつける偉いクラス
\r
9 * 1.3シンボルの位置関係から中間のシンボルを探す。
\r
11 * 3.残りの2シンボル間の最短距離の頂点セットを見つけて、それぞれの内角点を探す
\r
12 * 4.3個の内角点が決まったら、各シンボルごとに外角点(反対側の頂点)を特定する。
\r
13 * 5.対角のシンボルの外角頂点から伸びる線分を合成して、矩形を決める。
\r
14 * 6.矩形が決まったら、方程式を解いて交点を出して、頂点にする。
\r
15 * 7.交点と中央のシンボルの位置関係から、正しい計算が行われたかを判定(まだ実装してない)
\r
18 * この方法は浅い角度でシンボル集合を見たときに、1や3の手順が高い確率で失敗する。
\r
19 * その場合計算が途中で破綻するのでわかる(はず)
\r
20 * 他の方法もあるけど、それはまた今度。
\r
22 public class NyARQrCodeSymbolBinder
\r
24 private NyARCameraDistortionFactor _distfactor;
\r
26 public NyARQrCodeSymbolBinder(NyARCameraDistortionFactor i_ref_distortion)
\r
28 this._distfactor=i_ref_distortion;
\r
32 * 最小の三角形を構成する頂点セットを得る
\r
38 private static void getMinimumTriangleVertex(NyARSquare[] i_sqare,int[] o_vertex_id)
\r
40 //辺の長さが最小になる頂点の組合せを探す
\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
49 for(int i2=0;i2<4;i2++)
\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
55 x=vertex1[i2].x-vertex2[i3].x;
\r
56 y=vertex1[i2].y-vertex2[i3].y;
\r
58 x=vertex1[i2].x-vertex0[i].x;
\r
59 y=vertex1[i2].y-vertex0[i].y;
\r
73 * 2矩形の頂点距離が最低の組合せを探す
\r
75 * @param o_vertex_id
\r
77 private static void getMinimumLineVertex(NyARIntPoint[] i_sqare0,NyARIntPoint[] i_sqare1,int[] o_vertex_id)
\r
79 //辺の長さが最小になる頂点の組合せを探す
\r
82 int dmax=0x7fffffff;
\r
83 for(int i=0;i<4;i++)
\r
85 for(int i2=0;i2<4;i2++)
\r
87 x=i_sqare1[i2].x-i_sqare0[i].x;
\r
88 y=i_sqare1[i2].y-i_sqare0[i].y;
\r
104 private void getSymbolGroupCenter(NyARSquare[] i_sqare,NyARIntPoint i_center)
\r
109 for(int i=0;i<3;i++)
\r
111 final NyARIntPoint[] sq_ptr=i_sqare[i].imvertex;
\r
128 * @param i_vertex_id
\r
132 private static int getKeySymble(NyARSquare[] i_sqare,NyARIntPoint i_center,int[] i_vertex_id)
\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
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
151 return key_symble_idx;
\r
153 private NyARDoublePoint2d __bindSquare_ideal_vertex=new NyARDoublePoint2d();
\r
155 * 2つの対角にある矩形から、それらを対角とする矩形を作る。
\r
161 private void bindSquare(NyARSquare i_sq1,int i_lv1,NyARSquare i_sq2,int i_lv2,NyARSquare o_qr_square)
\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
169 final NyARDoublePoint2d[] l_sqvertex = o_qr_square.sqvertex;
\r
170 final NyARIntPoint[] imvertex_ptr = o_qr_square.imvertex;
\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
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
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
196 // g.drawPolygon(x,y,4);
\r
198 //基準点はVertexをそのまま採用
\r
199 //2個の想定点は座標を逆変換して設定
\r
202 * directionはキーシンボルのインデックスでARToolKitの頂点座標じゃないので注意すること。
\r
207 public boolean composeSquare(NyARSquare[] i_sq,NyARSquare o_sq)
\r
209 int[] minimum_triangle_vertex=new int[3];
\r
210 int[] minimum_line_vertex=new int[2];
\r
212 NyARIntPoint center=new NyARIntPoint();
\r
214 //辺の長さが最小になる頂点の組合せを探す
\r
215 getMinimumTriangleVertex(i_sq,minimum_triangle_vertex);
\r
218 getSymbolGroupCenter(i_sq,center);
\r
220 //キーシンボルのインデクス番号を得る
\r
221 int key_simble_idx=getKeySymble(i_sq,center,minimum_triangle_vertex);
\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
227 //対角シンボル間で最短距離を取る頂点ペアを取る
\r
228 //(角度を低くするとエラーが出やすい。対角線との類似性を確認する方法のほうがいい。多分)
\r
229 getMinimumLineVertex(i_sq[symbol_e1_idx].imvertex,i_sq[symbol_e2_idx].imvertex,minimum_line_vertex);
\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
236 bindSquare(i_sq[symbol_e1_idx],lv1,i_sq[symbol_e2_idx],lv2,o_sq);
\r
239 int direction=getDirection(o_sq,i_sq[key_simble_idx].imvertex[kv],center);
\r
243 o_sq.direction=direction;
\r
248 * この関数はあんまり頂点ズレがひどいと失敗する
\r
254 private int getDirection(NyARSquare i_square,NyARIntPoint i_vertex,NyARIntPoint i_center)
\r
256 //開始点(中央シンボル)までの頂点のシフト数を決める
\r
258 x=i_square.imvertex[0].x-i_vertex.x;
\r
259 y=i_square.imvertex[0].y-i_vertex.y;
\r
261 x=i_square.imvertex[2].x-i_vertex.x;
\r
262 y=i_square.imvertex[2].y-i_vertex.y;
\r
273 //小さい方の対角線が64(8x8)より大きくずれてたら認識ミスとみなす
\r
277 //シンボルがどの象限にあるか確認する
\r
278 x=i_vertex.x=i_center.x;
\r
279 y=i_vertex.y=i_center.y;
\r
282 dir=2;//dir=y<0?1:2;
\r
284 dir=4;//dir=y<0?3:4;
\r
286 return (dir+shift)%4;
\r