OSDN Git Service

31a3bd5b60ec4b766ab0d45b23dfdb1923fcc30b
[nyartoolkit-and/nyartoolkit-and.git] / sample / sandbox / jp / nyatla / nyartoolkit / sandbox / qrcode / NyARQrCodeDetector.java
1 package jp.nyatla.nyartoolkit.sandbox.qrcode;\r
2 \r
3 import jp.nyatla.nyartoolkit.NyARException;\r
4 import jp.nyatla.nyartoolkit.core.NyARVertexCounter;\r
5 import jp.nyatla.nyartoolkit.core.labeling.artoolkit.NyARLabelingImage;\r
6 import jp.nyatla.nyartoolkit.core.labeling.artoolkit.NyARLabelingLabel;\r
7 import jp.nyatla.nyartoolkit.core.labeling.artoolkit.NyARLabelingLabelStack;\r
8 import jp.nyatla.nyartoolkit.core.labeling.artoolkit.NyARLabeling_ARToolKit;\r
9 import jp.nyatla.nyartoolkit.core.param.NyARCameraDistortionFactor;\r
10 import jp.nyatla.nyartoolkit.core.pca2d.*;\r
11 import jp.nyatla.nyartoolkit.core.raster.NyARBinRaster;\r
12 import jp.nyatla.nyartoolkit.core.squaredetect.INyARSquareDetector;\r
13 import jp.nyatla.nyartoolkit.core.squaredetect.NyARSquare;\r
14 import jp.nyatla.nyartoolkit.core.squaredetect.NyARSquareStack;\r
15 import jp.nyatla.nyartoolkit.core.types.*;\r
16 import jp.nyatla.nyartoolkit.core.types.matrix.NyARDoubleMatrix22;\r
17 \r
18 public class NyARQrCodeDetector implements INyARSquareDetector\r
19 {\r
20         private NyARQrCodeSymbolBinder _binder;\r
21         private static final double VERTEX_FACTOR = 2.0;// 線検出のファクタ\r
22 \r
23         private static final int AR_AREA_MAX = 10000;\r
24 \r
25         private static final int AR_AREA_MIN = 50;\r
26 \r
27         private final int _width;\r
28 \r
29         private final int _height;\r
30 \r
31         private final NyARLabeling_ARToolKit _labeling;\r
32 \r
33         private final NyARLabelingImage _limage;\r
34 \r
35         private final NyARCameraDistortionFactor _dist_factor_ref;\r
36         private final double[] _xpos;\r
37         private final double[] _ypos;\r
38         /**\r
39          * 最大i_squre_max個のマーカーを検出するクラスを作成する。\r
40          * \r
41          * @param i_param\r
42          */\r
43         public NyARQrCodeDetector(NyARCameraDistortionFactor i_dist_factor_ref, NyARIntSize i_size) throws NyARException\r
44         {\r
45                 this._width = i_size.w;\r
46                 this._height = i_size.h;\r
47                 this._dist_factor_ref = i_dist_factor_ref;\r
48                 this._labeling = new NyARLabeling_ARToolKit();\r
49                 this._limage = new NyARLabelingImage(this._width, this._height);\r
50                 this._labeling.attachDestination(this._limage);\r
51                 this._binder=new NyARQrCodeSymbolBinder(i_dist_factor_ref);\r
52 \r
53                 // 輪郭の最大長はMAX_COORD_NUMの2倍に制限\r
54                 int number_of_coord = MAX_COORD_NUM* 2;\r
55 \r
56                 // 輪郭バッファはnumber_of_coordの2倍\r
57                 this._max_coord = number_of_coord;\r
58                 this._xcoord = new int[number_of_coord * 2];\r
59                 this._ycoord = new int[number_of_coord * 2];\r
60                 this._xpos=new double[this._width+this._height];//最大辺長はthis._width+this._height\r
61                 this._ypos=new double[this._width+this._height];//最大辺長はthis._width+this._height\r
62                 \r
63         }\r
64 \r
65         private final int _max_coord;\r
66 \r
67         private final int[] _xcoord;\r
68 \r
69         private final int[] _ycoord;\r
70 \r
71         private void normalizeCoord(int[] i_coord_x, int[] i_coord_y, int i_index, int i_coord_num)\r
72         {\r
73                 // vertex1を境界にして、後方に配列を連結\r
74                 System.arraycopy(i_coord_x, 1, i_coord_x, i_coord_num, i_index);\r
75                 System.arraycopy(i_coord_y, 1, i_coord_y, i_coord_num, i_index);\r
76         }\r
77 \r
78         private final int[] __detectMarker_mkvertex = new int[5];\r
79 \r
80         /**\r
81          * ARMarkerInfo2 *arDetectMarker2( ARInt16 *limage, int label_num, int *label_ref,int *warea, double *wpos, int *wclip,int area_max, int area_min, double\r
82          * factor, int *marker_num ) 関数の代替品 ラベリング情報からマーカー一覧を作成してo_marker_listを更新します。 関数はo_marker_listに重なりを除外したマーカーリストを作成します。\r
83          * \r
84          * @param i_raster\r
85          * 解析する2値ラスタイメージを指定します。\r
86          * @param o_square_stack\r
87          * 抽出した正方形候補を格納するリスト\r
88          * @throws NyARException\r
89          */\r
90         public final void detectMarker(NyARBinRaster i_raster, NyARSquareStack o_square_stack) throws NyARException\r
91         {\r
92                 final NyARLabeling_ARToolKit labeling_proc = this._labeling;\r
93                 final NyARLabelingImage limage = this._limage;\r
94 \r
95                 // 初期化\r
96 \r
97                 // マーカーホルダをリセット\r
98                 o_square_stack.clear();\r
99 \r
100                 // ラベリング\r
101                 labeling_proc.labeling(i_raster);\r
102 \r
103                 // ラベル数が0ならここまで\r
104                 final int label_num = limage.getLabelStack().getLength();\r
105                 if (label_num < 1) {\r
106                         return;\r
107                 }\r
108 \r
109                 final NyARLabelingLabelStack stack = limage.getLabelStack();\r
110                 final NyARLabelingLabel[] labels = stack.getArray();\r
111 \r
112                 // ラベルを大きい順に整列\r
113                 stack.sortByArea();\r
114 \r
115                 // デカいラベルを読み飛ばし\r
116                 int i;\r
117                 for (i = 0; i < label_num; i++) {\r
118                         // 検査対象内のラベルサイズになるまで無視\r
119                         if (labels[i].area <= AR_AREA_MAX) {\r
120                                 break;\r
121                         }\r
122                 }\r
123                 \r
124                 final int xsize = this._width;\r
125                 final int ysize = this._height;\r
126                 final int[] xcoord = this._xcoord;\r
127                 final int[] ycoord = this._ycoord;\r
128                 final int coord_max = this._max_coord;\r
129                 final int[] mkvertex = this.__detectMarker_mkvertex;\r
130                 final int[] buf = (int[]) limage.getBufferReader().getBuffer();\r
131                 final int[] indextable = limage.getIndexArray();\r
132                 int coord_num;\r
133                 int label_area;\r
134                 NyARLabelingLabel label_pt;\r
135                 NyARSquareStack wk_stack=new NyARSquareStack(10);\r
136                 wk_stack.clear();\r
137 \r
138                 for (; i < label_num; i++) {\r
139                         label_pt = labels[i];\r
140                         label_area = label_pt.area;\r
141                         // 検査対象サイズよりも小さくなったら終了\r
142                         if (label_area < AR_AREA_MIN) {\r
143                                 break;\r
144                         }\r
145                         // クリップ領域が画面の枠に接していれば除外\r
146                         if (label_pt.clip_l == 1 || label_pt.clip_r == xsize - 2) {// if(wclip[i*4+0] == 1 || wclip[i*4+1] ==xsize-2){\r
147                                 continue;\r
148                         }\r
149                         if (label_pt.clip_t == 1 || label_pt.clip_b == ysize - 2) {// if( wclip[i*4+2] == 1 || wclip[i*4+3] ==ysize-2){\r
150                                 continue;\r
151                         }\r
152                         // 特徴点候補であるかを確認する。\r
153                         if (!hasQrEdgeFeature(buf, indextable, label_pt)) {\r
154                                 continue;\r
155                         }\r
156 \r
157                         // 輪郭を取得\r
158                         coord_num = limage.getContour(i, coord_max, xcoord, ycoord);\r
159                         if (coord_num == coord_max) {\r
160                                 // 輪郭が大きすぎる。\r
161                                 continue;\r
162                         }\r
163                         // 頂点候補のインデクスを取得\r
164                         final int vertex1 = scanVertex(xcoord, ycoord, coord_num);\r
165 \r
166                         // 頂点候補(vertex1)を先頭に並べなおした配列を作成する。\r
167                         normalizeCoord(xcoord, ycoord, vertex1, coord_num);\r
168 \r
169                         // 頂点情報を取得\r
170                         if (!getSquareVertex(xcoord, ycoord, vertex1, coord_num, label_area, mkvertex)) {\r
171                                 continue;\r
172                         }\r
173                         NyARSquare square=(NyARSquare)wk_stack.prePush();\r
174                         //矩形からラインと観察座標を取得\r
175                         if(!getSquareLine(mkvertex,xcoord,ycoord,square.line,square.imvertex)){\r
176                                 wk_stack.pop();\r
177                                 continue;\r
178                         }\r
179                 }\r
180                 //シンボルの関連付け\r
181                 bindQrcodeEdge(wk_stack,o_square_stack);\r
182                 //エッジ同士の相関関係をしらべる。\r
183 \r
184                 return;\r
185         }\r
186 \r
187         /**\r
188          * QRコードのエッジグループを作る\r
189          * @param i_square_stack\r
190          */\r
191         public void bindQrcodeEdge(NyARSquareStack i_square_stack,NyARSquareStack o_square_stack) throws NyARException\r
192         {\r
193                 NyARSquare[] group=new NyARSquare[3];\r
194                 int number_of_edge=i_square_stack.getLength();\r
195                 if(number_of_edge<3){\r
196                         return;\r
197                 }\r
198                 NyARSquare[] sa=i_square_stack.getArray();\r
199                 for(int i=0;i<number_of_edge-2;i++)\r
200                 {       \r
201                         group[0]=sa[i];\r
202                         for(int i2=i+1;i2<number_of_edge-1;i2++)\r
203                         {\r
204                                 group[1]=sa[i2];\r
205                                 for(int i3=i2+1;i3<number_of_edge;i3++){\r
206                                         group[2]=sa[i3];\r
207                                         //3個のエッジの関連性を確認する。\r
208                                         NyARSquare new_square=(NyARSquare)o_square_stack.prePush();\r
209                                         if(!this._binder.composeSquare(group,new_square)){\r
210                                                 o_square_stack.pop();\r
211                                         }\r
212                                 }\r
213                         }\r
214                 }\r
215                 return;\r
216         }\r
217         private static int MAX_COORD_NUM=(320+240)*2;//サイズの1/2の長方形の編程度が目安(VGAなら(320+240)*2)\r
218         private final INyARPca2d _pca=new NyARPca2d_MatrixPCA_O2();\r
219         private final NyARDoubleMatrix22 __getSquareLine_evec=new NyARDoubleMatrix22();\r
220         private final NyARDoublePoint2d __getSquareLine_mean=new NyARDoublePoint2d();\r
221         private final NyARDoublePoint2d __getSquareLine_ev=new NyARDoublePoint2d();\r
222         /**\r
223          * 頂点インデクスと輪郭配列から、Ideal座標系とLineを作成して変数に返す\r
224          * @param i_cparam\r
225          * @return\r
226          * @throws NyARException\r
227          */\r
228         private boolean getSquareLine(int[] i_mkvertex, int[] i_xcoord, int[] i_ycoord, NyARLinear[] o_line,NyARIntPoint2d[] o_imvertex) throws NyARException\r
229         {\r
230                 final NyARDoubleMatrix22 evec=this.__getSquareLine_evec;\r
231                 final NyARDoublePoint2d mean=this.__getSquareLine_mean;\r
232                 final NyARDoublePoint2d ev=this.__getSquareLine_ev;\r
233         \r
234                 \r
235                 for (int i = 0; i < 4; i++) {\r
236                         final double w1 = (double) (i_mkvertex[i + 1] - i_mkvertex[i] + 1) * 0.05 + 0.5;\r
237                         final int st = (int) (i_mkvertex[i] + w1);\r
238                         final int ed = (int) (i_mkvertex[i + 1] - w1);\r
239                         final int n = ed - st + 1;\r
240                         if (n < 2 || n>MAX_COORD_NUM) {\r
241                                 // nが2以下、又はMAX_COORD_NUM以上なら主成分分析をしない。\r
242                                 return false;\r
243                         }\r
244                         //配列作成\r
245                         this._dist_factor_ref.observ2IdealBatch(i_xcoord, i_ycoord, st, n,this._xpos,this._ypos);\r
246                         \r
247                         //主成分分析する。\r
248                         this._pca.pca(this._xpos,this._ypos,n,evec, ev,mean);\r
249                         final NyARLinear l_line_i = o_line[i];\r
250                         l_line_i.run = evec.m01;// line[i][0] = evec->m[1];\r
251                         l_line_i.rise = -evec.m00;// line[i][1] = -evec->m[0];\r
252                         l_line_i.intercept = -(l_line_i.run * mean.x + l_line_i.rise * mean.y);// line[i][2] = -(line[i][0]*mean->v[0] + line[i][1]*mean->v[1]);\r
253                 }\r
254                 for (int i = 0; i < 4; i++) {\r
255                         final NyARLinear l_line_i = o_line[i];\r
256                         final NyARLinear l_line_2 = o_line[(i + 3) % 4];\r
257                         final double w1 = l_line_2.run * l_line_i.rise - l_line_i.run * l_line_2.rise;\r
258                         if (w1 == 0.0) {\r
259                                 return false;\r
260                         }\r
261                         // 頂点インデクスから頂点座標を得て保存\r
262                         o_imvertex[i].x = i_xcoord[i_mkvertex[i]];\r
263                         o_imvertex[i].y = i_ycoord[i_mkvertex[i]];\r
264                 }\r
265                 return true;\r
266         }\r
267         /**\r
268          * 辺からの対角線が最長になる点を対角線候補として返す。\r
269          * \r
270          * @param i_xcoord\r
271          * @param i_ycoord\r
272          * @param i_coord_num\r
273          * @return\r
274          */\r
275         private int scanVertex(int[] i_xcoord, int[] i_ycoord, int i_coord_num)\r
276         {\r
277                 final int sx = i_xcoord[0];\r
278                 final int sy = i_ycoord[0];\r
279                 int d = 0;\r
280                 int w, x, y;\r
281                 int ret = 0;\r
282                 for (int i = 1; i < i_coord_num; i++) {\r
283                         x = i_xcoord[i] - sx;\r
284                         y = i_ycoord[i] - sy;\r
285                         w = x * x + y * y;\r
286                         if (w > d) {\r
287                                 d = w;\r
288                                 ret = i;\r
289                         }\r
290                         // ここでうまく終了条件入れられないかな。\r
291                 }\r
292                 return ret;\r
293         }\r
294 \r
295         private final NyARVertexCounter __getSquareVertex_wv1 = new NyARVertexCounter();\r
296         private final NyARVertexCounter __getSquareVertex_wv2 = new NyARVertexCounter();\r
297 \r
298         /**\r
299          * static int arDetectMarker2_check_square( int area, ARMarkerInfo2 *marker_info2, double factor ) 関数の代替関数 OPTIMIZED STEP [450->415] o_squareに頂点情報をセットします。\r
300          * \r
301          * @param i_x_coord\r
302          * @param i_y_coord\r
303          * @param i_vertex1_index\r
304          * @param i_coord_num\r
305          * @param i_area\r
306          * @param o_vertex\r
307          * 要素数はint[4]である事\r
308          * @return\r
309          */\r
310         private boolean getSquareVertex(int[] i_x_coord, int[] i_y_coord, int i_vertex1_index, int i_coord_num, int i_area, int[] o_vertex)\r
311         {\r
312                 final NyARVertexCounter wv1 = this.__getSquareVertex_wv1;\r
313                 final NyARVertexCounter wv2 = this.__getSquareVertex_wv2;\r
314                 final int end_of_coord = i_vertex1_index + i_coord_num - 1;\r
315                 final int sx = i_x_coord[i_vertex1_index];// sx = marker_info2->x_coord[0];\r
316                 final int sy = i_y_coord[i_vertex1_index];// sy = marker_info2->y_coord[0];\r
317                 int dmax = 0;\r
318                 int v1 = i_vertex1_index;\r
319                 for (int i = 1 + i_vertex1_index; i < end_of_coord; i++) {// for(i=1;i<marker_info2->coord_num-1;i++)\r
320                         // {\r
321                         final int d = (i_x_coord[i] - sx) * (i_x_coord[i] - sx) + (i_y_coord[i] - sy) * (i_y_coord[i] - sy);\r
322                         if (d > dmax) {\r
323                                 dmax = d;\r
324                                 v1 = i;\r
325                         }\r
326                 }\r
327                 final double thresh = (i_area / 0.75) * 0.01 * VERTEX_FACTOR;\r
328 \r
329                 o_vertex[0] = i_vertex1_index;\r
330 \r
331                 if (!wv1.getVertex(i_x_coord, i_y_coord, i_vertex1_index, v1, thresh)) { // if(get_vertex(marker_info2->x_coord,marker_info2->y_coord,0,v1,thresh,wv1,&wvnum1)<\r
332                         // 0 ) {\r
333                         return false;\r
334                 }\r
335                 if (!wv2.getVertex(i_x_coord, i_y_coord, v1, end_of_coord, thresh)) {// if(get_vertex(marker_info2->x_coord,marker_info2->y_coord,v1,marker_info2->coord_num-1,thresh,wv2,&wvnum2)\r
336                         // < 0) {\r
337                         return false;\r
338                 }\r
339 \r
340                 int v2;\r
341                 if (wv1.number_of_vertex == 1 && wv2.number_of_vertex == 1) {// if(wvnum1 == 1 && wvnum2== 1) {\r
342                         o_vertex[1] = wv1.vertex[0];\r
343                         o_vertex[2] = v1;\r
344                         o_vertex[3] = wv2.vertex[0];\r
345                 } else if (wv1.number_of_vertex > 1 && wv2.number_of_vertex == 0) {// }else if( wvnum1 > 1 && wvnum2== 0) {\r
346                         // 頂点位置を、起点から対角点の間の1/2にあると予想して、検索する。\r
347                         v2 = (v1 - i_vertex1_index) / 2 + i_vertex1_index;\r
348                         if (!wv1.getVertex(i_x_coord, i_y_coord, i_vertex1_index, v2, thresh)) {\r
349                                 return false;\r
350                         }\r
351                         if (!wv2.getVertex(i_x_coord, i_y_coord, v2, v1, thresh)) {\r
352                                 return false;\r
353                         }\r
354                         if (wv1.number_of_vertex == 1 && wv2.number_of_vertex == 1) {\r
355                                 o_vertex[1] = wv1.vertex[0];\r
356                                 o_vertex[2] = wv2.vertex[0];\r
357                                 o_vertex[3] = v1;\r
358                         } else {\r
359                                 return false;\r
360                         }\r
361                 } else if (wv1.number_of_vertex == 0 && wv2.number_of_vertex > 1) {\r
362                         // v2 = (v1-i_vertex1_index+ end_of_coord-i_vertex1_index) / 2+i_vertex1_index;\r
363                         v2 = (v1 + end_of_coord) / 2;\r
364 \r
365                         if (!wv1.getVertex(i_x_coord, i_y_coord, v1, v2, thresh)) {\r
366                                 return false;\r
367                         }\r
368                         if (!wv2.getVertex(i_x_coord, i_y_coord, v2, end_of_coord, thresh)) {\r
369                                 return false;\r
370                         }\r
371                         if (wv1.number_of_vertex == 1 && wv2.number_of_vertex == 1) {\r
372                                 o_vertex[1] = v1;\r
373                                 o_vertex[2] = wv1.vertex[0];\r
374                                 o_vertex[3] = wv2.vertex[0];\r
375                         } else {\r
376                                 return false;\r
377                         }\r
378                 } else {\r
379                         return false;\r
380                 }\r
381                 o_vertex[4] = end_of_coord;\r
382                 return true;\r
383         }\r
384         /**\r
385          * QRコードのシンボル特徴を持つラベルであるかを調べる\r
386          * @param buf\r
387          * @param index_table\r
388          * @param i_label\r
389          * @return\r
390          */\r
391         private boolean hasQrEdgeFeature(int[] buf, int[] index_table, NyARLabelingLabel i_label)\r
392         {\r
393                 int tx, bx;\r
394                 int w;\r
395                 int i_label_id = i_label.id;\r
396                 int limage_j_ptr;\r
397                 final int clip_l = i_label.clip_l;\r
398                 final int clip_b = i_label.clip_b;\r
399                 final int clip_r = i_label.clip_r;\r
400                 final int clip_t = i_label.clip_t;\r
401 \r
402                 tx = bx = 0;\r
403                 // 上接点(→)\r
404                 limage_j_ptr = clip_t*this._width;\r
405                 for (int i = clip_l; i <= clip_r; i++) {// for( i = clip[0]; i <=clip[1]; i++, p1++ ) {\r
406                         w = buf[limage_j_ptr+i];\r
407                         if (w > 0 && index_table[w - 1] == i_label_id) {\r
408                                 tx = i;\r
409                                 break;\r
410                         }\r
411                 }\r
412                 // 下接点(←)\r
413                 limage_j_ptr = clip_b*this._width;\r
414                 for (int i = clip_r; i >= clip_l; i--) {// for( i = clip[0]; i <=clip[1]; i++, p1++ ) {\r
415                         w = buf[limage_j_ptr+i];\r
416                         if (w > 0 && index_table[w - 1] == i_label_id) {\r
417                                 bx = i;\r
418                                 break;\r
419                         }\r
420                 }\r
421                 final int cx = (clip_l + clip_r) / 2;\r
422                 final int cy = (clip_t + clip_b) / 2;\r
423                 // 横断チェック(中心から線を引いて、010になるかしらべる)\r
424                 if (!checkDiagonalLine(buf, cx, cy, bx, clip_b)) {\r
425                         return false;\r
426                 }\r
427                 if (!checkDiagonalLine(buf, tx, clip_t, cx, cy)) {\r
428                         return false;\r
429                 }\r
430                 return true;\r
431         }\r
432 \r
433         /**\r
434          * シンボルのパターン特徴を調べる関数\r
435          * 対角線の一部が010になってるか調べる。\r
436          * \r
437          * @param buf\r
438          * @param i_px1\r
439          * @param i_py1\r
440          * @param i_px2\r
441          * @param i_py2\r
442          * @return\r
443          */\r
444         private boolean checkDiagonalLine(int[] buf, int i_px1, int i_py1, int i_px2, int i_py2)\r
445         {\r
446                 int sub_y = i_py2 - i_py1;\r
447                 int sub_x = i_px2 - i_px1;\r
448                 // 黒\r
449                 int i = 0;\r
450                 for (; i < sub_y; i++) {\r
451                         int yp = i_py1 + i;\r
452                         int xp = i_px1 + i * sub_x / sub_y;\r
453                         if (buf[yp*this._width+xp] == 0 && buf[yp*this._width+(xp-1)] == 0 && buf[yp*this._width+(xp+1)] == 0) {\r
454                                 break;\r
455                         }\r
456 \r
457                 }\r
458                 if (i == sub_y) {\r
459                         return false;\r
460                 }\r
461                 // 白\r
462                 for (; i < sub_y; i++) {\r
463                         int yp = i_py1 + i;\r
464                         int xp = i_px1 + i * sub_x / sub_y;\r
465                         if (buf[yp*this._width+xp] != 0 && buf[yp*this._width+(xp-1)] != 0 && buf[yp*this._width+(xp+1)] != 0) {\r
466                                 break;\r
467                         }\r
468 \r
469                 }\r
470                 if (i == sub_y) {\r
471                         return false;\r
472                 }\r
473                 // 黒\r
474                 for (; i < sub_y; i++) {\r
475                         int yp = i_py1 + i;\r
476                         int xp = i_px1 + i * sub_x / sub_y;\r
477                         if (buf[yp*this._width+xp] == 0 && buf[yp*this._width+(xp-1)] == 0 && buf[yp*this._width+(xp+1)] == 0) {\r
478                                 break;\r
479                         }\r
480 \r
481                 }\r
482                 if (i != sub_y) {\r
483                         return false;\r
484                 }\r
485                 // 端まで到達したらOK\r
486                 return true;\r
487         }\r
488 \r
489 }\r