FC2カウンター FPGAの部屋 Vitis HLS
fc2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

Vitis HLS 2022.1 で KR260 用の multi_axi4ls IP を作成する

”RPi+PMOD Connector GPIO with Custom PL Design in Kria KR260”をやってみる1”の axi gpio を 4 こ追加した kr260_bd ブロック・デザインに追加するために”Vitis HLS 2022.1 で multi_axi4ls IP を作成する”の multi_axi4ls IP を KR260 用に作り直した。

ソースコードとテストベンチ・コードは”Vitis HLS 2022.1 で multi_axi4ls IP を作成する”のコードを使用した。

VItis HLS 2022.1 で multi_axi4ls プロジェクトを作成する。
New Vitis HLS Project ダイアログの Project Configuration 画面を示す。
KR260_282_230301.png

パートを選ぶ Device Selection Dialog を示す。
パートは Vivado のログを見ると xck26-sfvc784-2LV-c だった。
KR260_283_230301.png

multi_axi4ls プロジェクトを示す。
KR260_284_230301.png

C シミュレーションを行った。結果を示す。
KR260_285_230301.png

C コードの合成を行った。結果を示す。
KR260_286_230301.png
KR260_287_230301.png

C/RTL 協調シミュレーションを行った。結果を示す。
KR260_288_230301.png

C/RTL 協調シミュレーションの波形を示す。
KR260_289_230301.png

Export RTL を行って IP を生成した。

Implementation を行った。結果を示す。
KR260_290_230301.png
  1. 2023年03月01日 04:23 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Xilinx 社画像用 IP の AXI4-Stream のスタート位置を検出する IP のソースコードを 2 重ループから 1 重ループに変更した

”Xilinx 社画像用 IP の AXI4-Stream のスタート位置を検出する IP を Vitis HLS 2022.2 で作成する 1
Xilinx 社画像用 IP の AXI4-Stream のスタート位置を検出する IP を Vitis HLS 2022.2 で作成する 2
上の 2 つの記事で C コードの合成時の Estimated の成績があまり良くないので、もっとチューニングできる方法を考えていた。
考えた結果、for 文が 2 重ループになっているので、これを 1 重ループにしたら比較が減って高速にならないだろうか?という考えに至ったので、確かめてみよう。

find_startp.cpp を 2 重ループから 1 重ループに書き換えた。

// find_startp.cpp
// 2022/12/31 by marsee
// 2023/01/03 : 2重ループを1重ループに変更した
//

#include <stdio.h>
#include <stdint.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

int find_startp(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs,
        uint32_t row_size, uint32_t col_size){
#pragma HLS INTERFACE mode=s_axilite port=col_size
#pragma HLS INTERFACE mode=s_axilite port=row_size
#pragma HLS INTERFACE mode=axis register_mode=both port=ins register
#pragma HLS INTERFACE mode=axis register_mode=both port=outs register
#pragma HLS INTERFACE mode=s_axilite port=return

    ap_axis<32,1,1,1> pix;
    uint32_t all_pix;

    Loop1 : do {    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
    } while(pix.user == 0);

    all_pix = row_size * col_size;
    Loop2 : for (uint32_t p=0; p<all_pix; p++){
#pragma HLS LOOP_TRIPCOUNT avg=480000 max=2073600 min=3072
#pragma HLS PIPELINE II=1
        if (p != 0) // 最初の入力はすでに入力されている
            ins >> pix; // AXI4-Stream からの入力
        outs << pix;
    }
    return(0);
}


C コードの合成を行った。結果を示す。なお、solution2 を作成してテストしている。
Estimaed は 6.912 ns に向上して問題が無さそうだ。
Zybot2_81_230104.png

リソース使用量も FF が 894 個から 538 個に、LUT が 849 個から 562 個に減っている。

Export RTL を行った。solution2 なので、Implementation まで実行されている。
Zybot2_82_230104.png

CP achieved post-implementation が 9.433 ns から 6.534 ns にだいぶ向上した。

for 文などを 2 重ループにしていて性能向上に限界を感じたら 1 重ループにすれば性能が向上するだろう。当たり前といえば当たり前のような気がするが。。。
  1. 2023年01月04日 04:35 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Xilinx 社画像用 IP の AXI4-Stream のスタート位置を検出する IP を Vitis HLS 2022.2 で作成する 2

Xilinx 社画像用 IP の AXI4-Stream のスタート位置を検出する IP を Vitis HLS 2022.2 で作成する 1”の続き。

ガボール・フィルタ IP から Xilinx 社画像用 IP の AXI4-Stream のスタート位置を検出する機能を削除したので、その機能を実行する IP の find_startp を作成するということで、前回は、ソースコードを貼って、find_startp プロジェクトを作成する。今回は、C シミュレーション、C コードの合成、C/RTL 協調シミュレーション、Export RTL、Implementation を行った。

C シミュレーションを行った。結果を示す。
Zybot2_72_230103.png

find_startp/solution1/csim/build ディレクトリを示す。
Zybot2_73_230103.png

C コードの合成を行った。結果を示す。
Estimated が 8.289 ns であまり良くない。
Zybot2_74_230103.png

Vitis HLS で Solution メニューから Solution Settings... を選択する。
Solution Settings (solution1) ダイアログが表示された。
Uncertainty を 7 ns に変更した。
Zybot2_75_230103.png

もう一度、C コードの合成を行った。結果を示す。
今度は、Estimated が 7.311 ns に改善した。
Zybot2_76_230103.png
Zybot2_77_230103.png

C/RTL 協調シミュレーションを行った。結果を示す。
Zybot2_78_230103.png

