OSDN Git Service

[TAG]NyARToolkit/2.5.0
[nyartoolkit-and/nyartoolkit-and.git] / tags / 2.5.0 / 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.labeling.artoolkit.NyARLabelingImage;\r
5 import jp.nyatla.nyartoolkit.core.labeling.artoolkit.NyARLabelingLabel;\r
6 import jp.nyatla.nyartoolkit.core.labeling.artoolkit.NyARLabelingLabelStack;\r
7 import jp.nyatla.nyartoolkit.core.labeling.artoolkit.NyARLabeling_ARToolKit;\r
8 import jp.nyatla.nyartoolkit.core.param.NyARCameraDistortionFactor;\r
9 import jp.nyatla.nyartoolkit.core.raster.NyARBinRaster;\r
10 import jp.nyatla.nyartoolkit.core.squaredetect.NyARContourPickup;\r
11 import jp.nyatla.nyartoolkit.core.squaredetect.NyARSquareContourDetector;\r
12 import jp.nyatla.nyartoolkit.core.squaredetect.NyARSquare;\r
13 import jp.nyatla.nyartoolkit.core.squaredetect.NyARSquareStack;\r
14 import jp.nyatla.nyartoolkit.core.squaredetect.NyARCoord2Linear;\r
15 import jp.nyatla.nyartoolkit.core.types.*;\r
16 \r
17 public class NyARQrCodeDetector extends NyARSquareContourDetector\r
18 {\r
19         private NyARQrCodeSymbolBinder _binder;\r
20 \r
21         private static final int AR_AREA_MAX = 10000;\r
22 \r
23         private static final int AR_AREA_MIN = 50;\r
24 \r
25         private final int _width;\r
26 \r
27         private final int _height;\r
28 \r
29         private final NyARLabeling_ARToolKit _labeling;\r
30 \r
31         private final NyARLabelingImage _limage;\r
32 \r
33         private final NyARCoord2Linear _sqconvertor;\r
34         private final NyARContourPickup _cpickup=new NyARContourPickup();\r
35         \r
36         /**\r
37          * 最大i_squre_max個のマーカーを検出するクラスを作成する。\r
38          * \r
39          * @param i_param\r
40          */\r
41         public NyARQrCodeDetector(NyARCameraDistortionFactor i_dist_factor_ref, NyARIntSize i_size) throws NyARException\r
42         {\r
43                 this._width = i_size.w;\r
44                 this._height = i_size.h;\r
45                 this._labeling = new NyARLabeling_ARToolKit();\r
46                 this._limage = new NyARLabelingImage(this._width, this._height);\r
47                 this._binder=new NyARQrCodeSymbolBinder(i_dist_factor_ref);\r
48                 this._sqconvertor=new NyARCoord2Linear(i_size,i_dist_factor_ref);\r
49 \r
50                 // 輪郭の最大長はMAX_COORD_NUMの2倍に制限\r
51                 int number_of_coord = MAX_COORD_NUM* 2;\r
52 \r
53                 // 輪郭バッファはnumber_of_coordの2倍\r
54                 this._max_coord = number_of_coord;\r
55                 this._xcoord = new int[number_of_coord * 2];\r
56                 this._ycoord = new int[number_of_coord * 2];\r
57                 \r
58         }\r
59 \r
60         private final int _max_coord;\r
61 \r
62         private final int[] _xcoord;\r
63 \r
64         private final int[] _ycoord;\r
65 \r
66 \r
67         /**\r
68          * ARMarkerInfo2 *arDetectMarker2( ARInt16 *limage, int label_num, int *label_ref,int *warea, double *wpos, int *wclip,int area_max, int area_min, double\r
69          * factor, int *marker_num ) 関数の代替品 ラベリング情報からマーカー一覧を作成してo_marker_listを更新します。 関数はo_marker_listに重なりを除外したマーカーリストを作成します。\r
70          * \r
71          * @param i_raster\r
72          * 解析する2値ラスタイメージを指定します。\r
73          * @param o_square_stack\r
74          * 抽出した正方形候補を格納するリスト\r
75          * @throws NyARException\r
76          */\r
77         public final void detectMarker(NyARBinRaster i_raster, NyARSquareStack o_square_stack) throws NyARException\r
78         {\r
79                 final NyARLabelingImage limage = this._limage;\r
80 \r
81                 // 初期化\r
82 \r
83                 // マーカーホルダをリセット\r
84                 o_square_stack.clear();\r
85 \r
86                 // ラベリング\r
87                 this._labeling.labeling(i_raster,limage);\r
88 \r
89                 // ラベル数が0ならここまで\r
90                 final int label_num = limage.getLabelStack().getLength();\r
91                 if (label_num < 1) {\r
92                         return;\r
93                 }\r
94 \r
95                 final NyARLabelingLabelStack stack = limage.getLabelStack();\r
96                 // ラベルを大きい順に整列\r
97                 stack.sortByArea();\r
98 \r
99                 final NyARLabelingLabel[] labels = stack.getArray();\r
100                 // デカいラベルを読み飛ばし\r
101                 int i;\r
102                 for (i = 0; i < label_num; i++) {\r
103                         // 検査対象内のラベルサイズになるまで無視\r
104                         if (labels[i].area <= AR_AREA_MAX) {\r
105                                 break;\r
106                         }\r
107                 }\r
108                 \r
109                 final int xsize = this._width;\r
110                 final int ysize = this._height;\r
111                 final int[] xcoord = this._xcoord;\r
112                 final int[] ycoord = this._ycoord;\r
113                 final int coord_max = this._max_coord;\r
114                 final int[] buf = (int[]) limage.getBuffer();\r
115                 final int[] indextable = limage.getIndexArray();\r
116 \r
117                 int label_area;\r
118                 NyARLabelingLabel label_pt;\r
119                 NyARSquareStack wk_stack=new NyARSquareStack(10);\r
120                 wk_stack.clear();\r
121 \r
122                 for (; i < label_num; i++) {\r
123                         label_pt = labels[i];\r
124                         label_area = label_pt.area;\r
125                         // 検査対象サイズよりも小さくなったら終了\r
126                         if (label_area < AR_AREA_MIN) {\r
127                                 break;\r
128                         }\r
129                         // クリップ領域が画面の枠に接していれば除外\r
130                         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
131                                 continue;\r
132                         }\r
133                         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
134                                 continue;\r
135                         }\r
136                         // 特徴点候補であるかを確認する。\r
137                         if (!hasQrEdgeFeature(buf, indextable, label_pt)) {\r
138                                 continue;\r
139                         }\r
140                         // 輪郭を取得\r
141                         final int coord_num = _cpickup.getContour(limage,limage.getTopClipTangentX(label_pt),label_pt.clip_t, coord_max, xcoord, ycoord);\r
142                         if (coord_num == coord_max) {\r
143                                 // 輪郭が大きすぎる。\r
144                                 continue;\r
145                         }\r
146                         //輪郭分析用に正規化する。\r
147                         final int vertex1 = NyARCoord2Linear.normalizeCoord(xcoord, ycoord, coord_num);\r
148 \r
149                         //ここから先が輪郭分析\r
150                         NyARSquare square_ptr = o_square_stack.prePush();\r
151                         if(!this._sqconvertor.coordToSquare(xcoord,ycoord,vertex1,coord_num,label_area,square_ptr)){\r
152                                 o_square_stack.pop();// 頂点の取得が出来なかったので破棄\r
153                                 continue;                               \r
154                         }\r
155                 }\r
156                 //シンボルの関連付け\r
157                 bindQrcodeEdge(wk_stack,o_square_stack);\r
158                 //エッジ同士の相関関係をしらべる。\r
159 \r
160                 return;\r
161         }\r
162 \r
163         /**\r
164          * QRコードのエッジグループを作る\r
165          * @param i_square_stack\r
166          */\r
167         public void bindQrcodeEdge(NyARSquareStack i_square_stack,NyARSquareStack o_square_stack) throws NyARException\r
168         {\r
169                 NyARSquare[] group=new NyARSquare[3];\r
170                 int number_of_edge=i_square_stack.getLength();\r
171                 if(number_of_edge<3){\r
172                         return;\r
173                 }\r
174                 NyARSquare[] sa=i_square_stack.getArray();\r
175                 for(int i=0;i<number_of_edge-2;i++)\r
176                 {       \r
177                         group[0]=sa[i];\r
178                         for(int i2=i+1;i2<number_of_edge-1;i2++)\r
179                         {\r
180                                 group[1]=sa[i2];\r
181                                 for(int i3=i2+1;i3<number_of_edge;i3++){\r
182                                         group[2]=sa[i3];\r
183                                         //3個のエッジの関連性を確認する。\r
184                                         NyARSquare new_square=(NyARSquare)o_square_stack.prePush();\r
185                                         if(!this._binder.composeSquare(group,new_square)){\r
186                                                 o_square_stack.pop();\r
187                                         }\r
188                                 }\r
189                         }\r
190                 }\r
191                 return;\r
192         }\r
193         private static int MAX_COORD_NUM=(320+240)*2;//サイズの1/2の長方形の編程度が目安(VGAなら(320+240)*2)\r
194 \r
195         /**\r
196          * QRコードのシンボル特徴を持つラベルであるかを調べる\r
197          * @param buf\r
198          * @param index_table\r
199          * @param i_label\r
200          * @return\r
201          */\r
202         private boolean hasQrEdgeFeature(int[] buf, int[] index_table, NyARLabelingLabel i_label)\r
203         {\r
204                 int tx, bx;\r
205                 int w;\r
206                 int i_label_id = i_label.id;\r
207                 int limage_j_ptr;\r
208                 final int clip_l = i_label.clip_l;\r
209                 final int clip_b = i_label.clip_b;\r
210                 final int clip_r = i_label.clip_r;\r
211                 final int clip_t = i_label.clip_t;\r
212 \r
213                 tx = bx = 0;\r
214                 // 上接点(→)\r
215                 limage_j_ptr = clip_t*this._width;\r
216                 for (int i = clip_l; i <= clip_r; i++) {// for( i = clip[0]; i <=clip[1]; i++, p1++ ) {\r
217                         w = buf[limage_j_ptr+i];\r
218                         if (w > 0 && index_table[w - 1] == i_label_id) {\r
219                                 tx = i;\r
220                                 break;\r
221                         }\r
222                 }\r
223                 // 下接点(←)\r
224                 limage_j_ptr = clip_b*this._width;\r
225                 for (int i = clip_r; i >= clip_l; i--) {// for( i = clip[0]; i <=clip[1]; i++, p1++ ) {\r
226                         w = buf[limage_j_ptr+i];\r
227                         if (w > 0 && index_table[w - 1] == i_label_id) {\r
228                                 bx = i;\r
229                                 break;\r
230                         }\r
231                 }\r
232                 final int cx = (clip_l + clip_r) / 2;\r
233                 final int cy = (clip_t + clip_b) / 2;\r
234                 // 横断チェック(中心から線を引いて、010になるかしらべる)\r
235                 if (!checkDiagonalLine(buf, cx, cy, bx, clip_b)) {\r
236                         return false;\r
237                 }\r
238                 if (!checkDiagonalLine(buf, tx, clip_t, cx, cy)) {\r
239                         return false;\r
240                 }\r
241                 return true;\r
242         }\r
243 \r
244         /**\r
245          * シンボルのパターン特徴を調べる関数\r
246          * 対角線の一部が010になってるか調べる。\r
247          * \r
248          * @param buf\r
249          * @param i_px1\r
250          * @param i_py1\r
251          * @param i_px2\r
252          * @param i_py2\r
253          * @return\r
254          */\r
255         private boolean checkDiagonalLine(int[] buf, int i_px1, int i_py1, int i_px2, int i_py2)\r
256         {\r
257                 int sub_y = i_py2 - i_py1;\r
258                 int sub_x = i_px2 - i_px1;\r
259                 // 黒\r
260                 int i = 0;\r
261                 for (; i < sub_y; i++) {\r
262                         int yp = i_py1 + i;\r
263                         int xp = i_px1 + i * sub_x / sub_y;\r
264                         if (buf[yp*this._width+xp] == 0 && buf[yp*this._width+(xp-1)] == 0 && buf[yp*this._width+(xp+1)] == 0) {\r
265                                 break;\r
266                         }\r
267 \r
268                 }\r
269                 if (i == sub_y) {\r
270                         return false;\r
271                 }\r
272                 // 白\r
273                 for (; i < sub_y; i++) {\r
274                         int yp = i_py1 + i;\r
275                         int xp = i_px1 + i * sub_x / sub_y;\r
276                         if (buf[yp*this._width+xp] != 0 && buf[yp*this._width+(xp-1)] != 0 && buf[yp*this._width+(xp+1)] != 0) {\r
277                                 break;\r
278                         }\r
279 \r
280                 }\r
281                 if (i == sub_y) {\r
282                         return false;\r
283                 }\r
284                 // 黒\r
285                 for (; i < sub_y; i++) {\r
286                         int yp = i_py1 + i;\r
287                         int xp = i_px1 + i * sub_x / sub_y;\r
288                         if (buf[yp*this._width+xp] == 0 && buf[yp*this._width+(xp-1)] == 0 && buf[yp*this._width+(xp+1)] == 0) {\r
289                                 break;\r
290                         }\r
291 \r
292                 }\r
293                 if (i != sub_y) {\r
294                         return false;\r
295                 }\r
296                 // 端まで到達したらOK\r
297                 return true;\r
298         }\r
299 \r
300 }\r