目次

センサー処理

 今回利用するセンサーは、GameBoyCameraです。

 GameBoyCamera(GBC)は、人工網膜チップを載せた
  128ピクセルx128ラインのモノクロ画像を出力します。

 1個のGBCから得られる画像を次のように処理します。
  1. 1ライン(128ピクセル)を、ある閾値で2値化します。
  2. 128ライン中、画像の下側の64ライン〜127ラインを利用します。
  3. 2で求めた64ラインのデータを圧縮します。
 1、2はH8の内蔵SRAMが4kバイトしかないので、扱うデータ量  を減らす工夫です。  1ライン(128ピクセル)を、2値化すると128ビットになります。  128ビットは、16バイトに相当するので、64ラインで1024バイトになります。  GBCの画像128ラインのうち、最終5ラインはゴミデータになっているため  有効なのは59ライン分となります。  1ラインは、128バイトなので、2値化して処理を単純にします。  2値化するので、対象となるデータは128ビットになります。

1ライン処理

 1ラインの処理について説明します。  2値化した1ラインは、128ビットになります。  黒から白、白から黒になる位置を見つけ出し、  センターラインの位置を確定します。  1ビットごとに、白、黒の判定をすると効率が  悪いので、128ビットを8ビットごとにまとめて  16個の区間にします。  128回の判定をするよりも、16回の判定の方が  少なくなるので、処理時間が短くなります。  いきなり処理を記述するのは、バグを生む原因になるので、  一つ一つを確認しながら、動作を考えていきます。  1ラインデータ生成   128バイトの配列を定義して、計算で画像データを作成します。 typedef unsigned char UBYTE ; #define GBSIZE 128 UBYTE gdata[GBSIZE] ; for ( i = 0 ; i < GBSIZE ; i++ ) { *(gdata+i) = 2*i + 9 ; } for ( i = 64 ; i < GBSIZE ; i++ ) { *(gdata+i) = 255 - 2*i ; }   どんなデータが出来ているかを表示します。 for ( i = 0 ; i < GBSIZE ; i++ ) { printf("%3d ",*(gdata+i)) ; if ( (i % 8) == 7 ) putchar('\n'); }   データは、以下となりました。 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97 99 101 103 105 107 109 111 113 115 117 119 121 123 125 127 129 131 133 135 127 125 123 121 119 117 115 113 111 109 107 105 103 101 99 97 95 93 91 89 87 85 83 81 79 77 75 73 71 69 67 65 63 61 59 57 55 53 51 49 47 45 43 41 39 37 35 33 31 29 27 25 23 21 19 17 15 13 11 9 7 5 3 1  2値化 2値化した値を格納しておく配列を用意します。 UBYTE sdata[BBSIZE] ; 閾値を85として2値化し、8ビットに仕立てます。 res = 0 ; threshold = 85 ; j = 0 ; for ( i = 0 ; i < GBSIZE ; i++ ) { /* shift */ res <<= 1 ; /* judge */ if ( *(gdata+i) > threshold ) { res |= 1 ; } /* store */ if ( (i % 8) == 7 ) { *(sdata+j) = res ; res = 0 ; j++ ; } }   どんなデータが出来ているかを表示します。 for ( i = 0 ; i < BBSIZE ; i++ ) { binary_display( *(sdata+i) ) ; putchar('\n'); }   binary_displayは、次のように定義しました。 void binary_display(UBYTE x) { int i ; for ( i = 7 ; i > -1 ; i-- ) { putchar('0'+((x >> i) & 1)); } }   2値化すると、データは、以下となります。 00000000 00000000 00000000 00000000 00000001 11111111 11111111 11111111 11111111 11111111 11111000 00000000 00000000 00000000 00000000 00000000  8ビットの状態判定   配列sdataに格納されている、8ビットの状態を判定する   関数を定義します。   多くの状態を用意しても、複雑な判定になってしまうので   今回は、次の4つの状態に限定しました。    すべて黒    すべて白    黒から白に変化    白から黒に変化   符号なし8ビット(1バイト)を入力し、4つの状態を判定   する関数を定義します。すべて黒、すべて白は0x00、0xffで   判定できるので、黒から白、白から黒という変化を判定する   関数を定義します。 #define OFF 0 #define ON OFF+1    黒から白に変化 UBYTE is_black_white(UBYTE x) { UBYTE result ; /* default */ result = OFF ; /* judge */ if ( x == 0x7f ) { result = ON ; } /* 0111 1111 */ if ( x == 0x3f ) { result = ON ; } /* 0011 1111 */ if ( x == 0x1f ) { result = ON ; } /* 0001 1111 */ if ( x == 0x0f ) { result = ON ; } /* 0000 1111 */ if ( x == 0x07 ) { result = ON ; } /* 0000 0111 */ if ( x == 0x03 ) { result = ON ; } /* 0000 0011 */ if ( x == 0x01 ) { result = ON ; } /* 0000 0001 */ return result ; }    白から黒に変化 UBYTE is_white_black(UBYTE x) { UBYTE result ; /* default */ result = OFF ; /* judge */ if ( x == 0x80 ) { result = ON ; } /* 1000 0000 */ if ( x == 0xc0 ) { result = ON ; } /* 1100 0000 */ if ( x == 0xe0 ) { result = ON ; } /* 1110 0000 */ if ( x == 0xf0 ) { result = ON ; } /* 1111 0000 */ if ( x == 0xf8 ) { result = ON ; } /* 1111 1000 */ if ( x == 0xfc ) { result = ON ; } /* 1111 1100 */ if ( x == 0xfe ) { result = ON ; } /* 1111 1110 */ return result ; }   4つの状態を、フラグで記憶しておく配列を用意します。 UBYTE blackbyte[BBSIZE] ; /* すべて黒 */ UBYTE whitebyte[BBSIZE] ; /* すべて白 */ UBYTE bwbyte[BBSIZE] ; /* 黒から白 */ UBYTE wbbyte[BBSIZE] ; /* 白から黒 */ UBYTE edge[BBSIZE] ; /* 変化あり */   「変化あり」は、黒から白、白から黒の他に、すべて黒から   すべて白、すべて白からすべて黒の2つの状態をも含めて   記憶するために使います。   ループ処理で、16個の8ビットデータが、どういう状態に   あるのかを判定し、記憶します。 for ( i = 0 ; i < BBSIZE ; i++ ) { /* defualt */ *(blackbyte+i) = OFF ; *(whitebyte+i) = OFF ; *(bwbyte+i) = OFF ; *(wbbyte+i) = OFF ; *(edge+i) = OFF ; /* judge */ if ( *(sdata+i) == 0x00 ) { *(blackbyte+i) = ON ; } if ( *(sdata+i) == 0xff ) { *(whitebyte+i) = ON ; } *(bwbyte+i) = is_black_white( *(sdata+i) ) ; *(wbbyte+i) = is_white_black( *(sdata+i) ) ; if ( *(bwbyte+i) ) { *(edge+i) = ON ; } if ( *(wbbyte+i) ) { *(edge+i) = ON ; } }   隣合う8ビットが、すべて黒からすべて白となる場合と   その逆の判定が含まれていないので、付加します。 for ( i = 0 ; i < BBSIZE-1 ; i++ ) { if ( *(bwbyte+i) & *(wbbyte+i+1) ) { *(edge+i+1) = ON ; } if ( *(wbbyte+i) & *(bwbyte+i+1) ) { *(edge+i) = ON ; } }   各8ビットの判定が正しいかを表示してみます。 for ( i = 0 ; i < BBSIZE ; i++ ) { printf("Sector(%2d) ",i); binary_display( *(sdata+i) ) ; if ( *(blackbyte+i) ) { puts(" all black") ; } if ( *(whitebyte+i) ) { puts(" all white") ; } if ( *(edge+i) ) { puts(" edge") ; } }   結果は、次のようになりました。 Sector( 0) 00000000 all black Sector( 1) 00000000 all black Sector( 2) 00000000 all black Sector( 3) 00000000 all black Sector( 4) 00000001 edge Sector( 5) 11111111 all white Sector( 6) 11111111 all white Sector( 7) 11111111 all white Sector( 8) 11111111 all white Sector( 9) 11111111 all white Sector(10) 11111000 edge Sector(11) 00000000 all black Sector(12) 00000000 all black Sector(13) 00000000 all black Sector(14) 00000000 all black Sector(15) 00000000 all black  ピクセル位置計算   「変化あり」と判定されたデータの添字から、ピクセル位置を   求められます。   添字から、ピクセル数を求める処理を、関数calc_locationに   担当させます。 UBYTE calc_location(UBYTE x) { return( x << 3 ); }   8倍を、シフトで実現して計算処理にかかる時間を短くします。   8ビットの中の1の位置がどうなっているかを関数get_bit_location   に判定させます。 UBYTE get_bit_location(UBYTE x) { UBYTE result ; /* default */ result = 9 ; /* judge */ if ( x == 0x00 ) { result = 0 ; } /* 0000 0000 */ if ( x == 0xff ) { result = 0 ; } /* 1111 1111 */ if ( x == 0x7f ) { result = 1 ; } /* 0111 1111 */ if ( x == 0x3f ) { result = 2 ; } /* 0011 1111 */ if ( x == 0x1f ) { result = 3 ; } /* 0001 1111 */ if ( x == 0x0f ) { result = 4 ; } /* 0000 1111 */ if ( x == 0x07 ) { result = 5 ; } /* 0000 0111 */ if ( x == 0x03 ) { result = 6 ; } /* 0000 0011 */ if ( x == 0x01 ) { result = 7 ; } /* 0000 0001 */ if ( x == 0x80 ) { result = 1 ; } /* 1000 0000 */ if ( x == 0xc0 ) { result = 2 ; } /* 1100 0000 */ if ( x == 0xe0 ) { result = 3 ; } /* 1110 0000 */ if ( x == 0xf0 ) { result = 4 ; } /* 1111 0000 */ if ( x == 0xf8 ) { result = 5 ; } /* 1111 1000 */ if ( x == 0xfc ) { result = 6 ; } /* 1111 1100 */ if ( x == 0xfe ) { result = 7 ; } /* 1111 1110 */ return result ; }   処理を単純にする関数を用意したら、「変化あり」を探します。   (ピクセル位置は、スタックに保存します。) clear_stack(); /* judge show sector data */ for ( i = 0 ; i < BBSIZE ; i++ ) { binary_display( *(sdata+i) ) ; putchar(' '); if ( *(edge+i) ) { base = calc_location(i) ; offset = get_bit_location( *(sdata+i) ); printf("%d",base+offset); push( base+offset ); } putchar('\n'); }   確認表示してみます。 00000000 00000000 00000000 00000000 00000001 39 11111111 11111111 11111111 11111111 11111111 11111000 85 00000000 00000000 00000000 00000000 00000000  ピクセル位置取得   スタックに保存してある、ピクセル位置を取り出し表示します。 j = get_stack_size(); for ( i = 0 ; i < j ; i++ ) { printf("location %3d \n",reverse_pop()) ; }   表示は、次のようになりました。 location 39 location 85   確かに、白の始まり位置と終わり位置になっています。  スタック処理   ピクセル位置は、いくつあるのか見当がつかないので   スタックを利用して、発見したならば、記憶しておく   仕様にしました。   スタックは、8ビットのデータ個数と同じだけの配列を   用意して確保します。 UBYTE stack[BBSIZE] ;   スタックに保存されているデータ数、スタック内のデータ   位置を格納しておく、変数を定義します。 UBYTE stack_capacity ; UBYTE stack_index ; UBYTE stack_rindex ;   スタックに、データを保存するため、pushを利用します。 void push(UBYTE x) { /* store */ *(stack+stack_index) = x ; /* increment */ stack_index++ ; /* increment */ stack_capacity++ ; }   スタックから、データを取り出すため、popを利用します。 UBYTE pop(void) { UBYTE result ; /* judge */ if ( stack_capacity == 0 ) { return 0 ; } /* decrement */ stack_index-- ; /* decrement */ stack_capacity-- ; /* get */ result = *(stack+stack_index) ; return result ; }   スタックをFIFOのように使いたい場合もあるので、そのような   ときに、データを取り出すため、reverse_popを定義します。 UBYTE reverse_pop(void) { UBYTE result ; /* judge */ if ( stack_rindex == stack_index ) { return 0 ; } /* get */ result = *(stack+stack_rindex) ; /* increment */ stack_rindex++ ; return result ; }   スタックに格納されているデータ数を知りたいとき使う関数   get_stack_sizeを定義します。 UBYTE get_stack_size(void) { return stack_capacity ; }   スタックに関連する初期化に、関数clear_stackを定義します。 void clear_stack(void) { stack_index = 0 ; stack_capacity = 0 ; stack_rindex = 0 ; }  ここまで定義した関数を利用して、GBCが出力してくる画像  データから、センターラインを見つけます。
目次

inserted by FC2 system