総ピクセル数 3072 ピクセルを処理するのに、3087 クロックなので、性能的には問題ない。

C/RTL 協調シミュレーション波形を示す。
ここでも、ins_TREADY と outs_TVALID がほとんど 1 なので、スループットが高いということが分かる。
Zybot2_79_230103.png

Export RTL を行った。IP が生成された。

Implementation を行った。
CP achieved post-implementation が 9.433 ns と少し心配な値になっている。
Zybot2_80_230103.png
  1. 2023年01月03日 05:11 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Xilinx 社画像用 IP の AXI4-Stream のスタート位置を検出する IP を Vitis HLS 2022.2 で作成する 1

ガボール・フィルタ IP から Xilinx 社画像用 IP の AXI4-Stream のスタート位置を検出する機能を削除したので、その機能を実行する IP の find_startp を作成する。今回は、ソースコードを貼って、find_startp プロジェクトを作成する。
なお、find_startp IP はきちんと IP の制御をしていれば要らないとは思うが、フェール・セーフを考えて入れておくことにしよう。

ヘッダ・ファイルの bmp_header.h はここに貼ってある

find_startp.cpp を貼っておく。

// find_startp.cpp
// 2022/12/31 by marsee
//

#include <stdio.h>
#include <stdint.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

int find_startp(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs,
        uint32_t row_size, uint32_t col_size){
#pragma HLS INTERFACE mode=s_axilite port=col_size
#pragma HLS INTERFACE mode=s_axilite port=row_size
#pragma HLS INTERFACE mode=axis register_mode=both port=ins register
#pragma HLS INTERFACE mode=axis register_mode=both port=outs register
#pragma HLS INTERFACE mode=s_axilite port=return

    ap_axis<32,1,1,1> pix;

    Loop1 : do {    // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
    } while(pix.user == 0);

    Loop2 : for (int y=0; y<row_size; y++){
#pragma HLS LOOP_TRIPCOUNT avg=600 max=1080 min=48
        Loop3 : for (int x=0; x<col_size; x++){
#pragma HLS LOOP_TRIPCOUNT avg=800 max=1920 min=64
#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix; // AXI4-Stream からの入力
            outs << pix;
        }
    }
    return(0);
}


テストベンチの find_startp_tb.cpp を貼っておく。

// find_startp_tb.cpp
// 2022/12/31 by marsee
//

#include <stdio.h>
#include <stdint.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#include "bmp_header.h"

int find_startp(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs,
        uint32_t row_size, uint32_t col_size);

