/* * PROJECT: NyARToolkit(Extension) * -------------------------------------------------------------------------------- * The NyARToolkit is Java edition ARToolKit class library. * Copyright (C)2008-2009 Ryo Iizuka * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * For further information please contact. * http://nyatla.jp/nyatoolkit/ * <airmail(at)ebony.plala.or.jp> or <nyatla(at)nyatla.jp> * */ package jp.nyatla.nyartoolkit.nyidmarker; import jp.nyatla.nyartoolkit.NyARException; import jp.nyatla.nyartoolkit.core.raster.rgb.*; import jp.nyatla.nyartoolkit.core.rasterreader.*; import jp.nyatla.nyartoolkit.core.types.*; import jp.nyatla.nyartoolkit.core.utils.*; /** * このクラスは、ラスタ画像に定義したの任意矩形から、NyIdパターンのデータを読み取ります。 * 読み取り結果は、{@link NyIdMarkerPattern}クラスのオブジェクトに出力します。 * */ public class NyIdMarkerPickup { private PerspectivePixelReader _perspective_reader; private final PerspectivePixelReader.TThreshold __pickFromRaster_th=new PerspectivePixelReader.TThreshold(); private final MarkerPattEncoder __pickFromRaster_encoder=new MarkerPattEncoder(); /** * コンストラクタです。インスタンスを生成します。 */ public NyIdMarkerPickup() { this._perspective_reader=new PerspectivePixelReader(); return; } /** * この関数は、ラスタからNyIdマーカの情報を読み出します。 * @param image * NyIdマーカが撮影されている(見込みのある)ラスタ * @param i_vertex * マーカの頂点位置を示す配列。4要素である事。 * @param o_data * 抽出したパターンデータを格納するオブジェクト。 * @param o_param * 抽出したパターンパラメータを格納するオブジェクト。 * @return * 抽出に成功するとtrue.失敗するとfalseです。 * @throws NyARException */ public final boolean pickFromRaster(INyARRgbRaster image, NyARDoublePoint2d[] i_vertex,NyIdMarkerPattern o_data,NyIdMarkerParam o_param)throws NyARException { //遠近法のパラメータを計算 if(!this._perspective_reader.setSourceSquare(i_vertex)){ return false; } return this._pickFromRaster(image,o_data,o_param); } /** * この関数は、ラスタからNyIdマーカの情報を読み出します。 * @param image * NyIdマーカが撮影されている(見込みのある)ラスタ * @param i_vertex * マーカの頂点位置を示す配列。4要素である事。 * @param o_data * 抽出したパターンデータを格納するオブジェクト。 * @param o_param * 抽出したパターンパラメータを格納するオブジェクト。 * @return * 抽出に成功するとtrue.失敗するとfalseです。 * @throws NyARException */ public final boolean pickFromRaster(INyARRgbRaster image, NyARIntPoint2d[] i_vertex,NyIdMarkerPattern o_data,NyIdMarkerParam o_param)throws NyARException { if(!this._perspective_reader.setSourceSquare(i_vertex)){ return false; } return this._pickFromRaster(image,o_data,o_param); } /** * i_imageから、idマーカを読みだします。 * o_dataにはマーカデータ、o_paramにはマーカのパラメータを返却します。 * @param image * @param i_vertex * @param o_data * @param o_param * @return * @throws NyARException */ private final boolean _pickFromRaster(INyARRgbRaster image, NyIdMarkerPattern o_data,NyIdMarkerParam o_param)throws NyARException { INyARRgbPixelReader reader=image.getRgbPixelReader(); NyARIntSize raster_size=image.getSize(); final PerspectivePixelReader.TThreshold th=this.__pickFromRaster_th; final MarkerPattEncoder encoder=this.__pickFromRaster_encoder; //マーカパラメータを取得 this._perspective_reader.detectThresholdValue(reader,raster_size,th); if(!this._perspective_reader.readDataBits(reader,raster_size,th, encoder)){ return false; } final int d=encoder.encode(o_data); if(d<0){ return false; } o_param.direction=d; o_param.threshold=th.th; return true; } } /** * {@link NyIdMarkerPickup}がラスタからPerspective変換して読みだすためのクラス * 画像処理全般を担当します。 * ユーザがこのクラスを使うことはありません。 * */ final class PerspectivePixelReader { private static int READ_RESOLUTION=100; private NyARPerspectiveParamGenerator _param_gen=new NyARPerspectiveParamGenerator_O1(1,1); private double[] _cparam=new double[8]; /** * コンストラクタです。 */ public PerspectivePixelReader() { return; } /** * この関数は、マーカ四角形をインスタンスにセットします。 * @param i_vertex * セットする四角形頂点座標。4要素である必要があります。 * @return * 成功するとtrueです。 * @throws NyARException */ public boolean setSourceSquare(NyARIntPoint2d[] i_vertex)throws NyARException { return this._param_gen.getParam(READ_RESOLUTION,READ_RESOLUTION,i_vertex, this._cparam); } /** * この関数は、マーカ四角形をインスタンスにセットします。 * @param i_vertex * セットする四角形頂点座標。4要素である必要があります。 * @return * 成功するとtrueです。 * @throws NyARException */ public boolean setSourceSquare(NyARDoublePoint2d[] i_vertex)throws NyARException { return this._param_gen.getParam(READ_RESOLUTION,READ_RESOLUTION,i_vertex, this._cparam); } /** * ラスタから射影変換したピクセルを得ます。 * @param i_lt_x * @param i_lt_y * @param i_step_x * @param i_step_y * @param i_width * @param i_height * @param i_out_st * 格納バッファo_pixelの先頭のインデクス。 * @param o_pixel * @throws NyARException */ private boolean rectPixels(INyARRgbPixelReader i_reader,NyARIntSize i_raster_size,int i_lt_x,int i_lt_y,int i_step_x,int i_step_y,int i_width,int i_height,int i_out_st,int[] o_pixel)throws NyARException { final double[] cpara=this._cparam; final int[] ref_x=this._ref_x; final int[] ref_y=this._ref_y; final int[] pixcel_temp=this._pixcel_temp; final int raster_width=i_raster_size.w; final int raster_height=i_raster_size.h; int out_index=i_out_st; final double cpara_6=cpara[6]; final double cpara_0=cpara[0]; final double cpara_3=cpara[3]; for(int i=0;i<i_height;i++){ //1列分のピクセルのインデックス値を計算する。 int cy0=1+i*i_step_y+i_lt_y; double cpy0_12=cpara[1]*cy0+cpara[2]; double cpy0_45=cpara[4]*cy0+cpara[5]; double cpy0_7=cpara[7]*cy0+1.0; int pt=0; for(int i2=0;i2<i_width;i2++) { final int cx0=1+i2*i_step_x+i_lt_x; final double d=cpara_6*cx0+cpy0_7; final int x=(int)((cpara_0*cx0+cpy0_12)/d); final int y=(int)((cpara_3*cx0+cpy0_45)/d); if(x<0||y<0||x>=raster_width||y>=raster_height) { return false; } ref_x[pt]=x; ref_y[pt]=y; pt++; } //1行分のピクセルを取得(場合によっては専用アクセサを書いた方がいい) i_reader.getPixelSet(ref_x,ref_y,i_width,pixcel_temp); //グレースケールにしながら、line→mapへの転写 for(int i2=0;i2<i_width;i2++){ int index=i2*3; o_pixel[out_index]=(pixcel_temp[index+0]+pixcel_temp[index+1]+pixcel_temp[index+2])/3; out_index++; } } return true; } /** * パターン分析関数。 * i_freqにあるゼロクロス点の周期が、等間隔か調べます。 * 次段半周期が、前段の80%より大きく、120%未満であるものを、等間隔周期であるとみなします。 * @param i_freq * @param i_width */ private static boolean checkFreqWidth(int[] i_freq,int i_width) { int c=i_freq[1]-i_freq[0]; final int count=i_width*2-1; for(int i=1;i<count;i++){ final int n=i_freq[i+1]-i_freq[i]; final int v=n*100/c; if(v>150 || v<50){ return false; } c=n; } return true; } /** * パターン分析関数。 * i_freq_count_tableとi_freq_tableの内容を調査し、最も大きな周波数成分を返します。 * @param i_freq_count_table * @param i_freq_table * @param o_freq_table * @return * 見つかれば0以上、密辛ければ0未満 */ private static int getMaxFreq(int[] i_freq_count_table,int[] i_freq_table,int[] o_freq_table) { //一番成分の大きいものを得る int index=-1; int max=0; for(int i=0;i<MAX_FREQ;i++){ if(max<i_freq_count_table[i]){ index=i; max=i_freq_count_table[i]; } } if(index==-1){ return -1; } /*周波数インデクスを計算*/ final int st=(index-1)*index; for(int i=0;i<index*2;i++) { o_freq_table[i]=i_freq_table[st+i]*FRQ_STEP/max; } return index; } //タイミングパターン用のパラメタ(FRQ_POINTS*FRQ_STEPが100を超えないようにすること) private static final int FRQ_EDGE=5; private static final int FRQ_STEP=2; private static final int FRQ_POINTS=(100-(FRQ_EDGE*2))/FRQ_STEP; private static final int MIN_FREQ=3; private static final int MAX_FREQ=10; private static final int FREQ_SAMPLE_NUM=4; private static final int MAX_DATA_BITS=MAX_FREQ+MAX_FREQ-1; private final int[] _ref_x=new int[108]; private final int[] _ref_y=new int[108]; //(model+1)*4*3とTHRESHOLD_PIXEL*3のどちらか大きい方 private int[] _pixcel_temp=new int[108*3]; private final int[] _freq_count_table=new int[MAX_FREQ]; private final int[] _freq_table=new int[(MAX_FREQ*2-1)*MAX_FREQ*2/2]; /** * この関数は、マーカ画像のi_y1行目とi_y2行目を平均して、タイミングパターンの周波数を得ます。 * L=暗点、H=明点、LHL=1周期として、たとえばLHLHLの場合は2を返します。LHLHやHLHL等の始端と終端のレベルが異なるパターンを * 検出した場合、関数は失敗します。 * @param i_y1 * ライン1のインデクス * @param i_th_h * 明点の敷居値 * @param i_th_l * 暗点の敷居値 * @param o_edge_index * 検出したエッジ位置(H->L,L->H)のインデクスを受け取る配列。 * [FRQ_POINTS]以上の配列を指定すること。 * @return * 周波数の値。失敗すると-1 * @throws NyARException */ public int getRowFrequency(INyARRgbPixelReader i_reader,NyARIntSize i_raster_size,int i_y1,int i_th_h,int i_th_l,int[] o_edge_index)throws NyARException { //3,4,5,6,7,8,9,10 final int[] freq_count_table=this._freq_count_table; //0,2,4,6,8,10,12,14,16,18,20の要素を持つ配列 final int[] freq_table=this._freq_table; //初期化 final double[] cpara=this._cparam; final int[] ref_x=this._ref_x; final int[] ref_y=this._ref_y; final int[] pixcel_temp=this._pixcel_temp; for(int i=0;i<10;i++){ freq_count_table[i]=0; } for(int i=0;i<110;i++){ freq_table[i]=0; } final int raster_width=i_raster_size.w; final int raster_height=i_raster_size.h; final double cpara_0=cpara[0]; final double cpara_3=cpara[3]; final double cpara_6=cpara[6]; //10-20ピクセル目からタイミングパターンを検出 for(int i=0;i<FREQ_SAMPLE_NUM;i++){ //2行分のピクセルインデックスを計算 final double cy0=1+i_y1+i; final double cpy0_12=cpara[1]*cy0+cpara[2]; final double cpy0_45=cpara[4]*cy0+cpara[5]; final double cpy0_7=cpara[7]*cy0+1.0; int pt=0; for(int i2=0;i2<FRQ_POINTS;i2++) { final double cx0=1+i2*FRQ_STEP+FRQ_EDGE; final double d=(cpara_6*cx0)+cpy0_7; final int x=(int)((cpara_0*cx0+cpy0_12)/d); final int y=(int)((cpara_3*cx0+cpy0_45)/d); if(x<0||y<0||x>=raster_width||y>=raster_height) { return -1; } ref_x[pt]=x; ref_y[pt]=y; pt++; } //ピクセルを取得(入力画像を多様化するならここから先を調整すること) i_reader.getPixelSet(ref_x,ref_y,FRQ_POINTS,pixcel_temp); //o_edge_indexを一時的に破壊して調査する final int freq_t=getFreqInfo(pixcel_temp,i_th_h,i_th_l,o_edge_index); //周期は3-10であること if(freq_t<MIN_FREQ || freq_t>MAX_FREQ){ continue; } //周期は等間隔であること if(!checkFreqWidth(o_edge_index,freq_t)){ continue; } //検出カウンタを追加 freq_count_table[freq_t]++; final int table_st=(freq_t-1)*freq_t; for(int i2=0;i2<freq_t*2;i2++){ freq_table[table_st+i2]+=o_edge_index[i2]; } } return getMaxFreq(freq_count_table,freq_table,o_edge_index); } /** * この関数は、マーカ画像のi_x1列目とi_x2列目を平均して、タイミングパターンの周波数を得ます。 * L=暗点、H=明点、LHL=1周期として、たとえばLHLHLの場合は2を返します。LHLHやHLHL等の始端と終端のレベルが異なるパターンを * 検出した場合、関数は失敗します。 * @param i_x1 * ライン1のインデクス * @param i_th_h * 明点の敷居値 * @param i_th_l * 暗点の敷居値 * @param o_edge_index * 検出したエッジ位置(H->L,L->H)のインデクスを受け取る配列。 * [FRQ_POINTS]以上の配列を指定すること。 * @return * 周波数の値。失敗すると-1 * @throws NyARException */ public int getColFrequency(INyARRgbPixelReader i_reader,NyARIntSize i_raster_size,int i_x1,int i_th_h,int i_th_l,int[] o_edge_index)throws NyARException { final double[] cpara=this._cparam; final int[] ref_x=this._ref_x; final int[] ref_y=this._ref_y; final int[] pixcel_temp=this._pixcel_temp; //0,2,4,6,8,10,12,14,16,18,20=(11*20)/2=110 //初期化 final int[] freq_count_table=this._freq_count_table; for(int i=0;i<10;i++){ freq_count_table[i]=0; } final int[] freq_table=this._freq_table; for(int i=0;i<110;i++){ freq_table[i]=0; } final int raster_width=i_raster_size.w; final int raster_height=i_raster_size.h; final double cpara7=cpara[7]; final double cpara4=cpara[4]; final double cpara1=cpara[1]; //基準点から4ピクセルを参照パターンとして抽出 for(int i=0;i<FREQ_SAMPLE_NUM;i++){ int cx0=1+i+i_x1; final double cp6_0=cpara[6]*cx0; final double cpx0_0=cpara[0]*cx0+cpara[2]; final double cpx3_0=cpara[3]*cx0+cpara[5]; int pt=0; for(int i2=0;i2<FRQ_POINTS;i2++) { int cy=1+i2*FRQ_STEP+FRQ_EDGE; final double d=cp6_0+cpara7*cy+1.0; final int x=(int)((cpx0_0+cpara1*cy)/d); final int y=(int)((cpx3_0+cpara4*cy)/d); if(x<0||y<0||x>=raster_width||y>=raster_height) { return -1; } ref_x[pt]=x; ref_y[pt]=y; pt++; } //ピクセルを取得(入力画像を多様化するならここを調整すること) i_reader.getPixelSet(ref_x,ref_y,FRQ_POINTS,pixcel_temp); final int freq_t=getFreqInfo(pixcel_temp,i_th_h,i_th_l,o_edge_index); //周期は3-10であること if(freq_t<MIN_FREQ || freq_t>MAX_FREQ){ continue; } //周期は等間隔であること if(!checkFreqWidth(o_edge_index,freq_t)){ continue; } //検出カウンタを追加 freq_count_table[freq_t]++; final int table_st=(freq_t-1)*freq_t; for(int i2=0;i2<freq_t*2;i2++){ freq_table[table_st+i2]+=o_edge_index[i2]; } } return getMaxFreq(freq_count_table,freq_table,o_edge_index); } /** * デバックすんだらstaticにしておk * @param i_pixcels * @param i_th_h * @param i_th_l * @param o_edge_index * @return */ private static int getFreqInfo(int[] i_pixcels,int i_th_h,int i_th_l,int[] o_edge_index) { //トークンを解析して、周波数を計算 int i=0; int frq_l2h=0; int frq_h2l=0; while(i<FRQ_POINTS){ //L->Hトークンを検出する while(i<FRQ_POINTS){ final int index=i*3; final int pix=(i_pixcels[index+0]+i_pixcels[index+1]+i_pixcels[index+2])/3; if(pix>i_th_h){ //トークン発見 o_edge_index[frq_l2h+frq_h2l]=i; frq_l2h++; break; } i++; } i++; //L->Hトークンを検出する while(i<FRQ_POINTS){ final int index=i*3; final int pix=(i_pixcels[index+0]+i_pixcels[index+1]+i_pixcels[index+2])/3; if(pix<=i_th_l){ //トークン発見 o_edge_index[frq_l2h+frq_h2l]=i; frq_h2l++; break; } i++; } i++; } return frq_l2h==frq_h2l?frq_l2h:-1; } private static final int THRESHOLD_EDGE=10; private static final int THRESHOLD_STEP=2; private static final int THRESHOLD_WIDTH=10; private static final int THRESHOLD_PIXEL=THRESHOLD_WIDTH/THRESHOLD_STEP; private static final int THRESHOLD_SAMPLE=THRESHOLD_PIXEL*THRESHOLD_PIXEL; private static final int THRESHOLD_SAMPLE_LT=THRESHOLD_EDGE; private static final int THRESHOLD_SAMPLE_RB=100-THRESHOLD_WIDTH-THRESHOLD_EDGE; /** * 敷居値の保持型 */ public static class TThreshold{ public int th_h; public int th_l; public int th; public int lt_x; public int lt_y; public int rb_x; public int rb_y; } /** * H/Lの保持型 */ class THighAndLow { public int h; public int l; } /** * ピクセル配列の上位、下位の4ピクセルのピクセル値平均を求めます。 * この関数は、(4/i_pixcel.length)の領域を占有するPtail法で双方向の閾値を求めることになります。 * @param i_pixcel * @param i_initial * @param i_out */ private void getPtailHighAndLow(int[] i_pixcel,THighAndLow i_out) { int h3,h2,h1,h0,l3,l2,l1,l0; h3=h2=h1=h0=l3=l2=l1=l0=i_pixcel[0]; for(int i=i_pixcel.length-1;i>=1;i--){ final int pix=i_pixcel[i]; if(h0<pix){ if(h1<pix){ if(h2<pix){ if(h3<pix){ h0=h1; h1=h2; h2=h3; h3=pix; }else{ h0=h1; h1=h2; h2=pix; } }else{ h0=h1; h1=pix; } }else{ h0=pix; } } if(l0>pix){ if(l1>pix){ if(l2>pix){ if(l3>pix){ l0=l1; l1=l2; l2=l3; l3=pix; }else{ l0=l1; l1=l2; l2=pix; } }else{ l0=l1; l1=pix; } }else{ l0=pix; } } } i_out.l=(l0+l1+l2+l3)/4; i_out.h=(h0+h1+h2+h3)/4; return; } private THighAndLow __detectThresholdValue_hl=new THighAndLow(); private NyARIntPoint2d __detectThresholdValue_tpt=new NyARIntPoint2d(); private int[] _th_pixels=new int[THRESHOLD_SAMPLE*4]; /** * この関数はマーカパターンから、敷居値を決定します。 * @param i_reader * ラスタリーダオブジェクト * @param i_raster_size * ラスのタのサイズ * @param o_threshold * 敷居値を受け取るオブジェクト * @throws NyARException */ public void detectThresholdValue(INyARRgbPixelReader i_reader,NyARIntSize i_raster_size,TThreshold o_threshold)throws NyARException { final int[] th_pixels=this._th_pixels; //左上のピックアップ領域からピクセルを得る(00-24) rectPixels(i_reader,i_raster_size,THRESHOLD_SAMPLE_LT,THRESHOLD_SAMPLE_LT,THRESHOLD_STEP,THRESHOLD_STEP,THRESHOLD_PIXEL,THRESHOLD_PIXEL,0,th_pixels); //左下のピックアップ領域からピクセルを得る(25-49) rectPixels(i_reader,i_raster_size,THRESHOLD_SAMPLE_LT,THRESHOLD_SAMPLE_RB,THRESHOLD_STEP,THRESHOLD_STEP,THRESHOLD_PIXEL,THRESHOLD_PIXEL,THRESHOLD_SAMPLE,th_pixels); //右上のピックアップ領域からピクセルを得る(50-74) rectPixels(i_reader,i_raster_size,THRESHOLD_SAMPLE_RB,THRESHOLD_SAMPLE_LT,THRESHOLD_STEP,THRESHOLD_STEP,THRESHOLD_PIXEL,THRESHOLD_PIXEL,THRESHOLD_SAMPLE*2,th_pixels); //右下のピックアップ領域からピクセルを得る(75-99) rectPixels(i_reader,i_raster_size,THRESHOLD_SAMPLE_RB,THRESHOLD_SAMPLE_RB,THRESHOLD_STEP,THRESHOLD_STEP,THRESHOLD_PIXEL,THRESHOLD_PIXEL,THRESHOLD_SAMPLE*3,th_pixels); final THighAndLow hl=this.__detectThresholdValue_hl; //Ptailで求めたピクセル平均 getPtailHighAndLow(th_pixels,hl); //閾値中心 int th=(hl.h+hl.l)/2; //ヒステリシス(差分の20%) int th_sub=(hl.h-hl.l)/5; o_threshold.th=th; o_threshold.th_h=th+th_sub;//ヒステリシス付き閾値 o_threshold.th_l=th-th_sub;//ヒステリシス付き閾値 //エッジを計算(明点重心) int lt_x,lt_y,lb_x,lb_y,rt_x,rt_y,rb_x,rb_y; final NyARIntPoint2d tpt=this.__detectThresholdValue_tpt; //LT if(getHighPixelCenter(0,th_pixels,THRESHOLD_PIXEL,THRESHOLD_PIXEL,th,tpt)){ lt_x=tpt.x*THRESHOLD_STEP; lt_y=tpt.y*THRESHOLD_STEP; }else{ lt_x=11; lt_y=11; } //LB if(getHighPixelCenter(THRESHOLD_SAMPLE*1,th_pixels,THRESHOLD_PIXEL,THRESHOLD_PIXEL,th,tpt)){ lb_x=tpt.x*THRESHOLD_STEP; lb_y=tpt.y*THRESHOLD_STEP; }else{ lb_x=11; lb_y=-1; } //RT if(getHighPixelCenter(THRESHOLD_SAMPLE*2,th_pixels,THRESHOLD_PIXEL,THRESHOLD_PIXEL,th,tpt)){ rt_x=tpt.x*THRESHOLD_STEP; rt_y=tpt.y*THRESHOLD_STEP; }else{ rt_x=-1; rt_y=11; } //RB if(getHighPixelCenter(THRESHOLD_SAMPLE*3,th_pixels,THRESHOLD_PIXEL,THRESHOLD_PIXEL,th,tpt)){ rb_x=tpt.x*THRESHOLD_STEP; rb_y=tpt.y*THRESHOLD_STEP; }else{ rb_x=-1; rb_y=-1; } //トラッキング開始位置の決定 o_threshold.lt_x=(lt_x+lb_x)/2+THRESHOLD_SAMPLE_LT-1; o_threshold.rb_x=(rt_x+rb_x)/2+THRESHOLD_SAMPLE_RB+1; o_threshold.lt_y=(lt_y+rt_y)/2+THRESHOLD_SAMPLE_LT-1; o_threshold.rb_y=(lb_y+rb_y)/2+THRESHOLD_SAMPLE_RB+1; return; } private boolean getHighPixelCenter(int i_st,int[] i_pixels,int i_width,int i_height,int i_th,NyARIntPoint2d o_point) { int rp=i_st; int pos_x=0; int pos_y=0; int number_of_pos=0; for(int i=0;i<i_height;i++){ for(int i2=0;i2<i_width;i2++){ if(i_pixels[rp++]>i_th){ pos_x+=i2; pos_y+=i; number_of_pos++; } } } if(number_of_pos>0){ pos_x/=number_of_pos; pos_y/=number_of_pos; }else{ return false; } o_point.x=pos_x; o_point.y=pos_y; return true; } private int[] __detectDataBitsIndex_freq_index1=new int[FRQ_POINTS]; private int[] __detectDataBitsIndex_freq_index2=new int[FRQ_POINTS]; private int detectDataBitsIndex(INyARRgbPixelReader i_reader,NyARIntSize i_raster_size,PerspectivePixelReader.TThreshold i_th,double[] o_index_row,double[] o_index_col) throws NyARException { //周波数を測定 final int[] freq_index1=this.__detectDataBitsIndex_freq_index1; final int[] freq_index2=this.__detectDataBitsIndex_freq_index2; int frq_t=getRowFrequency(i_reader,i_raster_size,i_th.lt_y,i_th.th_h,i_th.th_l,freq_index1); int frq_b=getRowFrequency(i_reader,i_raster_size,i_th.rb_y,i_th.th_h,i_th.th_l,freq_index2); //周波数はまとも? if((frq_t<0 && frq_b<0) || frq_t==frq_b){ return -1; } //タイミングパターンからインデクスを作成 int freq_h,freq_v; int[] index; if(frq_t>frq_b){ freq_h=frq_t; index=freq_index1; }else{ freq_h=frq_b; index=freq_index2; } for(int i=0;i<freq_h+freq_h-1;i++){ o_index_row[i*2]=((index[i+1]-index[i])*2/5+index[i])+FRQ_EDGE; o_index_row[i*2+1]=((index[i+1]-index[i])*3/5+index[i])+FRQ_EDGE; } final int frq_l=getColFrequency(i_reader,i_raster_size,i_th.lt_x,i_th.th_h,i_th.th_l,freq_index1); final int frq_r=getColFrequency(i_reader,i_raster_size,i_th.rb_x,i_th.th_h,i_th.th_l,freq_index2); //周波数はまとも? if((frq_l<0 && frq_r<0) || frq_l==frq_r){ return -1; } //タイミングパターンからインデクスを作成 if(frq_l>frq_r){ freq_v=frq_l; index=freq_index1; }else{ freq_v=frq_r; index=freq_index2; } //同じ周期? if(freq_v!=freq_h){ return -1; } for(int i=0;i<freq_v+freq_v-1;i++){ final int w=index[i]; final int w2=index[i+1]-w; o_index_col[i*2]=((w2)*2/5+w)+FRQ_EDGE; o_index_col[i*2+1]=((w2)*3/5+w)+FRQ_EDGE; } //Lv4以上は無理 if(freq_v>MAX_FREQ){ return -1; } return freq_v; } private double[] __readDataBits_index_bit_x=new double[MAX_DATA_BITS*2]; private double[] __readDataBits_index_bit_y=new double[MAX_DATA_BITS*2]; /** * この関数は、マーカパターンからデータを読み取ります。 * @param i_reader * ラスタリーダ * @param i_raster_size * ラスタのサイズ * @param i_th * 敷居値情報 * @param o_bitbuffer * データビットの出力先 * @return * 成功するとtrue * @throws NyARException */ public boolean readDataBits(INyARRgbPixelReader i_reader,NyARIntSize i_raster_size,PerspectivePixelReader.TThreshold i_th,MarkerPattEncoder o_bitbuffer)throws NyARException { final int raster_width=i_raster_size.w; final int raster_height=i_raster_size.h; final double[] index_x=this.__readDataBits_index_bit_x; final double[] index_y=this.__readDataBits_index_bit_y; //読み出し位置を取得 final int size=detectDataBitsIndex(i_reader,i_raster_size,i_th,index_x,index_y); final int resolution=size+size-1; if(size<0){ return false; } if(!o_bitbuffer.initEncoder(size-1)){ return false; } final double[] cpara=this._cparam; final int[] ref_x=this._ref_x; final int[] ref_y=this._ref_y; final int[] pixcel_temp=this._pixcel_temp; final double cpara_0=cpara[0]; final double cpara_1=cpara[1]; final double cpara_3=cpara[3]; final double cpara_6=cpara[6]; final int th=i_th.th; int p=0; for(int i=0;i<resolution;i++){ //1列分のピクセルのインデックス値を計算する。 double cy0=1+index_y[i*2+0]; double cy1=1+index_y[i*2+1]; double cpy0_12=cpara_1*cy0+cpara[2]; double cpy0_45=cpara[4]*cy0+cpara[5]; double cpy0_7=cpara[7]*cy0+1.0; double cpy1_12=cpara_1*cy1+cpara[2]; double cpy1_45=cpara[4]*cy1+cpara[5]; double cpy1_7=cpara[7]*cy1+1.0; int pt=0; for(int i2=0;i2<resolution;i2++) { int xx,yy; double d; double cx0=1+index_x[i2*2+0]; double cx1=1+index_x[i2*2+1]; double cp6_0=cpara_6*cx0; double cpx0_0=cpara_0*cx0; double cpx3_0=cpara_3*cx0; double cp6_1=cpara_6*cx1; double cpx0_1=cpara_0*cx1; double cpx3_1=cpara_3*cx1; d=cp6_0+cpy0_7; ref_x[pt]=xx=(int)((cpx0_0+cpy0_12)/d); ref_y[pt]=yy=(int)((cpx3_0+cpy0_45)/d); if(xx<0 || xx>=raster_width || yy<0 || yy>=raster_height) { ref_x[pt]=xx<0?0:(xx>=raster_width?raster_width-1:raster_width); ref_y[pt]=yy<0?0:(yy>=raster_height?raster_height-1:raster_height); } pt++; d=cp6_0+cpy1_7; ref_x[pt]=xx=(int)((cpx0_0+cpy1_12)/d); ref_y[pt]=yy=(int)((cpx3_0+cpy1_45)/d); if(xx<0 || xx>=raster_width || yy<0 || yy>=raster_height) { ref_x[pt]=xx<0?0:(xx>=raster_width?raster_width-1:raster_width); ref_y[pt]=yy<0?0:(yy>=raster_height?raster_height-1:raster_height); } pt++; d=cp6_1+cpy0_7; ref_x[pt]=xx=(int)((cpx0_1+cpy0_12)/d); ref_y[pt]=yy=(int)((cpx3_1+cpy0_45)/d); if(xx<0 || xx>=raster_width || yy<0 || yy>=raster_height) { ref_x[pt]=xx<0?0:(xx>=raster_width?raster_width-1:raster_width); ref_y[pt]=yy<0?0:(yy>=raster_height?raster_height-1:raster_height); } pt++; d=cp6_1+cpy1_7; ref_x[pt]=xx=(int)((cpx0_1+cpy1_12)/d); ref_y[pt]=yy=(int)((cpx3_1+cpy1_45)/d); if(xx<0 || xx>=raster_width || yy<0 || yy>=raster_height) { ref_x[pt]=xx<0?0:(xx>=raster_width?raster_width-1:raster_width); ref_y[pt]=yy<0?0:(yy>=raster_height?raster_height-1:raster_height); } pt++; } //1行分のピクセルを取得(場合によっては専用アクセサを書いた方がいい) i_reader.getPixelSet(ref_x,ref_y,resolution*4,pixcel_temp); //グレースケールにしながら、line→mapへの転写 for(int i2=0;i2<resolution;i2++){ int index=i2*3*4; int pixel=( pixcel_temp[index+0]+pixcel_temp[index+1]+pixcel_temp[index+2]+ pixcel_temp[index+3]+pixcel_temp[index+4]+pixcel_temp[index+5]+ pixcel_temp[index+6]+pixcel_temp[index+7]+pixcel_temp[index+8]+ pixcel_temp[index+9]+pixcel_temp[index+10]+pixcel_temp[index+11])/(4*3); //暗点を1、明点を0で表現します。 o_bitbuffer.setBitByBitIndex(p,pixel>th?0:1); p++; } } return true; } } class MarkerPattDecoder { public void decode(int model,int domain,int mask) { } } /** * このクラスは、マーカパターンのエンコーダです。{@link PerspectivePixelReader}から使います。 * ピクセルセットから、マーカーデータへエンコードする処理を定義します。 * 暫定として、5x5と、7x7のマーカについて定義します。 */ class MarkerPattEncoder { private static final int[] _bit_table_3={ 25, 26, 27, 28, 29, 30, 31, 48, 9, 10, 11, 12, 13, 32, 47, 24, 1, 2, 3, 14, 33, 46, 23, 8, 0, 4, 15, 34, 45, 22, 7, 6, 5, 16, 35, 44, 21, 20, 19, 18, 17, 36, 43, 42, 41, 40, 39, 38, 37 }; private static final int[] _bit_table_2={ 9, 10, 11, 12, 13, 24, 1, 2, 3, 14, 23, 8, 0, 4, 15, 22, 7, 6, 5, 16, 21, 20, 19, 18, 17}; private static final int[][] _bit_tables={ _bit_table_2,_bit_table_3,null,null,null,null,null, }; /** * RECT(0):[0]=(0) * RECT(1):[1]=(1-8) * RECT(2):[2]=(9-16),[3]=(17-24) * RECT(3):[4]=(25-32),[5]=(33-40),[6]=(41-48) */ private int[] _bit_table; private int[] _bits=new int[16]; private int[] _work=new int[16]; private int _model; /** * この関数は、 * @param i_index_no * @param i_value */ public void setBitByBitIndex(int i_index_no,int i_value) { assert i_value==0 || i_value==1; final int bit_no=this._bit_table[i_index_no]; if(bit_no==0){ this._bits[0]=i_value; }else{ int bidx=(bit_no-1)/8+1; int sidx=(bit_no-1)%8; this._bits[bidx]=(this._bits[bidx]&(~(0x01<<sidx)))|(i_value<<sidx); } return; } /** * この関数は、ビットイメージ{@link #_bits}のnビット目に、値をセットします。 * @param i_bit_no * ビットイメージのインデクス * @param i_value * セットする値。 */ public void setBit(int i_bit_no,int i_value) { assert i_value==0 || i_value==1; if(i_bit_no==0){ this._bits[0]=i_value; }else{ int bidx=(i_bit_no-1)/8+1; int sidx=(i_bit_no-1)%8; this._bits[bidx]=(this._bits[bidx]&(~(0x01<<sidx)))|(i_value<<sidx); } return; } /** * この関数は、ビットイメージ{@link #_bits}のnビット目から、値を得ます。 * @param i_bit_no * ビットイメージのインデクス * @return * ビット値 */ public int getBit(int i_bit_no) { if(i_bit_no==0){ return this._bits[0]; }else{ int bidx=(i_bit_no-1)/8+1; int sidx=(i_bit_no-1)%8; return (this._bits[bidx]>>(sidx))&(0x01); } } /** * この関数は、現在セットされているモデル番号を返します。 * @return * 現在のモデル番号 */ public int getModel() { return this._model; } private static int getControlValue(int i_model,int[] i_data) { int v; switch(i_model){ case 2: v=(i_data[2] & 0x0e)>>1; return v>=5?v-1:v; case 3: v=(i_data[4] & 0x3e)>>1; return v>=21?v-1:v; case 4: case 5: case 6: case 7: default: break; } return -1; } /** * この関数は、チェックドットの値を返します。 * @param i_model * モデル番号 * @param i_data * データ配列 * @return */ private static int getCheckValue(int i_model,int[] i_data) { int v; switch(i_model){ case 2: v=(i_data[2] & 0xe0)>>5; return v>5?v-1:v; case 3: v=((i_data[4] & 0x80)>>7) |((i_data[5] & 0x0f)<<1); return v>21?v-1:v; case 4: case 5: case 6: case 7: default: break; } return -1; } /** * この関数は、インスタンスの状態を、指定モデル向けにセットします。 * @param i_model * モデル番号 * @return * 成功するとtrueを返します。 */ public boolean initEncoder(int i_model) { if(i_model>3 || i_model<2){ //Lv4以降に対応する時は、この制限を変える。 return false; } this._bit_table=_bit_tables[i_model-2]; this._model=i_model; return true; } private int getDirection() { int l,t,r,b; int timing_pat; switch(this._model){ case 2: //トラッキングセルを得る t=this._bits[2] & 0x1f; r=((this._bits[2] & 0xf0)>>4)|((this._bits[3]&0x01)<<4); b=this._bits[3] & 0x1f; l=((this._bits[3] & 0xf0)>>4)|((this._bits[2]&0x01)<<4); timing_pat=0x0a; break; case 3: t=this._bits[4] & 0x7f; r=((this._bits[4] & 0xc0)>>6)|((this._bits[5] & 0x1f)<<2); b=((this._bits[5] & 0xf0)>>4)|((this._bits[6] & 0x07)<<4); l=((this._bits[6] & 0xfc)>>2)|((this._bits[4] & 0x01)<<6); timing_pat=0x2a; break; default: return -3; } //タイミングパターンの比較 if(t==timing_pat){ if(r==timing_pat){ return (b!=timing_pat && l!=timing_pat)?2:-2; }else if(l==timing_pat){ return (b!=timing_pat && r!=timing_pat)?3:-2; } }else if(b==timing_pat){ if(r==timing_pat){ return (t!=timing_pat && l!=timing_pat)?1:-2; }else if(l==timing_pat){ return (t!=timing_pat && r!=timing_pat)?0:-2; } } return -1; } /** * 格納しているマーカパターンをエンコードして、マーカデータを返します。 * @param o_out * マーカデータを受け取るオブジェクト。 * @return * 成功すればマーカの方位を返却します。失敗すると-1を返します。 */ public int encode(NyIdMarkerPattern o_out) { final int d=getDirection(); if(d<0){ return -1; } //回転ビットの取得 getRotatedBits(d,o_out.data); final int model=this._model; //周辺ビットの取得 o_out.model=model; int control_bits=getControlValue(model,o_out.data); o_out.check=getCheckValue(model,o_out.data); o_out.ctrl_mask=control_bits%5; o_out.ctrl_domain=control_bits/5; if(o_out.ctrl_domain!=0 || o_out.ctrl_mask!=0){ return -1; } //マスク解除処理を実装すること return d; } private void getRotatedBits(int i_direction,int[] o_out) { int sl=i_direction*2; int sr=8-sl; int w1; o_out[0]=this._bits[0]; //RECT1 w1=this._bits[1]; o_out[1]=((w1<<sl)|(w1>>sr))& 0xff; //RECT2 sl=i_direction*4; sr=16-sl; w1=this._bits[2]|(this._bits[3]<<8); w1=(w1<<sl)|(w1>>sr); o_out[2]=w1 & 0xff; o_out[3]=(w1>>8) & 0xff; if(this._model<2){ return; } //RECT3 sl=i_direction*6; sr=24-sl; w1=this._bits[4]|(this._bits[5]<<8)|(this._bits[6]<<16); w1=(w1<<sl)|(w1>>sr); o_out[4]=w1 & 0xff; o_out[5]=(w1>>8) & 0xff; o_out[6]=(w1>>16) & 0xff; if(this._model<3){ return; } //RECT4(Lv4以降はここの制限を変える) // shiftLeft(this._bits,7,3,i_direction*8); // if(this._model<4){ // return; // } return; } /** * この関数は、ビットパックi_packを指定ビットだけ左シフトします。 * 現在未使用です。 * @param i_pack * 操作する配列 * @param i_start * シフトの開始アドレス * @param i_length * シフトする配列の長さ * @param i_ls * シフト量 */ public void shiftLeft(int[] i_pack,int i_start,int i_length,int i_ls) { int[] work=this._work; //端数シフト final int mod_shift=i_ls%8; for(int i=i_length-1;i>=1;i--){ work[i]=(i_pack[i+i_start]<<mod_shift)|(0xff&(i_pack[i+i_start-1]>>(8-mod_shift))); } work[0]=(i_pack[i_start]<<mod_shift)|(0xff&(i_pack[i_start+i_length-1]>>(8-mod_shift))); //バイトシフト final int byte_shift=(i_ls/8)%i_length; for(int i=i_length-1;i>=0;i--){ i_pack[(byte_shift+i)%i_length+i_start]=0xff & work[i]; } return; } }