FC2カウンター FPGAの部屋 Vivado HLS によるアンシャープマスクキング・フィルタの作製2(floatで実装してみた)

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

FPGAの部屋

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

Vivado HLS によるアンシャープマスクキング・フィルタの作製2(floatで実装してみた)

”Vivado HLS によるアンシャープマスクキング・フィルタの作製1(準備編)”の続き。

今回はZYBO 用のアンシャープマスキング・フィルタのプロジェクト unsharp_mask_14_4 をVivado HLS 2014.4 で作製した。
Source には、unsharp_mask_axis.cpp、unsharp_mask_axis.h を入れた。
Test Bench には、bmp_header.h、test.bmp、unsharp_mask_axis_tb.cpp を入れた。
unsharp_mask_2_150926.png

Cシミュレーションを行った。その際に、C Simulation Dialog で Clean Build にチェックを入れておいたほうが良さそうだ。
特に、バグありの状態からデバックしている際には、TCLエラーか抜け出せない時があったが、 Clean Build にチェックを入れたらビルドが成功した。
unsharp_mask_3_150926.png

Cシミュレーションが成功した。
unsharp_mask_4_150926.png

A の文字のBMPファイル(test.bmp) をアンシャープマスキング・フィルタを通した結果が test_usm.bmp ファイルに出力される。
unsharp_mask_5_150926.png

下に2つのファイルを示す。鮮鋭化の強さ k は、3.0 に設定してある。
unsharp_mask_6_150926.png

C から HDL へ合成を行った。
Estmated が 8.62 ns なので成功しているのだが、
unsharp_mask_7_150926.png

リソースのDSP が 153 %、LUT が 330 % になってしまってZYBO のZynq-7010 に入らない。
unsharp_mask_8_150926.png

失敗だ。

Anaylsis を見てみた。
unsharp_mask_9_150926.png

ステートはC114 まである。とても長いステートなので、LUT も相当増えてしまったのだろう?
unsharp_mask_10_150926.png

最後にソースだが、bmp_header.h は”SDSoC 2015.2 でハードウェアとソフトウェアのラプラシアンフィルタの性能を比較した1(ソースの公開)”を参照のこと。

unsharp_mask_axis.h を下に示す。

// unsharp_mask_axis.h
// 2015/09/26 by marsee

//#define HORIZONTAL_PIXEL_WIDTH 1280
//#define VERTICAL_PIXEL_WIDTH 720

#define HORIZONTAL_PIXEL_WIDTH 64
#define VERTICAL_PIXEL_WIDTH 48

#define ALL_PIXEL_VALUE (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)


unsharp_mask_axis.cpp を下に示す。

// unsharp_mask_axis.cpp
// 2015/09/24 by marsee
//

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

#include "unsharp_mask_axis.h"

int unsharp_masking(int pix_mat[3][3], int k, int num_adec_k);

