窓切り出し回路
前項の「窓切り出し回路」をCコードで書いてみる。1フレーム分の画像をシリアル入力して、それに対応する窓をシリアル出力し終わるまでの動作を書くから、トップ階層は非パイプライン型になる。
◇
窓の右下隅が画像の入力位置だから、入力画像の先頭が窓の右下隅に来る所から、入力画像の最後が窓の中心に来る所まで、窓を移動させればよい。今、窓の右下隅の位置を(v,h)とおくと次のようになる。
(0) 窓の移動範囲は、入力画像(v=0 to V-1,h=0 to H-1)よりも窓の半分幅(W-1)/2だけ広い
(1) このうち画像が入力されるのは、窓の右下隅が入力画像にかかっている期間(v<V,h<H)である
(2) 窓の中心が入力画像にかからない期間は画像を出力しない
(3) 窓が境界領域(不定値)を含んでいる期間は出力画像も不定値になるので、識別するためborder信号を出す
タイミングチャートで表すと、次のように水平方向・垂直方向ともに入力から窓の半分幅だけ遅れて出力されることになる。
◇◇
上記の動作をCコードで書くと次のようになる。ここで、画像サイズ(vsize,hsize)を可変にしたかったので、入出力(din,dout)はstreamクラス、ラインバッファ(line)は可変長のap_shift_regクラスを使った。
/* win.h*/ #ifndef WIN_H #define WIN_H #include <ap_int.h> #include <hls_stream.h>
static const int HSIZE_MAX=640; // max 水平長(1ラインの画素数) static const int VSIZE_MAX=512; // max 垂直長(1フレームのライン数) static const int WSIZE=3; // 窓サイズ(奇数であること)
typedef ap_uint<8> img_t; // 画像データ型
typedef img_t win_din_t; // 入力データ
typedef struct { img_t w[WSIZE][WSIZE]; // 窓[垂直方向][水平方向] ap_uint<10> h; // 窓中心の水平座標(0 to HSIZE_MAX-1) ap_uint<9> v; // 窓中心の垂直座標(0 to VSIZE_MAX-1) bool border; // 窓が境界領域を含む } win_dout_t; // 出力データ
typedef struct { ap_uint<10> hsize; // 水平長(0 to HSIZE_MAX) ap_uint<10> vsize; // 垂直長(0 to VSIZE_MAX) } win_param_t; // パラメータ
void win_hls (hls::stream<win_din_t> &din // (i) 入力データ ,hls::stream<win_dout_t> &dout // (o) 出力データ(窓と中心座標) ,win_param_t param // (i) パラメータ(水平長と垂直長) ); #endif |
/* win.cpp */ #include "win.h" #include <ap_shift_reg.h> void win_hls (hls::stream<win_din_t> &din // (i) 入力データ ,hls::stream<win_dout_t> &dout // (o) 出力データ(窓と中心座標) ,win_param_t param // (i) パラメータ(水平長と垂直長) ) { #pragma HLS interface ap_ctrl_none port=return #pragma HLS interface ap_fifo port=din #pragma HLS interface ap_fifo port=dout #pragma HLS interface ap_none port=param #pragma HLS aggregate variable=dout compact=bit #pragma HLS aggregate variable=param compact=bit ap_shift_reg<win_din_t,HSIZE_MAX> line[WSIZE-1]; // ラインバッファ win_din_t line_out[WSIZE]; win_din_t line_out_tmp; win_din_t w[WSIZE][WSIZE]; win_din_t din_tmp; win_dout_t dout_tmp; for(int v=0; v<param.vsize+(WSIZE-1)/2; v++) { for(int h=0; h<param.hsize+(WSIZE-1)/2; h++) { #pragma HLS pipeline II=1 /* (1)入力 */ if(v<param.vsize && h<param.hsize) { din_tmp = din.read(); } /* (2)ラインバッファのシフト */ if(h<param.hsize) { for(int i=WSIZE-1; i>=0; i--) { line_out[i] = (i==WSIZE-1)? din_tmp : line[i].shift(line_out[i+1],param.hsize-1); } } /* (3)窓のシフト */ for(int i=0; i<WSIZE; i++) { for(int j=0; j<WSIZE; j++) { w[i][j] = (j==WSIZE-1)? line_out[i] : w[i][j+1]; } } /* (4)出力 */ if((WSIZE-1)/2<=v && (WSIZE-1)/2<=h) { dout_tmp.v = v - (WSIZE-1)/2; // 窓中心の垂直座標 dout_tmp.h = h - (WSIZE-1)/2; // 窓中心の水平座標 dout_tmp.border = (v<(WSIZE-1) || param.vsize<=v || h<(WSIZE-1) || param.hsize<=h)? 1:0; // 境界領域 for(int i=0; i<WSIZE; i++) { for(int j=0; j<WSIZE; j++) { dout_tmp.w[i][j] = w[i][j]; // 窓 } } dout.write(dout_tmp); } } } } |
◇◇
このCコードは次のテストベンチで検証してある。
/* main.cpp */ #include "win.h" #define DEBUG 1 /***************************** * サブルーチン - 窓の表示 *****************************/ void print_win(win_dout_t dout) { printf("- v=%d h=%d border=%d\n" ,dout.v.to_uint(),dout.h.to_uint(),dout.border); for(int i=WSIZE-1; i>=0; i--) { printf(" i=%d",i); for(int j=WSIZE-1; j>=0; j--) printf("%4d",dout.w[i][j].to_uint()); pringf("\n"); } } /****************************************************** * サブルーチン - 入力画像としてインクリメント値を返す ******************************************************/ int din_val(int v,int h) { return (10*v+h+1); } /****************** * メインルーチン ******************/ int main(int argc,char *argv[]) { hls::stream<win_din_t> din_stream; hls::stream<win_dout_t> dout_stream; win_param_t param; win_dout_t dout; win_din_t w_expected; #if DEBUG int vsize = WSIZE+1; int hsize = WSIZE+3; #else int vsize = VSIZE_MAX; int hsize = HSIZE_MAX; #endif /* (1) 入力画像に値を設定する */ for(int v=0; v<vsize; v++) { for(int h=0; h<hsize; h++) { din_stream << din_val(v,h); } } /* (2) DUTを実行する */ param.vsize = vsize; param.hsize = hsize; win_hls(din_stream,dout_stream,param); /* (3) 窓出力を期待値と比較する */ printf("-- vsize=%d hsize=%d\n",vsize,hsize); int h,v; while(!dout_stream.empty()) { dout_stream >> dout; #if DEBUG print_win(dout); #endif for(int i=0; i<WSIZE; i++) { for(int j=0; j<WSIZE; j++) { v = dout.v + i - (WSIZE-1)/2; h = dout.h + j - (WSIZE-1)/2; w_expected = din_val(v,h); if(h<0 || hsize<=h) w_expected = dout.w[i][j];// 境界領域は比較しない if(v<0 || vsize<=v) w_expected = dout.w[i][j];// 境界領域は比較しない if(dout.w[i][j]!=w_expected) { printf("- v=%d h=%d border=%d\n" ,dout.v.to_uint(),dout.h.to_uint(),dout.border); printf(" w[%d][%d]=%d",i,j,dout.w[i][j].to_uint()); printf(" w_expected=%d [FAIL]\n",w_expected.to_uinit()); } } } } return 0; } |