FC2カウンター FPGAの部屋 Vivado HLS でRGB2HSV IPを作る4(C ソースコード)
fc2ブログ

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

FPGAの部屋

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

Vivado HLS でRGB2HSV IPを作る4(C ソースコード)

Vivado HLS でRGB2HSV IPを作る3(H の表現方法)”の続き。

今回は現在のC ソースコードを貼っておこうと思う。

まずは、rgb2hsv.h を貼っておく。

// rgb2hsv.h
// 2016/10/06 by marsee
//

#ifndef __RGB2HSV_H__
#define __RGB2HSV_H__

//#define HORIZONTAL_PIXEL_WIDTH    800
//#define VERTICAL_PIXEL_WIDTH    600

#define HORIZONTAL_PIXEL_WIDTH    640
#define VERTICAL_PIXEL_WIDTH    480

//#define HORIZONTAL_PIXEL_WIDTH    64
//#define VERTICAL_PIXEL_WIDTH    48

#define ALL_PIXEL_VALUE    (HORIZONTAL_PIXEL_WIDTH*VERTICAL_PIXEL_WIDTH)

#endif


次に、rgb2hsv.cpp を貼っておく。

// rgb2hsv.cpp
// 2016/10/06 by marsee
//

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

#include "rgb2hsv.h"

#define MAG 8 // 小数点以下のビット幅

int rgb2hsv(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs){
#pragma HLS INTERFACE axis port=outs
#pragma HLS INTERFACE axis port=ins
    ap_axis<32,1,1,1> pix;
    int r, g, b;
    int h, s, v;
    int max, min;
    int hsv;

    do{
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
    }while(pix.user == 0);

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

            b = pix.data & 0xff;
            g = (pix.data>>8) & 0xff;
            r = (pix.data>>16) & 0xff;

            // h と max, min を求める
            if(r==g && g==b && r==b){
                max = r; // 8倍
                min = r;
            }else if(r>=g && r>=b){ // r が最大
                max = r;
                if(g>=b)
                    min = b;
                else
                    min = g;
            }else if(g>=r && g>=b){ // g が最大
                max = g;
                if(r>=b)
                    min = b;
                else
                    min = r;
            }else// b が最大
                max = b;
                if(r>=g)
                    min = g;
                else
                    min = r;
            }
            if(max-min == 0)
                h = 0;
            else if(max == r)
                h = 60 * (((g-b)<<MAG)/(max-min)); // MAGビットシフトして精度を確保
            else if(max == g)
                h = 60 * (((b-r)<<MAG)/(max-min)) + (120<<MAG); // MAGビットシフトして精度を確保
            else // if(max == b)
                h = 60 * (((r-g)<<MAG)/(max-min)) + (240<<MAG); // MAGビットシフトして精度を確保

            if(h < 0)
                h += 360<<MAG;
            h += 1<<(MAG-1); // +0.5、四捨五入
            h >>= MAG; // 桁を戻す

            if(max == 0)
                s = 0;
            else
                s = (((max - min)<<MAG)/max) * 255// MAGビットシフトして精度を確保

            s += 1<<(MAG-1); // +0.5、四捨五入
            s >>= MAG; // 桁を戻す

            v = max;

            hsv = (h&0x1ff)*65536 + (s&0xff)*256 + (v&0xff);

            pix.data = hsv;

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

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

            outs << pix;
        }
    }

    return(0);
}


最後にテストベンチの rgb2hsv_tb.cpp を貼っておく。

// rgb2hsv_tb.cpp
// 2016/10/09
//

#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 <hls_video.h>

#include "rgb2hsv.h"
#include "bmp_header.h"

#define BMP_FILE_NAME   "road_1.bmp"
#define SQUARE_ERROR_LIMIT    4 // 2乗誤差のエラー限界、この数以上はエラーとする

#define H    0
#define S    1
#define V    2