int main(){
    using namespace std;

    hls::stream<ap_axis<32,1,1,1> > ins;
    hls::stream<ap_axis<32,1,1,1> > outs;
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> vals;

    BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
    BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
    FILE *fbmpr, *fbmpw;
    int *rd_bmp, *hw_lapd;
    int blue, green, red;

    if ((fbmpr = fopen("test.bmp", "rb")) == NULL){ // test.bmp をオープン
        fprintf(stderr, "Can't open test.bmp by binary read mode\n");
        exit(1);
    }
    // bmpヘッダの読み出し
    fread(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpr);
    fread(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpr);
    fread(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpr);

    // ピクセルを入れるメモリをアロケートする
    if ((rd_bmp =(int32_t *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }
    if ((hw_lapd =(int32_t *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_lapd memory\n");
        exit(1);
    }

    // rd_bmp にBMPのピクセルを代入。その際に、行を逆転する必要がある
    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            blue = fgetc(fbmpr);
            green = fgetc(fbmpr);
            red = fgetc(fbmpr);
            rd_bmp[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] = (blue & 0xff) | ((green & 0xff)<<8) | ((red & 0xff)<<16);
        }
    }
    fclose(fbmpr);

    // ins に入力データを用意する
    for(int i=0; i<5; i++){ // dummy data
        pix.user = 0;
        pix.data = i;
        ins << pix;
    }

    for(int j=0; j < bmpihr.biHeight; j++){
        for(int i=0; i < bmpihr.biWidth; i++){
            pix.data = (int32_t)rd_bmp[(j*bmpihr.biWidth)+i];

            if (j==0 && i==0)   // 最初のデータの時に TUSER を 1 にする
                pix.user = 1;
            else
                pix.user = 0;

            if (i == bmpihr.biWidth-1) // 行の最後でTLASTをアサートする
                pix.last = 1;
            else
                pix.last = 0;

            ins << pix;
        }
    }
    find_startp(ins, outs, bmpihr.biHeight, bmpihr.biWidth);

    for(int j=0; j < bmpihr.biHeight; j++){
        for(int i=0; i < bmpihr.biWidth; i++){
            outs >> vals;
            int32_t val = vals.data;
            hw_lapd[(j*bmpihr.biWidth)+i] = (int)val;
        }
    }

    // 結果を temp_startp.bmp へ出力する
    if ((fbmpw=fopen("temp_startp.bmp", "wb")) == NULL){
        fprintf(stderr, "Can't open temp_lap.bmp by binary write mode\n");
        exit(1);
    }
    // BMPファイルヘッダの書き込み
    fwrite(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpw);
    fwrite(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpw);
    fwrite(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpw);
    fwrite(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpw);
    fwrite(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpw);
    fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpw);

    // RGB データの書き込み、逆順にする
    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            blue = hw_lapd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
            green = (hw_lapd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
            red = (hw_lapd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

            fputc(blue, fbmpw);
            fputc(green, fbmpw);
            fputc(red, fbmpw);
        }
    }
    fclose(fbmpw);
    free(rd_bmp);
    free(hw_lapd);

    return 0;
}


Vitis HLS 2022.2 で作成した find_startp プロジェクトを示す。
Zybot2_71_230102.png
  1. 2023年01月02日 05:06 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream 32 ビット入出力のガボール・フィルタを変更する3

AXI4-Stream 32 ビット入出力のガボール・フィルタを変更する2”の続き。

Vitis HLS 2022.2 を使用して、AXI4-Stream 32 ビット入出力のガボール・フィルタ Gabor_Filter_lh_2 を Gabor_Filter_lh_3 として作成することにしたということで、前回は、C シミュレーションを行って、C コードの合成を行ったところ、Estimated が 10.213 ns と 10 ns をオーバーしてしまったので、 Gabor_Filter_lh_3.cpp を書き換えた。今回は、C コードの合成をやり直したところ、Estimated は 7.385 ns に改善した。C/RTL 協調シミュレーション、Export RTL、Implementation を行った。

Gabor_Filter_lh_3.cpp を変更したので、C コードの合成をやり直した。
C コードの合成結果で、前回示したクリティカル・パスの部分を示す。
icmp が一番レイテンシが大きいようで、2.5 ns 程度になるようだ。これを排除できればレイテンシが短くなる。icmp は引き算が入るので、レイテンシが大きいのだろう?
Zybot2_66_221231.png

C コードの合成結果を貼っておく。
Zybot2_67_221231.png

C/RTL 協調シミュレーションを行った。結果を示す。
Zybot2_68_221231.png

64 x 48 ピクセル = 3072 ピクセルを 2 画面分で 6114 ピクセル分ガボール・フィルタ処理を行っているので、1 クロック / ピクセルに近くなっている。

C/RTL 協調シミュレーション波形を示す。
Zybot2_69_221231.png

Export RTL を行った。

Implementation を行った。結果を示す。
Zybot2_70_221231.png

CP achieved post-implementation は 9.329 ns で問題無さそうだ。
  1. 2022年12月31日 05:10 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream 32 ビット入出力のガボール・フィルタを変更する2

AXI4-Stream 32 ビット入出力のガボール・フィルタを変更する1”の続き。

Vitis HLS 2022.2 を使用して、AXI4-Stream 32 ビット入出力のガボール・フィルタ Gabor_Filter_lh_2 を Gabor_Filter_lh_3 として作成することにしたということで、前回は、ソースコードを貼って、Vitis HLS 2022.2 の Gabor_Filter_lh_3 プロジェクトを作成した。今回は、C シミュレーションを行って、C コードの合成を行ったところ、Estimated が 10.213 ns と 10 ns をオーバーしてしまったので、 Gabor_Filter_lh_3.cpp を書き換えた。

C シミュレーションを行った。結果を示す。
Zybot2_63_221229.png

Gabor_Filter_lh_3/solution1/csim/build ディレクトリを示す。
???0.bmp が左白線用パラメータのガボール・フィルタ画像で、???0.bmp が右白線パラメータのガボール・フィルタ画像を示す。
Zybot2_64_221229.png

C コードの合成を行ったが、Estimated が 10.213 ns と 10 ns をオーバーしてしまった。
Zybot2_65_221229.png

Estimated は、Uncertainty を大きくしても変わらなかった。
そこで、クリティカル・パスを調査した。
その結果、最後の”('icmp_ln54', Gabor_Filter_lh_3/Gabor_Filter_lh_3.cpp:54) [315] (2.47 ns)”は回避することができそうだ。
Zybot2_66_221230.png

この if 文は画像の最初の user = 1 の時のピクセルはすでに do while() 文で読み込まれているので、最初だけピクセルを読まないようにする文だ。
この文は、最初から user = 1 だったら無くすことができる。その場合は、do while() 文も削除する。
もし最初に user = 1 じゃない場合は、最初に user = 1 になってから受け取るような IP を置いておけば良いはずだ。
その様に改良した Gabor_Filter_lh_3.cpp を示す。

// Gabor_Fiter_lh_3.cpp
// 2016/07/23 by marsee
// 2016/07/25 : 右白線検出用のGabor Filterを追加して、右左の白線を指定するRorL 引数を追加
// 2016/07/27 : 右白線検出用配列と左白線検出用配列を統合
// 2016/08/29 : 1回目左白線検出、2回目右白線検出のモードを追加
// 2022/12/14 : Vitis HLS用に書き直した
// 2022/12/27 : 引数にrow_size, col_size, functionを追加
//              functionの値で元画像をスルーとガボール・フィルタ処理を選択できるようにした
// 2022/12/30 : user=1を待つdo while()文とmain()中のif文を削除した
//

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#include "Gabor_Filter_lh_3.h"

int conv_rgb2y(int rgb);

int Gabor_Filter_lh_3(hls::stream<ap_axis<32,1,1,1> >& ins,
        hls::stream<ap_axis<32,1,1,1> >& outs, ap_uint<2> & RorL,
        int32_t row_size, int32_t col_size, int32_t function){
#pragma HLS INTERFACE mode=s_axilite port=function
#pragma HLS INTERFACE mode=s_axilite port=col_size
#pragma HLS INTERFACE mode=s_axilite port=row_size
#pragma HLS INTERFACE mode=ap_none port=RorL
#pragma HLS INTERFACE mode=axis register_mode=both port=ins register
#pragma HLS INTERFACE mode=axis register_mode=both port=outs register
#pragma HLS INTERFACE mode=s_axilite port=return

    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> gabor;

    ap_int<32> line_buf[ARRAY_SIZE-1][1920]; // Up to HD resolution
#pragma HLS array_partition variable=line_buf block factor=9 dim=1

    ap_int<32> pix_mat[ARRAY_SIZE][ARRAY_SIZE];
    int gray_pix, val, i, j, x, y, k;

    LOOP_LR: for (k=0; k<2; k++){
        LOOP_Y: for (y=0; y<row_size; y++){
#pragma HLS LOOP_TRIPCOUNT avg=600 max=1080 min=48
            LOOP_X: for (x=0; x<col_size; x++){
#pragma HLS LOOP_TRIPCOUNT avg=800 max=1920 min=64
#pragma HLS PIPELINE II=1
                ins >> pix;    // AXI4-Stream からの入力

                LOOP_PIX_MAT_K: for(int k=0; k<ARRAY_SIZE; k++){
                    LOOP_PIX_MAT_M: for(int m=0; m<ARRAY_SIZE-1; m++){
                        pix_mat[k][m] = pix_mat[k][m+1];
                    }
                }
                for (i=0; i<ARRAY_SIZE-1; i++){
                    pix_mat[i][ARRAY_SIZE-1] = line_buf[i][x];
                }
                ap_int<32> y_val = conv_rgb2y(pix.data);
                pix_mat[ARRAY_SIZE-1][ARRAY_SIZE-1] = y_val;

                for (i=0; i<ARRAY_SIZE-2; i++){    // 行の入れ替え
                    line_buf[i][x] = line_buf[i+1][x];
                }
                line_buf[ARRAY_SIZE-2][x] = y_val;

                // 使用する配列を決定する
                int ano;
                switch (RorL){
                case LEFT_WEIGHT :
                    ano = LEFT_WEIGHT;
                    break;
                case RIGHT_WEIGHT :
                    ano = RIGHT_WEIGHT;
                    break;
                case L_R_WEIGHT :
                    if (k == 0)
                        ano = LEFT_WEIGHT;
                    else
                        ano = RIGHT_WEIGHT;
                    break;
                default :
                    ano = L_R_WEIGHT;
                    break;
                }

                // Gabor filter の演算
                for (j=0, val=0; j<ARRAY_SIZE; j++){
                    for (i=0; i<ARRAY_SIZE; i++){
                        val += gabor_weight[ano][j][i] * pix_mat[j][i];
                    }
                }
                val = val/256; // 256倍してあるので、1/256して戻す
                if (val<0)
                    //val = -val; // 絶対値
                    val = 0; // マイナスの値を0に丸める
                else if (val>255)
                    val = 255;

                // Gabor filter・データの書き込み
                if(function == Gabor_Filter)
                    gabor.data = (val<<16)+(val<<8)+val;
                else
                    gabor.data = pix.data;

                // 最初のARRAY_SIZE-1行とその他の行の最初のARRAY_SIZE-1列は無効データなので0とする
                if (x<(ARRAY_SIZE-1) || y<(ARRAY_SIZE-1)){
                    if(function == Gabor_Filter)
                        gabor.data = 0;
                }

                if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                    gabor.user = 1;
                else
                    gabor.user = 0;

                if (x == (col_size-1))    // 行の最後で TLAST をアサートする
                    gabor.last = 1;
                else
                    gabor.last = 0;

                outs << gabor;    // AXI4-Stream へ出力
             }
         }
    }
     return(0);
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8; // 256で割る

    return(y);
}

  1. 2022年12月30日 15:50 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream 32 ビット入出力のガボール・フィルタを変更する1

Vitis HLS 2022.2 を使用して、AXI4-Stream 32 ビット入出力のガボール・フィルタ Gabor_Filter_lh_2 を Gabor_Filter_lh_3 として作成することにした。
Gabor_Filter_lh_3 はスルー出力とガボール・フィルタ出力を切り替えることができて、画像の大きさを指定することができるようにする。

Gabor_Filter_lh_3 のソースコードを貼っておく。
まずは、bmp_header.h だが、これはおなじみなのでリンクを貼っておく。
bmp_header.h は”AXI4-Stream 32 ビット入出力のラプラシアン・フィルタを作る1”に貼ってある。

Gabor_filter_lh_3.h を示す。

// Gabor_filter_lh_3.h
// 2016/07/24
// 2016/07/25 : 右白線検出用のGabor Filterの重みを追加
// 2016/07/27 : 右白線検出用配列と左白線検出用配列を統合
// 2016/08/29 : 1回目左白線検出、2回目右白線検出のモードを追加
// 2022/12/27 : 引数にrow_size, col_size, functionを追加
//              functionの値で元画像をスルーとガボール・フィルタ処理を選択できるようにした
//

#ifndef __Gabor_filter_lh_H__
#define __Gabor_filter_lh_H__

#define ARRAY_SIZE  9

#define RIGHT_WEIGHT    1
#define LEFT_WEIGHT     0
#define L_R_WEIGHT      2

#define Through         0
#define Gabor_Filter    1

const int gabor_weight[2][ARRAY_SIZE][ARRAY_SIZE] = { // 左白線検出用+右白線検出用
    {
        {0,-3,-10,-16,-7,7,10,5,1},
        {-3,-15,-27,-11,32,50,29,5,-2},
        {-11,-24,-5,73,135,95,16,-17,-10},
        {-11,4,85,187,160,14,-72,-52,-14},
        {4,51,135,137,-18,-159,-136,-45,-2},
        {16,50,59,-39,-179,-185,-73,3,13},
        {10,12,-25,-104,-131,-60,15,27,11},
        {1,-7,-31,-48,-24,18,29,14,3},
        {-1,-5,-9,-4,10,16,10,2,-1}
    },
    {
        {1,5,7,1,-12,-17,-10,-3,0},
        {1,11,33,45,21,-16,-27,-15,-4},
        {-8,-5,35,107,131,69,-2,-21,-11},
        {-17,-47,-51,40,169,187,93,13,-7},
        {-8,-54,-134,-147,-18,123,130,58,11},
        {9,-6,-82,-185,-187,-65,36,44,17},
        {11,24,12,-55,-125,-112,-43,1,7},
        {3,14,30,23,-13,-41,-33,-12,-1},
        {0,2,9,17,14,1,-6,-5,-2}
    }
};
const float gabor_fweight[2][ARRAY_SIZE][ARRAY_SIZE] = { // 左白線検出用+右白線検出用(float)
    {
        {0.001282,-0.009914,-0.04062,-0.060586,-0.027574,0.026072,0.038427,0.018191,0.003056},
        {-0.012155,-0.057448,-0.104645,-0.042953,0.123263,0.197238,0.114451,0.020448,-0.007239},
        {-0.042252,-0.093065,-0.018911,0.285374,0.525746,0.372687,0.060734,-0.064748,-0.040465},
        {-0.042261,0.015673,0.332798,0.728763,0.625046,0.053591,-0.283076,-0.203293,-0.05608},
        {0.017342,0.198305,0.52554,0.535526,-0.069756,-0.622839,-0.531089,-0.177366,-0.006367},
        {0.060866,0.19708,0.231032,-0.154219,-0.699885,-0.721808,-0.286707,0.013004,0.049249},
        {0.038379,0.04877,-0.098477,-0.404993,-0.510165,-0.233566,0.057894,0.104366,0.041887},
        {0.0047,-0.0278,-0.121277,-0.187262,-0.093276,0.070512,0.113857,0.055799,0.009976},
        {-0.003798,-0.01885,-0.035607,-0.01709,0.037692,0.064268,0.038606,0.007536,-0.002133}
    },
    {
        {0.005562,0.018882,0.028293,0.004499,-0.044995,-0.064838,-0.039469,-0.009822,0.000815},
        {0.002294,0.04108,0.127023,0.175094,0.083025,-0.063755,-0.106402,-0.057798,-0.01406},
        {-0.031269,-0.021096,0.135641,0.417286,0.512467,0.269946,-0.008354,-0.082091,-0.041357},
        {-0.066348,-0.184919,-0.197802,0.15614,0.65976,0.728616,0.361674,0.052074,-0.027152},
        {-0.031146,-0.211178,-0.523777,-0.573856,-0.069756,0.480311,0.506451,0.225223,0.041031},
        {0.035552,-0.023892,-0.320104,-0.723563,-0.728735,-0.253689,0.1391,0.170625,0.067723},
        {0.04216,0.094939,0.047511,-0.216623,-0.488075,-0.437898,-0.168739,0.003336,0.027009},
        {0.012112,0.056596,0.115239,0.090332,-0.05076,-0.158403,-0.127847,-0.046375,-0.004918},
        {-0.00168,0.007437,0.036985,0.067021,0.053689,0.004977,-0.02365,-0.018248,-0.005928}
    }
};

#endif


Gabor_Filter_lh_3.cpp を示す。

// Gabor_Filter_lh_3.cpp
// 2016/07/23 by marsee
// 2016/07/25 : 右白線検出用のGabor Filterを追加して、右左の白線を指定するRorL 引数を追加
// 2016/07/27 : 右白線検出用配列と左白線検出用配列を統合
// 2016/08/29 : 1回目左白線検出、2回目右白線検出のモードを追加
// 2022/12/14 : Vitis HLS用に書き直した
// 2022/12/27 : 引数にrow_size, col_size, functionを追加
//              functionの値で元画像をスルーとガボール・フィルタ処理を選択できるようにした
//

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#include "Gabor_Filter_lh_3.h"

int conv_rgb2y(int rgb);

int Gabor_Filter_lh_3(hls::stream<ap_axis<32,1,1,1> >& ins,
        hls::stream<ap_axis<32,1,1,1> >& outs, ap_uint<2> & RorL,
        int32_t row_size, int32_t col_size, int32_t function){
#pragma HLS INTERFACE mode=s_axilite port=function
#pragma HLS INTERFACE mode=s_axilite port=col_size
#pragma HLS INTERFACE mode=s_axilite port=row_size
#pragma HLS INTERFACE mode=ap_none port=RorL
#pragma HLS INTERFACE mode=axis register_mode=both port=ins register
#pragma HLS INTERFACE mode=axis register_mode=both port=outs register
#pragma HLS INTERFACE mode=s_axilite port=return

    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> gabor;

    ap_int<32> line_buf[ARRAY_SIZE-1][1920]; // Up to HD resolution
#pragma HLS array_partition variable=line_buf block factor=9 dim=1

    ap_int<32> pix_mat[ARRAY_SIZE][ARRAY_SIZE];
    int gray_pix, val, i, j, x, y, k;

    LOOP_WAIT_USER: do {
#pragma HLS LOOP_TRIPCOUNT avg=1 max=1 min=1
    // user が 1になった時にフレームがスタートする
        ins >> pix;
    } while(pix.user == 0);

    LOOP_LR: for (k=0; k<2; k++){
        LOOP_Y: for (y=0; y<row_size; y++){
#pragma HLS LOOP_TRIPCOUNT avg=600 max=1080 min=48
            LOOP_X: for (x=0; x<col_size; x++){
#pragma HLS LOOP_TRIPCOUNT avg=800 max=1920 min=64
#pragma HLS PIPELINE II=1
                if (!(k==0 && x==0 && y==0))    // 最初の入力はすでに入力されている
                    ins >> pix;    // AXI4-Stream からの入力

                LOOP_PIX_MAT_K: for(int k=0; k<ARRAY_SIZE; k++){
                    LOOP_PIX_MAT_M: for(int m=0; m<ARRAY_SIZE-1; m++){
                        pix_mat[k][m] = pix_mat[k][m+1];
                    }
                }
                for (i=0; i<ARRAY_SIZE-1; i++){
                    pix_mat[i][ARRAY_SIZE-1] = line_buf[i][x];
                }
                ap_int<32> y_val = conv_rgb2y(pix.data);
                pix_mat[ARRAY_SIZE-1][ARRAY_SIZE-1] = y_val;

                for (i=0; i<ARRAY_SIZE-2; i++){    // 行の入れ替え
                    line_buf[i][x] = line_buf[i+1][x];
                }
                line_buf[ARRAY_SIZE-2][x] = y_val;

                // 使用する配列を決定する
                int ano;
                switch (RorL){
                case LEFT_WEIGHT :
                    ano = LEFT_WEIGHT;
                    break;
                case RIGHT_WEIGHT :
                    ano = RIGHT_WEIGHT;
                    break;
                case L_R_WEIGHT :
                    if (k == 0)
                        ano = LEFT_WEIGHT;
                    else
                        ano = RIGHT_WEIGHT;
                    break;
                default :
                    ano = L_R_WEIGHT;
                    break;
                }

                // Gabor filter の演算
                for (j=0, val=0; j<ARRAY_SIZE; j++){
                    for (i=0; i<ARRAY_SIZE; i++){
                        val += gabor_weight[ano][j][i] * pix_mat[j][i];
                    }
                }
                val = val/256; // 256倍してあるので、1/256して戻す
                if (val<0)
                    //val = -val; // 絶対値
                    val = 0; // マイナスの値を0に丸める
                else if (val>255)
                    val = 255;

                // Gabor filter・データの書き込み
                if(function == Gabor_Filter)
                    gabor.data = (val<<16)+(val<<8)+val;
                else
                    gabor.data = pix.data;

                // 最初のARRAY_SIZE-1行とその他の行の最初のARRAY_SIZE-1列は無効データなので0とする
                if (x<(ARRAY_SIZE-1) || y<(ARRAY_SIZE-1)){
                    if(function == Gabor_Filter)
                        gabor.data = 0;
                }

                if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                    gabor.user = 1;
                else
                    gabor.user = 0;

                if (x == (col_size-1))    // 行の最後で TLAST をアサートする
                    gabor.last = 1;
                else
                    gabor.last = 0;

                outs << gabor;    // AXI4-Stream へ出力
             }
         }
    }
     return(0);
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8; // 256で割る

    return(y);
}


Gabor_Filter_lh_3_tb.cpp を示す。

// Gabor_Filter_lh_3_tb.cpp
// 2016/07/24 by marsee
// 2016/07/25 : 右白線検出用のGabor Filterを追加して、右左の白線を指定するRorL 引数を追加
// 2022/12/14 : Vitis HLS用に書き直した
// 2022/12/27 : 引数にrow_size, col_size, functionを追加
//              functionの値で元画像をスルーとガボール・フィルタ処理を選択できるようにした
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>
#include <math.h>
#include <ap_axi_sdata.h>

#include "Gabor_Filter_lh_3.h"
#include "bmp_header.h"

int Gabor_Filter_lh_3(hls::stream<ap_axis<32,1,1,1> >& ins,
        hls::stream<ap_axis<32,1,1,1> >& outs, ap_uint<2> & RorL,
        int32_t row_size, int32_t col_size, int32_t function);

int conv_rgb2y_soft(int rgb);
int Gabor_Filter_lh_3_soft(hls::stream<ap_axis<32,1,1,1> >& ins,
        hls::stream<ap_axis<32,1,1,1> >& outs, ap_uint<2> & RorL,
        int32_t row_size, int32_t col_size, int32_t function);

#define CLOCK_PERIOD 10
#define RIGHT_OR_LEFT   L_R_WEIGHT
#define BMP_FILE_NAME   "test.bmp"

int main()
{
    using namespace std;

    hls::stream<ap_axis<32,1,1,1> > ins, ins2;
    hls::stream<ap_axis<32,1,1,1> > ins_soft;
    hls::stream<ap_axis<32,1,1,1> > outs, outs2;
    hls::stream<ap_axis<32,1,1,1> > outs_soft;
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> vals;
    ap_axis<32,1,1,1> vals_soft;

    int m_seq = 1; // M系列の値
    int i, k;
    int xor_shift;

    BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
    BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
    FILE *fbmpr, *fbmpw, *fbmpwf;
    int *rd_bmp, *hw_gabor, *sw_gabor;
    int blue, green, red;
    ap_uint<2> r_l;
    char fhname[100];
    char fsname[100];

    if ((fbmpr = fopen(BMP_FILE_NAME, "rb")) == NULL){ // test.bmp をオープン
        fprintf(stderr, "Can't open test.bmp by binary read mode\n");
        exit(1);
    }
    // bmpヘッダの読み出し
    fread(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpr);
    fread(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpr);
    fread(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpr);
    fread(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpr);

    printf("bmpihr.biWidth = %d, bmpihr.biHeight = %d\n",(int)bmpihr.biWidth, (int)bmpihr.biHeight);
    // ピクセルを入れるメモリをアロケートする
    if ((rd_bmp =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate rd_bmp memory\n");
        exit(1);
    }
    if ((hw_gabor =(int *)malloc(2 * sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_gabor memory\n");
        exit(1);
    }
    if ((sw_gabor =(int *)malloc(2 * sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_gabor memory\n");
        exit(1);
    }

    // rd_bmp にBMPのピクセルを代入。その際に、行を逆転する必要がある
    for (int y=0; y<bmpihr.biHeight; y++){
        for (int x=0; x<bmpihr.biWidth; x++){
            blue = fgetc(fbmpr);
            green = fgetc(fbmpr);
            red = fgetc(fbmpr);
            rd_bmp[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] = (blue & 0xff) | ((green & 0xff)<<8) | ((red & 0xff)<<16);
        }
    }
    fclose(fbmpr);

    // 2画面分を入力する
    for(int k=0; k<2; k++){
        for(int j=0; j < bmpihr.biHeight; j++){
            for(i=0; i < bmpihr.biWidth; i++){
                pix.data = (ap_int<32>)rd_bmp[(j*bmpihr.biWidth)+i];

                if (j==0 && i==0)    // 最初のデータの時に TUSER を 1 にする
                    pix.user = 1;
                else
                    pix.user = 0;

                if (i == bmpihr.biWidth-1) // 行の最後でTLASTをアサートする
                    pix.last = 1;
                else
                    pix.last = 0;

                ins << pix;
                ins2 << pix;
                ins_soft << pix;
            }
        }
    }

    r_l = (ap_uint<2>)RIGHT_OR_LEFT;
    Gabor_Filter_lh_3(ins, outs, r_l, 48, 64, Gabor_Filter);
    Gabor_Filter_lh_3_soft(ins_soft, outs_soft, r_l, 48, 64, Gabor_Filter);

    // ハードウェアとソフトウェアのラプラシアン・フィルタの値のチェック
    for(k=0; k<2; k++){
        for(int j=0; j < bmpihr.biHeight; j++){
            for(i=0; i < bmpihr.biWidth; i++){
                outs >> vals;
                outs_soft >> vals_soft;
                ap_int<32> val = vals.data;
                ap_int<32> val_soft = vals_soft.data;

                hw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+(j*bmpihr.biWidth)+i] = (int)val;
                sw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+(j*bmpihr.biWidth)+i] = (int)val_soft;

                //printf("k=%d, j=%d, i=%d\n",k,j,i);
                if ((double)pow((double)(val&0xff)-(val_soft&0xff),(double)2) > 4){ // 2乗誤差が4よりも大きい
                    printf("ERROR HW and SW results mismatch i = %ld, j = %ld, HW = %08x, SW = %08x\n", i, j, (int)val, (int)val_soft);
                    //return(1);
                }
                //if (vals.last)
                    //cout << "AXI-Stream is end" << endl;
            }
        }
    }
    cout << "Success HW and SW results match" << endl;
    cout << endl;

    // ハードウェアのラプラシアンフィルタの結果を temp_gabor0.bmp, temp_gabor1.bmp へ出力する
    for(k=0; k<2; k++){
        if(k == 0){
            if ((fbmpw=fopen("temp_gabor0.bmp", "wb")) == NULL){
                fprintf(stderr, "Can't open temp_gabor0.bmp by binary write mode\n");
                exit(1);
            }
        } else {
            if ((fbmpw=fopen("temp_gabor1.bmp", "wb")) == NULL){
                fprintf(stderr, "Can't open temp_gabor1.bmp by binary write mode\n");
                exit(1);
            }
        }

        // BMPファイルヘッダの書き込み
        fwrite(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpw);
        fwrite(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpw);
        fwrite(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpw);
        fwrite(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpw);
        fwrite(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpw);
        fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpw);
        // RGB データの書き込み、逆順にする
        for (int y=0; y<bmpihr.biHeight; y++){
            for (int x=0; x<bmpihr.biWidth; x++){
                blue = hw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
                green = (hw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
                red = (hw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

                fputc(blue, fbmpw);
                fputc(green, fbmpw);
                fputc(red, fbmpw);
            }
        }
        fclose(fbmpw);
    }

    // 元画像を出力
    Gabor_Filter_lh_3(ins2, outs2, r_l, 48, 64, Through);

    // hw_gabor[]に画像を出力
    for(k=0; k<2; k++){
        for(int j=0; j < bmpihr.biHeight; j++){
            for(i=0; i < bmpihr.biWidth; i++){
                outs2 >> vals;
                ap_int<32> val = vals.data;

                hw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+(j*bmpihr.biWidth)+i] = (int)val;
            }
        }
    }

    // ハードウェアのラプラシアンフィルタの結果を temp_gabor0.bmp, temp_gabor1.bmp へ出力する
    for(k=0; k<2; k++){
        if(k == 0){
            if ((fbmpw=fopen("temp_org0.bmp", "wb")) == NULL){
                fprintf(stderr, "Can't open temp_org0.bmp by binary write mode\n");
                exit(1);
            }
        } else {
            if ((fbmpw=fopen("temp_org1.bmp", "wb")) == NULL){
                fprintf(stderr, "Can't open temp_org1.bmp by binary write mode\n");
                exit(1);
            }
        }

        // BMPファイルヘッダの書き込み
        fwrite(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpw);
        fwrite(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpw);
        fwrite(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpw);
        fwrite(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpw);
        fwrite(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpw);
        fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpw);
        // RGB データの書き込み、逆順にする
        for (int y=0; y<bmpihr.biHeight; y++){
            for (int x=0; x<bmpihr.biWidth; x++){
                blue = hw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
                green = (hw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
                red = (hw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

                fputc(blue, fbmpw);
                fputc(green, fbmpw);
                fputc(red, fbmpw);
            }
        }
        fclose(fbmpw);
    }

    // ソフトウェアのラプラシアンフィルタの結果を temp_gabor_float0.bmp, temp_gabor_float1.bmp へ出力する
    for(k=0; k<2; k++){
        if (k == 0){
            if ((fbmpwf=fopen("temp_gabor_float0.bmp", "wb")) == NULL){
                fprintf(stderr, "Can't open temp_gabor_float0.bmp by binary write mode\n");
                exit(1);
            }
        } else {
            if ((fbmpwf=fopen("temp_gabor_float1.bmp", "wb")) == NULL){
                fprintf(stderr, "Can't open temp_gabor_float1.bmp by binary write mode\n");
                exit(1);
            }
        }

        // BMPファイルヘッダの書き込み
        fwrite(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpwf);
        fwrite(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpwf);
        fwrite(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpwf);
        fwrite(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpwf);
        fwrite(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpwf);
        fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpwf);
        // RGB データの書き込み、逆順にする
        for (int y=0; y<bmpihr.biHeight; y++){
            for (int x=0; x<bmpihr.biWidth; x++){
                blue = sw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
                green = (sw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
                red = (sw_gabor[bmpihr.biWidth*bmpihr.biHeight*k+((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;

                fputc(blue, fbmpwf);
                fputc(green, fbmpwf);
                fputc(red, fbmpwf);
            }
        }
        fclose(fbmpwf);
    }

    free(rd_bmp);
    free(hw_gabor);

    return 0;
}

int Gabor_Filter_lh_3_soft(hls::stream<ap_axis<32,1,1,1> >& ins,
        hls::stream<ap_axis<32,1,1,1> >& outs, ap_uint<2> & RorL,
        int32_t row_size, int32_t col_size, int32_t function){
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> gabor;

    ap_int<32> line_buf[ARRAY_SIZE-1][1920]; // Up to HD resolution
    ap_int<32> pix_mat[ARRAY_SIZE][ARRAY_SIZE];
    int gray_pix, val, i, j, x, y, k;

    do {
    // user が 1になった時にフレームがスタートする
        ins >> pix;
    } while(pix.user == 0);

    for (k=0; k<2; k++){
        for (y=0; y<row_size; y++){
            for (x=0; x<col_size; x++){
                if (!(k==0 && x==0 && y==0))    // 最初の入力はすでに入力されている
                    ins >> pix;    // AXI4-Stream からの入力

                LOOP_PIX_MAT_K: for(int k=0; k<ARRAY_SIZE; k++){
                    LOOP_PIX_MAT_M: for(int m=0; m<ARRAY_SIZE-1; m++){
                        pix_mat[k][m] = pix_mat[k][m+1];
                    }
                }
                for (i=0; i<ARRAY_SIZE-1; i++){
                    pix_mat[i][ARRAY_SIZE-1] = line_buf[i][x];
                }
                ap_int<32> y_val = conv_rgb2y_soft(pix.data);
                pix_mat[ARRAY_SIZE-1][ARRAY_SIZE-1] = y_val;

                for (i=0; i<ARRAY_SIZE-2; i++){    // 行の入れ替え
                    line_buf[i][x] = line_buf[i+1][x];
                }
                line_buf[ARRAY_SIZE-2][x] = y_val;

                // 使用する配列を決定する
                int ano;
                switch (RorL){
                case LEFT_WEIGHT :
                    ano = LEFT_WEIGHT;
                    break;
                case RIGHT_WEIGHT :
                    ano = RIGHT_WEIGHT;
                    break;
                case L_R_WEIGHT :
                    if (k == 0)
                        ano = LEFT_WEIGHT;
                    else
                        ano = RIGHT_WEIGHT;
                    break;
                default :
                    ano = L_R_WEIGHT;
                    break;
                }

                // Gabor filter の演算
                for (j=0, val=0; j<ARRAY_SIZE; j++){
                    for (i=0; i<ARRAY_SIZE; i++){
                        val += gabor_weight[ano][j][i] * pix_mat[j][i];
                    }
                }
                val = val/256; // 256倍してあるので、1/256して戻す
                if (val<0)
                    //val = -val; // 絶対値
                    val = 0; // マイナスの値を0に丸める
                else if (val>255)
                    val = 255;

                // Gabor filter・データの書き込み
                if(function == Gabor_Filter)
                    gabor.data = (val<<16)+(val<<8)+val;
                else
                    gabor.data = pix.data;

                // 最初のARRAY_SIZE-1行とその他の行の最初のARRAY_SIZE-1列は無効データなので0とする
                if (x<(ARRAY_SIZE-1) || y<(ARRAY_SIZE-1)){
                    if(function == Gabor_Filter)
                        gabor.data = 0;
                }

                if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
                    gabor.user = 1;
                else
                    gabor.user = 0;

                if (x == (col_size-1))    // 行の最後で TLAST をアサートする
                    gabor.last = 1;
                else
                    gabor.last = 0;

                outs << gabor;    // AXI4-Stream へ出力
             }
         }
    }
     return(0);
}

// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int conv_rgb2y_soft(int rgb){
    int r, g, b, y_f;
    int y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8; // 256で割る

    return(y);
}


Vitis HLS 2022.2 の Gabor_Filter_lh_3 プロジェクトを示す。
Zybot2_62_221229.png
  1. 2022年12月29日 05:09 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0
»