2 * PROJECT: NyARToolkit(Extension)
\r
3 * --------------------------------------------------------------------------------
\r
4 * The NyARToolkit is Java edition ARToolKit class library.
\r
5 * Copyright (C)2008-2009 Ryo Iizuka
\r
7 * This program is free software: you can redistribute it and/or modify
\r
8 * it under the terms of the GNU General Public License as published by
\r
9 * the Free Software Foundation, either version 3 of the License, or
\r
10 * (at your option) any later version.
\r
12 * This program is distributed in the hope that it will be useful,
\r
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
15 * GNU General Public License for more details.
\r
17 * You should have received a copy of the GNU General Public License
\r
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
\r
20 * For further information please contact.
\r
21 * http://nyatla.jp/nyatoolkit/
\r
22 * <airmail(at)ebony.plala.or.jp> or <nyatla(at)nyatla.jp>
\r
25 package jp.nyatla.nyartoolkit.core.labeling.rlelabeling;
\r
27 import jp.nyatla.nyartoolkit.NyARException;
\r
28 import jp.nyatla.nyartoolkit.core.raster.*;
\r
29 import jp.nyatla.nyartoolkit.core.types.*;
\r
30 import jp.nyatla.nyartoolkit.core.types.stack.NyARObjectStack;
\r
36 * このクラスは、RLE圧縮を利用した輪郭線エントリポイント検出用のラべリングクラスです。
\r
37 * ラべリング画像を生成せずに、ラベル輪郭へのエントリーポイントの一覧を作ることに注意してください。
\r
39 * このクラスはいくつかの自己コールバック関数を持つ抽象クラスです。継承クラスでコールバック関数を実装して使います。
\r
41 * <li>{@link #onLabelFound}- {@link #labeling}関数が検出したラベルを通知するコールバック関数です。
\r
42 * ここに、発見したラベルを処理するコードを書きます。
\r
45 * <p>ラベル輪郭のエントリーポイント -
\r
46 * このエントリポイントは、ラベルを構成する塊の輪郭を指す1点です。ここから8方位輪郭検出を実行すると、
\r
47 * ラベルの輪郭を一周することができます。
\r
50 * <p>{@link NyARBinRaster}を入力する場合
\r
52 * <li>{@link NyARBufferType#INT1D_BIN_8}
\r
55 * <p>{@link NyARGrayscaleRaster}を入力する場合
\r
57 * <li>{@link NyARBufferType#INT1D_GRAY_8}
\r
62 public abstract class NyARLabeling_Rle
\r
64 private static final int AR_AREA_MAX = 100000;// #define AR_AREA_MAX 100000
\r
65 private static final int AR_AREA_MIN = 70;// #define AR_AREA_MIN 70
\r
67 private RleInfoStack _rlestack;
\r
68 private RleElement[] _rle1;
\r
69 private RleElement[] _rle2;
\r
70 private int _max_area;
\r
71 private int _min_area;
\r
73 protected NyARIntSize _raster_size=new NyARIntSize();
\r
75 * コンストラクタです。{@link #labeling}に入力するラスタのサイズを指定して、インスタンスを生成します。
\r
80 * @throws NyARException
\r
82 public NyARLabeling_Rle(int i_width,int i_height) throws NyARException
\r
84 this._raster_size.setValue(i_width,i_height);
\r
85 this._rlestack=new RleInfoStack(i_width*i_height*2048/(320*240)+32);
\r
86 this._rle1 = RleElement.createArray(i_width/2+1);
\r
87 this._rle2 = RleElement.createArray(i_width/2+1);
\r
88 this._max_area=AR_AREA_MAX;
\r
89 this._min_area=AR_AREA_MIN;
\r
94 * 検出するラベルのエリア(画素数)範囲を設定します。
\r
95 * この範囲にあるラベルのみが、結果に返されます。
\r
96 * 初期値は、{@link #AR_AREA_MAX},{@link #AR_AREA_MIN}です。
\r
102 public void setAreaRange(int i_max,int i_min)
\r
104 assert(i_min>0 && i_max>i_min);
\r
105 this._max_area=i_max;
\r
106 this._min_area=i_min;
\r
111 * i_bin_bufのgsイメージをREL圧縮する。
\r
117 * BINラスタのときは0,GSラスタの時は閾値を指定する。
\r
118 * この関数は、閾値を暗点と認識します。
\r
122 private final int toRel(int[] i_bin_buf, int i_st, int i_len, RleElement[] i_out,int i_th)
\r
128 final int right_edge = i_st + i_len - 1;
\r
129 while (x < right_edge) {
\r
131 if (i_bin_buf[x] > i_th) {
\r
137 i_out[current].l = r;
\r
140 while (x < right_edge) {
\r
141 if (i_bin_buf[x] > i_th) {
\r
142 // 明点(1)→暗点(0)配列終了>登録
\r
143 i_out[current].r = r;
\r
146 r = -1;// 右端の位置を0に。
\r
155 // 最後の1点だけ判定方法が少し違うの。
\r
156 if (i_bin_buf[x] > i_th) {
\r
157 // 明点→rカウント中なら暗点配列終了>登録
\r
159 i_out[current].r = r;
\r
163 // 暗点→カウント中でなければl1で追加
\r
165 i_out[current].r = (r + 1);
\r
168 i_out[current].l = (i_len - 1);
\r
169 i_out[current].r = (i_len);
\r
177 * フラグメントをRLEスタックへ追加する。
\r
180 * @param i_row_index
\r
183 * @throws NyARException
\r
185 private final boolean addFragment(RleElement i_rel_img, int i_nof, int i_row_index,RleInfoStack o_stack) throws NyARException
\r
188 final int len=i_rel_img.r - l;
\r
189 i_rel_img.fid = i_nof;// REL毎の固有ID
\r
190 NyARRleLabelFragmentInfo v = o_stack.prePush();
\r
192 System.err.println("addFragment force recover!");
\r
198 v.clip_r=i_rel_img.r-1;
\r
199 v.clip_t=i_row_index;
\r
200 v.clip_b=i_row_index;
\r
201 v.pos_x=(len*(2*l+(len-1)))/2;
\r
202 v.pos_y=i_row_index*len;
\r
207 * この関数は、2値イメージの{@link NyARBinRaster}ラスタをラベリングします。
\r
208 * 検出したラベルは、自己コールバック関数{@link #onLabelFound}で通知します。
\r
209 * @param i_bin_raster
\r
210 * 入力画像。対応する形式は、クラスの説明を参照してください。
\r
211 * @throws NyARException
\r
213 public void labeling(NyARBinRaster i_bin_raster) throws NyARException
\r
215 assert(i_bin_raster.isEqualBufferType(NyARBufferType.INT1D_BIN_8));
\r
216 NyARIntSize size=i_bin_raster.getSize();
\r
217 this.imple_labeling(i_bin_raster,0,0,0,size.w,size.h);
\r
220 * この関数は、2値イメージの{@link NyARBinRaster}ラスタの指定範囲をラベリングします。
\r
221 * 検出したラベルは、自己コールバック関数{@link #onLabelFound}で通知します。
\r
222 * @param i_bin_raster
\r
223 * 入力画像。対応する形式は、クラスの説明を参照してください。
\r
226 * @throws NyARException
\r
228 public void labeling(NyARBinRaster i_bin_raster,NyARIntRect i_area) throws NyARException
\r
230 assert(i_bin_raster.isEqualBufferType(NyARBufferType.INT1D_BIN_8));
\r
231 this.imple_labeling(i_bin_raster,0,i_area.x,i_area.y,i_area.w,i_area.h);
\r
234 * この関数は、2値イメージの{@link NyARBinRaster}ラスタをラベリングします。
\r
235 * 検出したラベルは、自己コールバック関数{@link #onLabelFound}で通知します。
\r
236 * @param i_gs_raster
\r
237 * 入力画像。対応する形式は、クラスの説明を参照してください。
\r
239 * 暗点を判定するための敷居値0から255の数値である事。
\r
240 * @throws NyARException
\r
242 public void labeling(NyARGrayscaleRaster i_gs_raster,int i_th) throws NyARException
\r
244 assert(i_gs_raster.isEqualBufferType(NyARBufferType.INT1D_GRAY_8));
\r
245 NyARIntSize size=i_gs_raster.getSize();
\r
246 this.imple_labeling(i_gs_raster,i_th,0,0,size.w,size.h);
\r
249 * この関数は、2値イメージの{@link NyARBinRaster}ラスタの指定範囲をラベリングします。
\r
250 * 検出したラベルは、自己コールバック関数{@link #onLabelFound}で通知します。
\r
251 * @param i_gs_raster
\r
252 * 入力画像。対応する形式は、クラスの説明を参照してください。
\r
256 * 暗点を判定するための敷居値0から255の数値である事。
\r
257 * @throws NyARException
\r
259 public void labeling(NyARGrayscaleRaster i_gs_raster,NyARIntRect i_area,int i_th) throws NyARException
\r
261 assert(i_gs_raster.isEqualBufferType(NyARBufferType.INT1D_GRAY_8));
\r
262 this.imple_labeling(i_gs_raster,i_th,i_area.x,i_area.y,i_area.w,i_area.h);
\r
264 private void imple_labeling(INyARRaster i_raster,int i_th,int i_left,int i_top,int i_width, int i_height) throws NyARException
\r
267 assert(i_raster.getSize().isEqualSize(this._raster_size));
\r
269 RleElement[] rle_prev = this._rle1;
\r
270 RleElement[] rle_current = this._rle2;
\r
272 final RleInfoStack rlestack=this._rlestack;
\r
277 int len_current = 0;
\r
278 final int bottom=i_top+i_height;
\r
279 final int row_stride=this._raster_size.w;
\r
280 int[] in_buf = (int[]) i_raster.getBuffer();
\r
284 int rle_top_index=i_left+row_stride*i_top;
\r
287 len_prev = toRel(in_buf, rle_top_index, i_width, rle_prev,i_th);
\r
288 for (int i = 0; i < len_prev; i++) {
\r
289 // フラグメントID=フラグメント初期値、POS=Y値、RELインデクス=行
\r
290 if(addFragment(rle_prev[i], id_max, i_top,rlestack)){
\r
296 NyARRleLabelFragmentInfo[] f_array = rlestack.getArray();
\r
298 for (int y = i_top + 1; y < bottom; y++) {
\r
300 rle_top_index+=row_stride;
\r
301 len_current = toRel(in_buf,rle_top_index, i_width, rle_current,i_th);
\r
302 int index_prev = 0;
\r
304 SCAN_CUR: for (int i = 0; i < len_current; i++) {
\r
305 // index_prev,len_prevの位置を調整する
\r
307 // チェックすべきprevがあれば確認
\r
308 SCAN_PREV: while (index_prev < len_prev) {
\r
309 if (rle_current[i].l - rle_prev[index_prev].r > 0) {// 0なら8方位ラベリング
\r
310 // prevがcurの左方にある→次のフラグメントを探索
\r
313 } else if (rle_prev[index_prev].l - rle_current[i].r > 0) {// 0なら8方位ラベリングになる
\r
314 // prevがcur右方にある→独立フラグメント
\r
315 if(addFragment(rle_current[i], id_max, y,rlestack)){
\r
322 id=rle_prev[index_prev].fid;//ルートフラグメントid
\r
323 NyARRleLabelFragmentInfo id_ptr = f_array[id];
\r
324 //結合対象(初回)->prevのIDをコピーして、ルートフラグメントの情報を更新
\r
325 rle_current[i].fid = id;//フラグメントIDを保存
\r
327 final int l= rle_current[i].l;
\r
328 final int r= rle_current[i].r;
\r
330 //結合先フラグメントの情報を更新する。
\r
331 id_ptr.area += len;
\r
332 //tとentry_xは、結合先のを使うので更新しない。
\r
333 id_ptr.clip_l=l<id_ptr.clip_l?l:id_ptr.clip_l;
\r
334 id_ptr.clip_r=r>id_ptr.clip_r?r-1:id_ptr.clip_r;
\r
336 id_ptr.pos_x+=(len*(2*l+(len-1)))/2;
\r
337 id_ptr.pos_y+=y*len;
\r
340 while (index_prev < len_prev) {
\r
341 if (rle_current[i].l - rle_prev[index_prev].r > 0) {// 0なら8方位ラベリング
\r
342 // prevがcurの左方にある→prevはcurに連結していない。
\r
344 } else if (rle_prev[index_prev].l - rle_current[i].r > 0) {// 0なら8方位ラベリングになる
\r
345 // prevがcurの右方にある→prevはcurに連結していない。
\r
349 // prevとcurは連結している→ルートフラグメントの統合
\r
352 final int prev_id =rle_prev[index_prev].fid;
\r
353 NyARRleLabelFragmentInfo prev_ptr = f_array[prev_id];
\r
354 if (id != prev_id){
\r
356 //prevとcurrentのフラグメントidを書き換える。
\r
357 for(int i2=index_prev;i2<len_prev;i2++){
\r
359 if(rle_prev[i2].fid==prev_id){
\r
360 rle_prev[i2].fid=id;
\r
363 for(int i2=0;i2<i;i2++){
\r
364 //currentは0から現在-1まで
\r
365 if(rle_current[i2].fid==prev_id){
\r
366 rle_current[i2].fid=id;
\r
370 //現在のルートフラグメントに情報を集約
\r
371 id_ptr.area +=prev_ptr.area;
\r
372 id_ptr.pos_x+=prev_ptr.pos_x;
\r
373 id_ptr.pos_y+=prev_ptr.pos_y;
\r
375 if (id_ptr.clip_t > prev_ptr.clip_t) {
\r
377 id_ptr.clip_t = prev_ptr.clip_t;
\r
378 id_ptr.entry_x = prev_ptr.entry_x;
\r
379 }else if (id_ptr.clip_t < prev_ptr.clip_t) {
\r
380 // 現在の方が上にある。prevにフィードバック
\r
382 // 水平方向で小さい方がエントリポイント。
\r
383 if (id_ptr.entry_x > prev_ptr.entry_x) {
\r
384 id_ptr.entry_x = prev_ptr.entry_x;
\r
389 if (id_ptr.clip_l > prev_ptr.clip_l) {
\r
390 id_ptr.clip_l=prev_ptr.clip_l;
\r
394 if (id_ptr.clip_r < prev_ptr.clip_r) {
\r
395 id_ptr.clip_r=prev_ptr.clip_r;
\r
400 //結合済のルートフラグメントを無効化する。
\r
410 // curにidが割り当てられたかを確認
\r
413 if(addFragment(rle_current[i], id_max, y,rlestack)){
\r
420 RleElement[] tmp = rle_prev;
\r
421 rle_prev = rle_current;
\r
422 len_prev = len_current;
\r
426 final int max=this._max_area;
\r
427 final int min=this._min_area;
\r
428 for(int i=id_max-1;i>=0;i--){
\r
429 final NyARRleLabelFragmentInfo src_info=f_array[i];
\r
430 final int area=src_info.area;
\r
431 if(area<min || area>max){//対象外のエリア0のもminではじく
\r
435 src_info.clip_l+=i_left;
\r
436 src_info.clip_r+=i_left;
\r
437 src_info.entry_x+=i_left;
\r
438 src_info.pos_x/=area;
\r
439 src_info.pos_y/=area;
\r
441 this.onLabelFound(src_info);
\r
445 * この仮想関数は自己コールバック関数です。
\r
446 * {@link #labeling}関数が、検出したラベルを通知するために使います。
\r
447 * @param i_ref_label
\r
448 * 検出したラベルを格納したオブジェクト。値の有効期間は、次の{@link #labeling}が実行されるまでです。
\r
449 * (注)この仕様は変わるかもしれません。
\r
450 * @throws NyARException
\r
453 protected abstract void onLabelFound(NyARRleLabelFragmentInfo i_ref_label) throws NyARException;
\r
456 * クラスの仕様確認フラグです。ラベル配列の参照アクセスが可能かを返します。
\r
457 * このクラスでは、true固定です。
\r
459 public final static boolean _sf_label_array_safe_reference=true;
\r
463 * このクラスは、{@link NyARLabeling_Rle}が内部的に使うRLEスタックです。
\r
466 class RleInfoStack extends NyARObjectStack<NyARRleLabelFragmentInfo>
\r
468 public RleInfoStack(int i_length) throws NyARException
\r
471 super.initInstance(i_length, NyARRleLabelFragmentInfo.class);
\r
475 protected NyARRleLabelFragmentInfo createElement()
\r
477 return new NyARRleLabelFragmentInfo();
\r
481 * このクラスは、{@link #RleInfoStack}の要素です。
\r
482 * RLEフラグメントのパラメータを保持します。
\r
490 public static RleElement[] createArray(int i_length)
\r
492 RleElement[] ret = new RleElement[i_length];
\r
493 for (int i = 0; i < i_length; i++) {
\r
494 ret[i] = new RleElement();
\r