目次
前
ファームウエアへの展開
マイクロコンピュータあるいはFPGAを利用した、Machine Visionの
画像処理を説明した書籍やホームページがないかと探してみました。
OpenCVを利用したパーソナルコンピュータ上のアプリケーションの
解説は、星の数ほどありましたが、マイクロコンピュータやFPGAで
どう実現するかを記述したものは、皆無でした。
ないなら、自分で簡単な例を用意して、それを処理して
テストしてしまえばよいと考えを改めました。
HSPで簡単な画像処理を記述しました。
これらをマイクロコンピュータで実現する場合には
以下のハードルを越えなければなりません。
- 画像フォーマット解析
- 画像データ転送
- 画像メモリ用意
- メモリアクセス
- 浮動小数点計算回避
画像処理は、元画像をSRAMから取り出し、加工後、再度SRAM
に保存すると考えると、次のブロック図の操作になります。
ブロック図から、画像メモリへのアクセスと加工ができると
画像処理ができることがわかります。
カメラから画像メモリへの転送は、個々のデバイスで異なる
ので、画像メモリへのアクセスと加工に限定して説明します。
画像メモリアクセス
マイクロコンピュータの中に、潤沢なメモリ容量があれば
単純に配列への入出力で対応できます。
マイクロコンピュータにSRAMを外付けする場合にはGPIO(General
Purpose Inputs and Outputs)があれば、次の回路で対応します。
ここでは、H8/3664Fを想定していますが、14ビットの入出力が
あれば実現可能です。
上の回路で、以下の4つの関数を定義すれば、メモリアクセス
はおしまいです。
SRAMアドレス設定
typedef unsigned char UBYTE ;
typedef unsigned short UWORD ;
void set_sram_address(UWORD x)
{
UBYTE dummy ;
/* set address */
sram_address = x ;
/* set upper address */
PDAT5 = (UBYTE)(sram_address / 256) ;
TRGH = OFF ; dummy = 0 ; TRGH = ON ;
/* set lower address */
PDAT5 = (UBYTE)(sram_address % 256) ;
TRGL = OFF ; dummy = 0 ; TRGL = ON ;
}
アドレスインクリメント
void inc_sram_address(void)
{
UBYTE dummy ;
/* address increment */
sram_address++ ;
/* increment */
TRGCLK = OFF ; dummy = 0 ; TRGCLK = ON ;
}
1バイトデータリード
UBYTE get_sram_data(void)
{
UBYTE result ;
/* change input */
PDDR5 = 0x00 ;
/* enable CE */
SRAM_CS = OFF ;
/* enable OE */
SRAM_OE = OFF ;
/* get data */
result = PDAT5 ;
/* disable OE */
SRAM_OE = ON ;
/* disable CE */
SRAM_CS = ON ;
/* change output */
PDDR5 = 0xff ;
/* address increment */
inc_sram_address();
return result ;
}
1バイトデータライト
void put_sram_data(UBYTE x)
{
UBYTE dummy ;
/* impress data */
PDAT5 = x ;
/* enable CE */
SRAM_CS = OFF ;
/* enable WE */
SRAM_WE = OFF ;
/* dummy */
dummy = 0 ;
/* disable WE */
SRAM_WE = ON ;
/* disable CE */
SRAM_CS = ON ;
}
モノクロ変換
R、G、Bに区切って、画像メモリに保存されている場合は
単純に計算で求めてしまえばよいのですが、YUVのような
フォーマットの場合は、YUVからRGBに変換してしまえば
カラー→モノクロ変換できます。
---------------------------------------------
UBYTE tmp_r[XLAST] ;
UBYTE tmp_g[XLAST] ;
UBYTE tmp_b[XLAST] ;
UBYTE tmp ;
UWORD i_r,i_g,i_b,j ;
UWORD x,y ;
UWORD idx ;
i_r = src_red_adr ;
i_g = src_green_adr ;
i_b = src_blue_adr ;
j = dst_adr ;
for ( y = 0 ; y < YLAST ; y++ ) {
idx = y*XLAST ;
/* get red 1 line data */
set_sram_address(i_r+idx);
for ( x = 0 ; x < XLAST ; x++ ) {
*(tmp_r+x) = get_sram_data();
}
/* get green 1 line data */
set_sram_address(i_g+idx);
for ( x = 0 ; x < XLAST ; x++ ) {
*(tmp_g+x) = get_sram_data();
}
/* get blue 1 line data */
set_sram_address(i_b+idx);
for ( x = 0 ; x < XLAST ; x++ ) {
*(tmp_b+x) = get_sram_data();
}
/* set 1 line data */
set_sram_address(j+idx);
for ( x = 0 ; x < XLAST ; x++ ) {
/* calculate */
tmp = *(tmp_r+x) * 77 + *(tmp_g+x) * 151 + *(tmp_b+x) * 28 ;
tmp /= 256 ;
/* store */
put_sram_data( (UBYTE)tmp );
}
}
---------------------------------------------
2倍に拡大
2倍に拡大するには、元画像のラインを2回保存し
ピクセルも2回繰り返します。
---------------------------------------------
UBYTE tmp[XLAST] ;
UWORD i,j ;
UWORD x,y ;
UWORD idx ;
UWORD idy ;
i = src_adr ; j = dst_adr ;
for ( y = 0 ; y < YLAST ; y++ ) {
idx = y*XLAST ; idy = (idx << 1) ;
/* get 1 line data */
set_sram_address(i+idx);
for ( x = 0 ; x < XLAST ; x++ ) {
*(tmp+x) = get_sram_data();
}
/* set 2 line data */
set_sram_address(j+idy);
for ( x = 0 ; x < XLAST ; x += 2 ) {
put_sram_data( *(tmp+x) ); /* store left pixel */
put_sram_data( *(tmp+x) ); /* store right pixel */
}
for ( x = 0 ; x < XLAST ; x += 2 ) {
put_sram_data( *(tmp+x) ); /* store left pixel */
put_sram_data( *(tmp+x) ); /* store right pixel */
}
}
---------------------------------------------
1/2に縮小
1/2に縮小するには、元画像のラインを1飛びに取り出し
さらに、ピクセルも1飛びにして階調値を保存します。
---------------------------------------------
UBYTE tmp[XLAST] ;
UWORD i,j ;
UWORD x,y ;
UWORD idx ;
i = src_adr ; j = dst_adr ;
for ( y = 0 ; y < YLAST ; y++ ) {
idx = y*XLAST ;
/* judge */
if ( (y % 2) == 1 ) continue ;
/* if ( (y % 2) == 0 ) continue ; */
/* get 1 line data */
set_sram_address(i+idx);
for ( x = 0 ; x < XLAST ; x++ ) {
*(tmp+x) = get_sram_data();
}
/* set 1 line data */
set_sram_address(j+idx);
for ( x = 0 ; x < XLAST ; x += 2 ) {
put_sram_data( *(tmp+x) );
}
}
---------------------------------------------
間引き処理
間引きは、元画像のラインを1飛びに取り出して
目的のアドレスに保存すれば、実現できます。
---------------------------------------------
UBYTE tmp[XLAST] ;
UWORD i,j ;
UWORD x,y ;
UWORD idx ;
i = src_adr ; j = dst_adr ;
for ( y = 0 ; y < YLAST ; y++ ) {
idx = y*XLAST ;
/* judge */
if ( (y % 2) == 1 ) continue ;
/* if ( (y % 2) == 0 ) continue ; */
/* get 1 line data */
set_sram_address(i+idx);
for ( x = 0 ; x < XLAST ; x++ ) {
*(tmp+x) = get_sram_data();
}
/* set 1 line data */
set_sram_address(j+idx);
for ( x = 0 ; x < XLAST ; x++ ) {
put_sram_data( *(tmp+x) );
}
}
---------------------------------------------
横スクロール
横スクロールは、元画像のX方向の座標にシフト量を
加えて剰余を求めて、新しい座標値とします。
この座標値を使い、階調値を保存します。
---------------------------------------------
UBYTE tmp[XLAST] ;
UWORD i,j ;
UWORD x,y ;
UWORD idx ;
UWORD xx ;
i = src_adr ; j = dst_adr ;
for ( y = 0 ; y < YLAST ; y++ ) {
idx = y*XLAST ;
/* get 1 line data */
set_sram_address(i+idx);
for ( x = 0 ; x < XLAST ; x++ ) {
*(tmp+x) = get_sram_data();
}
/* set 1 line data */
set_sram_address(j+idx);
for ( x = 0 ; x < XLAST ; x++ ) {
xx = (x + shift) / XLAST ;
put_sram_data( *(tmp+xx) );
}
}
---------------------------------------------
縦スクロール
縦スクロールは、元画像のY方向の座標にシフト量を
加えて剰余を求めて、新しいライン位置とします。
このライン位置を使い、階調値を保存します。
---------------------------------------------
UBYTE tmp[XLAST] ;
UWORD i,j ;
UWORD x,y ;
UWORD idx ;
UWORD yy ;
i = src_adr ; j = dst_adr ;
for ( y = 0 ; y < YLAST ; y++ ) {
idx = y*XLAST ;
/* get 1 line data */
set_sram_address(i+idx);
for ( x = 0 ; x < XLAST ; x++ ) {
*(tmp+x) = get_sram_data();
}
/* set 1 line data */
yy = j + ((y+shift) % YLAST) * XLAST ;
set_sram_address(yy);
for ( x = 0 ; x < XLAST ; x++ ) {
put_sram_data( *(tmp+x) );
}
}
---------------------------------------------
90°回転
under construction
特定色抽出
under construction
エッジ抽出
エッジ抽出は、Laplacianフィルタを適用して実現します。
Laplacianフィルタは、8点の情報を必要とするので
ラインバッファを3つ用意して対応します。
---------------------------------------------
UBYTE gtmp[3*XLAST] ;
UWORD tmp ;
UWORD i,j ;
UWORD x,y ;
UWORD idx ;
UWORD x00,x01,x02 ;
UWORD x10,x11,x12 ;
UWORD x20,x21,x22 ;
j = src_adr ;
j = dst_adr ;
for ( y = 0 ; y < YLAST ; y++ ) {
idx = y*XLAST ;
/* get line data */
set_sram_address(i+idx);
for ( x = 0 ; x < XLAST ; x++ ) {
*(gtmp+x) = *(gtmp+x+XLAST);
*(gtmp+x+XLAST) = *(gtmp+x+2*XLAST);
*(gtmp+x+2*XLAST) = get_sram_data();
}
/* set 1 line data */
set_sram_address(j+idx);
for ( x = 0 ; x < XLAST ; x++ ) {
/* store */
put_sram_data( (UBYTE)(*(gtmp+x+2*XLAST)) );
}
set_sram_address(j+idx);
if ( 1 < y && y < YLAST-1) {
for ( x = 0 ; x < XLAST ; x++ ) {
/* judge */
if ( 0 < x && x < XLAST-1 ) {
/* calculate index */
x01 = x ; x00 = x01-1 ; x02 = x01+1 ;
x11 = x+XLAST ; x10 = x11-1 ; x12 = x11+1 ;
x21 = x+XLAST*2 ; x20 = x21-1 ; x21 = x21+1 ;
/* calculate */
tmp = *(gtmp+x00) + *(gtmp+x01) + *(gtmp+x02) ;
tmp += *(gtmp+x10) + *(gtmp+x11) + *(gtmp+x12) ;
tmp += *(gtmp+x20) + *(gtmp+x21) + *(gtmp+x22) ;
tmp -= (*(gtmp+x11) * 9) ;
put_sram_data( (UBYTE)tmp );
}
}
}
}
---------------------------------------------
左右反転
左右反転は、元画像のX方向の座標を小さい方と
大きい方を交換すれば実現できます。
---------------------------------------------
UBYTE tmp[XLAST] ;
UWORD i,j ;
UWORD x,y ;
UWORD idx ;
i = src_adr ; j = dst_adr ;
for ( y = 0 ; y < YLAST ; y++ ) {
idx = y*XLAST ;
/* get 1 line data */
set_sram_address(i+idx);
for ( x = 0 ; x < XLAST ; x++ ) {
*(tmp+x) = get_sram_data();
}
/* set 1 line data */
set_sram_address(j+idx);
for ( x = 0 ; x < XLAST ; x++ ) {
put_sram_data( *(tmp+XLAST-1-x) );
}
}
---------------------------------------------
目次
前