int unsharp_mask_axis(ap_uint<1> usm_fil_enable, ap_uint<4> usm_fil_k, hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs){
#pragma HLS INTERFACE ap_none port=usm_fil_enable
#pragma HLS INTERFACE ap_none port=usm_fil_k
#pragma HLS INTERFACE axis port=ins
#pragma HLS INTERFACE axis port=outs
#pragma HLS INTERFACE ap_ctrl_none port=return

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

    int line_buf[2][HORIZONTAL_PIXEL_WIDTH];
#pragma HLS array_partition variable=line_buf block factor=2 dim=1
#pragma HLS resource variable=line_buf core=RAM_2P

    int pix_mat[3][3];
#pragma HLS array_partition variable=pix_mat complete

    int usm_fil_val;

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

    for (int y=0; y<VERTICAL_PIXEL_WIDTH; y++){
        for (int x=0; x<HORIZONTAL_PIXEL_WIDTH; x++){
#pragma HLS PIPELINE
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            for (int i=0; i<3; i++){
                for (int j=0; j<2; j++){
#pragma HLS UNROLL
                    pix_mat[i][j] = pix_mat[i][j+1];
                }
            }
            pix_mat[0][2] = line_buf[0][x];
            pix_mat[1][2] = line_buf[1][x];

            pix_mat[2][2] = pix.data;

            line_buf[0][x] = line_buf[1][x];    // 行の入れ替え
            line_buf[1][x] = pix.data;

            usm.data = unsharp_masking(pix_mat, (int)usm_fil_k, 2);

            if (x<2 || y<2// 最初の2行とその他の行の最初の2列は無効データなので元のデータとする
                usm.data = pix.data;

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

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

            if (usm_fil_enable)
                outs << usm;    // AXI4-Stream へ出力
            else
                outs << pix;    // 入力画像をそのまま出力
        }
    }

    return 0;
}

// アンシャープマスキング・フィルタ
// x0y0 x1y0 x2y0 -k   -j  -k
// x0y1 x1y1 x2y1 -k  9+8k -k x 1/9
// x0y2 x1y2 x2y2 -k   -k  -k
//
// k : 鮮鋭化の強さ(固定小数点)
// num_adec_k : Kの小数点の位置
//
int unsharp_masking(int pix_mat[3][3], int k, int num_adec_k)
{
    float y;
    int xy[3][3];
    float rgb[3];
    int result=0;

    float fk = (float)k;
    for (int m=0; m<num_adec_k; m++){ // 小数点の位置を正しい位置にする
        fk /= 2.0;
    }

    for (int i=0; i<=16; i += 8){
        for (int j=0; j<3; j++){
            for (int k=0; k<3; k++){
                xy[j][k] = (pix_mat[j][k] >> i) & 0xff; // RGBのいずれかを抽出
            }
        }
        y = -(float)xy[0][0]         -(float)xy[0][1]      -(float)xy[0][2]
            -(float)xy[1][0] +(9.0/fk+8.0)*(float)xy[1][1] -(float)xy[1][2]
            -(float)xy[2][0]         -(float)xy[2][1]      -(float)xy[2][2];
        y = (fk * y)/9.0;

        int z = (int)(y + 0.5);    // 四捨五入

        if (z<0// 飽和演算
            z = 0;
        else if (z>255)
            z = 255;

        result += z<<i; // i=0 : blue, i=8 : green, i=16 : red
    }

    return(result);
}


unsharp_mask_axis_tb.cpp を下に示す。

// unsharp_mask_axis_tb.cpp
// 2015/09/26 by marsee
//

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

#include "unsharp_mask_axis.h"
#include "bmp_header.h"

int unsharp_mask_axis(ap_uint<1> usm_fil_enable, ap_uint<4> usm_fil_k, hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs);

int unsharp_masking_soft(int pix_mat[3][3], int k, int num_adec_k);
int unsharp_mask_axis_soft(ap_uint<1> usm_fil_enable, ap_uint<4> usm_fil_k, hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs, int width, int height);

#define CLOCK_PERIOD 10

#define K 3.0 // 鮮鋭化の強さ
#define NUM_ADEC_K 2 // Kの小数点の位置

int main()
{
    using namespace std;

    hls::stream<ap_axis<32,1,1,1> > ins;
    hls::stream<ap_axis<32,1,1,1> > ins_soft;
    hls::stream<ap_axis<32,1,1,1> > outs;
    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;
    int xor_shift;

    BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
    BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
    FILE *fbmpr, *fbmpw;
    int *rd_bmp, *hw_usmd;
    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(char), 2, fbmpr);
    fread(&bmpfhr.bfSize, sizeof(long), 1, fbmpr);
    fread(&bmpfhr.bfReserved1, sizeof(short), 1, fbmpr);
    fread(&bmpfhr.bfReserved2, sizeof(short), 1, fbmpr);
    fread(&bmpfhr.bfOffBits, sizeof(long), 1, fbmpr);
    fread(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpr);

    // ピクセルを入れるメモリをアロケートする
    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_usmd =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_usmd 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(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;
            ins_soft << pix;
        }
    }

    int usm_k = (int)(K * pow(2.0, (double)NUM_ADEC_K));
    unsharp_mask_axis(1, usm_k, ins, outs); // k = 2
    unsharp_mask_axis_soft(1, usm_k, ins_soft, outs_soft, bmpihr.biWidth, bmpihr.biHeight); // k = 2;

    // ハードウェアとソフトウェアのラプラシアン・フィルタの値のチェック
    cout << endl;
    cout << "outs" << endl;
    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_usmd[(j*bmpihr.biWidth)+i] = (int)val;

            if (val != val_soft){
                printf("ERROR HW and SW results mismatch i = %ld, j = %ld, HW = %d, SW = %d\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_usm.bmp へ出力する
    if ((fbmpw=fopen("temp_usm.bmp""wb")) == NULL){
        fprintf(stderr, "Can't open temp_usm.bmp by binary write mode\n");
        exit(1);
    }
    // BMPファイルヘッダの書き込み
    fwrite(&bmpfhr.bfType, sizeof(char), 2, fbmpw);
    fwrite(&bmpfhr.bfSize, sizeof(long), 1, fbmpw);
    fwrite(&bmpfhr.bfReserved1, sizeof(short), 1, fbmpw);
    fwrite(&bmpfhr.bfReserved2, sizeof(short), 1, fbmpw);
    fwrite(&bmpfhr.bfOffBits, sizeof(long), 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_usmd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
            green = (hw_usmd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
            red = (hw_usmd[((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_usmd);

    return 0;
}

int unsharp_mask_axis_soft(ap_uint<1> usm_fil_enable, ap_uint<4> usm_fil_k, hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs, int width, int height){
    ap_axis<32,1,1,1> pix;
    ap_axis<32,1,1,1> usm;
    int **line_buf;
    int pix_mat[3][3];
    int usm_fil_val;
    int i;

    // line_buf の1次元目の配列をアロケートする
    if ((line_buf =(int **)malloc(sizeof(int *) * 2)) == NULL){
        fprintf(stderr, "Can't allocate line_buf[3][]\n");
        exit(1);
    }

    // メモリをアロケートする
    for (i=0; i<2; i++){
        if ((line_buf[i]=(int *)malloc(sizeof(int) * width)) == NULL){
            fprintf(stderr, "Can't allocate line_buf[%d]\n", i);
            exit(1);
        }
    }

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

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

            for (int k=0; k<3; k++){
                for (int m=0; m<2; m++){
                    pix_mat[k][m] = pix_mat[k][m+1];
                }
            }
            pix_mat[0][2] = line_buf[0][x];
            pix_mat[1][2] = line_buf[1][x];

            pix_mat[2][2] = pix.data;

            line_buf[0][x] = line_buf[1][x];    // 行の入れ替え
            line_buf[1][x] = pix.data;

            usm.data = unsharp_masking_soft(pix_mat, (int)usm_fil_k, 2);

            if (x<2 || y<2// 最初の2行とその他の行の最初の2列は無効データなので元のデータとする
                usm.data = pix.data;

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

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

            if (usm_fil_enable)
                outs << usm;    // AXI4-Stream へ出力
            else
                outs << pix;    // 入力画像をそのまま出力
        }
    }

    for (i=0; i<2; i++)
        free(line_buf[i]);
    free(line_buf);

    return 0;
}

// アンシャープマスキング・フィルタ
// x0y0 x1y0 x2y0 -k   -j  -k
// x0y1 x1y1 x2y1 -k  9+8k -k x 1/9
// x0y2 x1y2 x2y2 -k   -k  -k
//
// k : 鮮鋭化の強さ(固定小数点)
// num_adec_k : Kの小数点の位置
//
int unsharp_masking_soft(int pix_mat[3][3], int k, int num_adec_k)
{
    float y;
    int xy[3][3];
    float rgb[3];
    int result=0;

    float fk = (float)k;
    for (int m=0; m<num_adec_k; m++){ // 小数点の位置を正しい位置にする
        fk /= 2.0;
    }

    for (int i=0; i<=16; i += 8){
        for (int j=0; j<3; j++){
            for (int k=0; k<3; k++){
                xy[j][k] = (pix_mat[j][k] >> i) & 0xff; // RGBのいずれかを抽出
            }
        }
        y = -(float)xy[0][0]         -(float)xy[0][1]      -(float)xy[0][2]
            -(float)xy[1][0] +(9.0/fk+8.0)*(float)xy[1][1] -(float)xy[1][2]
            -(float)xy[2][0]         -(float)xy[2][1]      -(float)xy[2][2];
        y = (fk * y)/9.0;

        int z = (int)(y + 0.5); // 四捨五入

        if (z<0// 飽和演算
            z = 0;
        else if (z>255)
            z = 255;

        result += z<<i; // i=0 : blue, i=8 : green, i=16 : red
    }

    return(result);
}

  1. 2015年09月26日 20:22 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
http://marsee101.blog19.fc2.com/tb.php/3260-39708c37
この記事にトラックバックする(FC2ブログユーザー)