int rgb2hsv(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs);
int rgb2hsv_soft(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs);
void WriteBmpFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, int *pixel_buf, int select_hsv);

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;

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

    if ((fbmpr = fopen(BMP_FILE_NAME, "rb")) == NULL){ // 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_hsv =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate hw_hsv memory\n");
        exit(1);
    }
    if ((sw_hsv =(int *)malloc(sizeof(int) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
        fprintf(stderr, "Can't allocate sw_hsv 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;
        ins_soft << pix;
    }

    for(int j=0; j < bmpihr.biHeight; j++){
        for(int 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;
        }
    }

    rgb2hsv(ins, outs);
    rgb2hsv_soft(ins_soft, outs_soft);

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

            red = (rd_bmp[(j*bmpihr.biWidth)+i]>>16) & 0xff;
            green = (rd_bmp[(j*bmpihr.biWidth)+i]>>8) & 0xff;
            blue = rd_bmp[(j*bmpihr.biWidth)+i] & 0xff;

            if ((double)pow((double)(val&0xff)-(val_soft&0xff),(double)2) > SQUARE_ERROR_LIMIT || // v の2乗誤差が4よりも大きい
                (double)pow((double)((val>>8)&0xff)-((val_soft>>8)&0xff),(double)2) > SQUARE_ERROR_LIMIT || // s の2乗誤差が4よりも大きい
                (double)pow((double)((val>>16)&0x1ff)-((val_soft>>16)&0x1ff),(double)2) > SQUARE_ERROR_LIMIT){ // h の2乗誤差が4よりも大きい
                printf("ERROR HW and SW results mismatch i = %ld, j = %ld, Red = %d, Green = %d, Blue = %d, "
                    "HW_h = %d, HW_s = %d, HW_v = %d, SW_h = %d, SW_s = %d, SW_v = %d\n",
                    i, j, red, green, blue, (int)(val>>16)&0xff, (int)(val>>8)&0xff, (int)val&0xff,
                    (int)(val_soft>>16)&0xff, (int)(val_soft>>8)&0xff, (int)val_soft&0xff);
                //return(1);
            }
            //if (vals.last)
                //cout << "AXI-Stream is end" << endl;
        }
    }
    cout << "Success HW and SW results match" << endl;
    cout << endl;

    if ((fbmpw=fopen("h_hw.bmp""wb")) == NULL){
        fprintf(stderr, "Can't open h_hw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, hw_hsv, H);

    if ((fbmpw=fopen("s_hw.bmp""wb")) == NULL){
        fprintf(stderr, "Can't open s_hw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, hw_hsv, S);

    if ((fbmpw=fopen("v_hw.bmp""wb")) == NULL){
        fprintf(stderr, "Can't open v_hw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, hw_hsv, V);

    if ((fbmpw=fopen("h_sw.bmp""wb")) == NULL){
        fprintf(stderr, "Can't open h_sw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, sw_hsv, H);

    if ((fbmpw=fopen("s_sw.bmp""wb")) == NULL){
        fprintf(stderr, "Can't open s_sw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, sw_hsv, S);

    if ((fbmpw=fopen("v_sw.bmp""wb")) == NULL){
        fprintf(stderr, "Can't open v_sw.bmp by binary write mode\n");
        exit(1);
    }
    WriteBmpFile(fbmpw, bmpfhr, bmpihr, sw_hsv, V);

    free(rd_bmp);
    free(hw_hsv);
    free(sw_hsv);


}

int rgb2hsv_soft(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;
    int r, g, b;
    float h, s;
    int v;
    int max, min;
    int hsv;

    do{
        ins >> pix;
    }while(pix.user == 0);

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

            b = pix.data & 0xff;
            g = (pix.data>>8) & 0xff;
            r = (pix.data>>16) & 0xff;

            // h と max, min を求める
            if(r==g && g==b && r==b){
                max = r;
                min = r;
            }else if(r>=g && r>=b){
                max = r;
                if(g>=b)
                    min = b;
                else
                    min = g;
            }else if(g>=r && g>=b){
                max = g;
                if(r>=b)
                    min = b;
                else
                    min = r;
            }else// b が最大
                max = b;
                if(r>=g)
                    min = g;
                else
                    min = r;
            }
            if(max-min == 0)
                h = 0.0;
            else if(max == r)
                h = 60.0 * ((float)(g-b)/(float)(max-min));
            else if(max == g)
                h = 60.0 * ((float)(b-r)/(float)(max-min)) + 120.0;
            else // if(max == b)
                h = 60.0 * ((float)(r-g)/(float)(max-min)) + 240.0;

            if(h < 0)
                h += 360.0;

            if(max == 0)
                s = 0.0;
            else
                s = (float)(max - min)/(float)max * 255.0;

            v = max;

            hsv = (((int)(h+0.5))&0x1ff)*65536 + (((int)(s+0.5))&0xff)*256 + (v&0xff); // h と s は四捨五入

            pix.data = hsv;

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

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

            outs << pix;
        }
    }

    return(0);
}

// SV のうちの1つをblue, green, redの値として同じ値をBMPファイルに書く
// H は S,V の値を255とした時の H の値に対する RGB の値を計算する
void WriteBmpFile(FILE *fbmpw, BITMAPFILEHEADER &bmpfhr, BITMAPINFOHEADER &bmpihr, int *pixel_buf, int select_hsv){
    int h;
    int sv;
    int r, g, b;

    // 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++){
            switch(select_hsv){
            case H:
                h = (pixel_buf[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0x1ff;
                if(h>=0 && h<60){
                    r = 255;
                    g = (int)(((float)h/60.0)*255.0+0.5);
                    b = 0;
                }else if(h>=60 && h<120){
                    r = (int)(((120.0-(float)h)/60.0)*255+0.5);
                    g = 255;
                    b = 0;
                }else if(h>=120 && h<180){
                    r = 0;
                    g = 255;
                    b = (int)((((float)h-120.0)/60.0)*255+0.5);
                }else if(h>=180 && h<240){
                    r = 0;
                    g = (int)(((240.0-(float)h)/60.0)*255+0.5);
                    b = 255;
                }else if(h>=240 && h<300){
                    r = (int)((((float)h-240.0)/60.0)*255+0.5);
                    g = 0;
                    b = 255;
                }else// h>=300 && h<=360
                    r = 255;
                    g = 0;
                    b = (int)(((360.0-(float)h)/60.0)*255+0.5);
                }
                break;
            case S:
                sv = (pixel_buf[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
                break;
            default// case V:
                sv = pixel_buf[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
                break;
            }

            if(select_hsv==S || select_hsv==V){
                fputc(sv, fbmpw);
                fputc(sv, fbmpw);
                fputc(sv, fbmpw);
            }else{
                fputc(b, fbmpw);
                fputc(g, fbmpw);
                fputc(r, fbmpw);
            }
        }
    }
    fclose(fbmpw);
}

  1. 2016年10月16日 06:12 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

トラックバック URL
https://marsee101.blog.fc2.com/tb.php/3604-f6cde3d7
この記事にトラックバックする(FC2ブログユーザー)