SSブログ

Vivado HLS を使ってみた(その2) [FPGA]

まずテストプログラムを作ってしまいます。ごく普通にC/C++で書いていきます。
(tab=4をスペースに置換してるので崩れてます)
I/Oポートのデータ幅を決めておきたいので、そこだけ特別な記述が入ります。

前の記事の通り、題材はRGB→YCbCr変換。実はVivado HLSには画像のためのパッケージも入っていて、RGBとかYCbCrなどの型がすでに宣言されていますが、今回は使わずに全部自分で書いてみます。

ヘッダ。「Source」を右クリックして「rgb2ycbcr.h」というファイル名で追加。
#define dw 8

#include <ap_int.h>

struct RGB
{
    ap_uint<dw>	R,G,B;
};

struct YCbCr
{
    ap_uint<dw>	Y,Cb,Cr;
};

YCbCr rgb2ycbcr(RGB s);
「ap_uint<n>」が「nビットの符号無し整数」という型で、これがI/Oのデータ幅になります。SystemCの「sc_uint<>」とだいたい同じでしょう。「rgb2ycbcr」がハードウェアになる関数です。
※これは後から気づいたことですが、dwはマクロじゃ無くてテンプレートで与えるべきでしょうね。
テストベンチ。「TestBench」を右クリックして「test01.cpp」というファイル名で追加。

#include <fstream>
#include <iostream>
#include <iomanip>
#include <cstdlib>
using namespace std;

#include "rgb2ycbcr.h"

#define dat    256        /*    pow(2, dw)    */

////////////////////////////
//
//    Y  = ( 0.2126R + 0.7152G + 0.0722B)
//    Cb = (-0.1146R - 0.3854G + 0.5000B) + 0.5
//    Cr = ( 0.5000R - 0.4542G - 0.0458B) + 0.5
//
YCbCr ref_rgb2ycbcr(RGB s)
{
    YCbCr    d;

    const float cY [3] = { 0.2126f,  0.7152f,  0.0722f };
    const float cCb[3] = {-0.1146f, -0.3854f,  0.5000f };
    const float cCr[3] = { 0.5000f, -0.4542f, -0.0458f };
    const float op5    = 0.5f * dat;
    const float llim   = 0.0f;
    const float ulim   = (float)(dat-1);

    float R,G,B;
    float Y,Cb,Cr;

    R = (float)s.R;
    G = (float)s.G;
    B = (float)s.B;

    Y  = cY [0] * R + cY [1] * G + cY [2] * B;
    Cb = cCb[0] * R + cCb[1] * G + cCb[2] * B + op5;
    Cr = cCr[0] * R + cCr[1] * G + cCr[2] * B + op5;

    if (Y  < llim) Y  = llim;    if (ulim < Y ) Y  = ulim;
    if (Cb < llim) Cb = llim;    if (ulim < Cb) Cb = ulim;
    if (Cb < llim) Cr = llim;    if (ulim < Cb) Cr = ulim;

    d.Y  = Y;
    d.Cb = Cb;
    d.Cr = Cr;

    return d;
}


////////////////////////////
//
//    R = Y                  + 1.5748(Cr-0.5)
//    G = Y - 0.1873(Cb-0.5) - 0.4681(Cr-0.5)
//    B = Y + 1.8556(Cb-0.5)
//
//    R = Y            + 1.5748Cr - 0.7874
//    G = Y - 0.1873Cb - 0.4681Cr + 0.3277
//    B = Y + 1.8556Cb            - 0.9278
//
RGB ref_ycbcr2rgb(YCbCr s)
{
    RGB d;

    const float cR[3] = { 0.0000f,  1.5748f, -0.7874f * dat };
    const float cG[3] = {-0.1873f, -0.4681f,  0.3277f * dat };
    const float cB[3] = { 1.8556f,  0.0000f, -0.9278f * dat };
    const float llim   = 0.0f;
    const float ulim   = (float)(dat-1);

    float R,G,B;
    float Y,Cb,Cr;

    Y  = (float)s.Y;
    Cb = (float)s.Cb;
    Cr = (float)s.Cr;

    R = Y              + cR[1] * Cr + cR[2];
    G = Y + cG[0] * Cb + cG[1] * Cr + cG[2];
    B = Y + cB[0] * Cb              + cB[2];

    if (R < llim) R = llim;    if (ulim < R) R = ulim;
    if (G < llim) G = llim;    if (ulim < G) G = ulim;
    if (B < llim) B = llim;    if (ulim < B) B = ulim;

    d.R = R;
    d.G = G;
    d.B = B;

    return d;
}

#define dpath "../../../../"
const char ifname[]= dpath "H-IIA-F13.bmp";
const char ofname[]= dpath "out.bmp";
const char ofnameRGB[]= dpath "outRGB.bmp";
const char ofnameY[]= dpath "outY.bmp";
const char ofnameCb[]= dpath "outCb.bmp";
const char ofnameCr[]= dpath "outCr.bmp";

#pragma pack(1)
struct BITMAPFILEHEADER {
    char            bfType[2];            //    "BM"
    unsigned int    bfSize;                //    File Size (Byte)
    unsigned short    bfReserved1;        //
    unsigned short    bfReserved2;        //
    unsigned int    bfOffBits;            //    Image Offset
};
#pragma pack(0)

#pragma pack(1)
struct BGR {
    unsigned char B,G,R;
};
#pragma pack(0)

int main()
{
    ifstream ifile;
    ofstream ofile;
    ofstream ofileRGB;
    ofstream ofileY;
    ofstream ofileCb;
    ofstream ofileCr;
    BITMAPFILEHEADER bf;
    int    imgsize;

    RGB        src;
    YCbCr    des;

    ifile.open(ifname, ios::binary);
    ofile.open(ofname, ios::binary);
    ofileRGB.open(ofnameRGB, ios::binary);
    ofileY.open(ofnameY, ios::binary);
    ofileCb.open(ofnameCb, ios::binary);
    ofileCr.open(ofnameCr, ios::binary);

    //    bf
    ifile.read((char*)&bf, sizeof(bf));

    cout << "bfType : " << bf.bfType[0] << bf.bfType[1] << endl;
    cout << "bfSize : " << bf.bfSize << endl;
    cout << "bfOffBits : " << bf.bfOffBits << endl;

    //    copy header
    ofile.write((char*)&bf, sizeof(bf));
    ofileRGB.write((char*)&bf, sizeof(bf));
    ofileY.write((char*)&bf, sizeof(bf));
    ofileCb.write((char*)&bf, sizeof(bf));
    ofileCr.write((char*)&bf, sizeof(bf));

    for (int i = sizeof(bf); i < bf.bfOffBits; i++)
    {
        char tch;
        ifile.read(&tch, sizeof(tch));
        ofile.write(&tch, sizeof(tch));
        ofileRGB.write(&tch, sizeof(tch));
        ofileY.write(&tch, sizeof(tch));
        ofileCb.write(&tch, sizeof(tch));
        ofileCr.write(&tch, sizeof(tch));
    }

    //    copy
    imgsize = bf.bfSize - bf.bfOffBits;

    for (int i = 0; i < imgsize; i+=3)
    {
        BGR ipx, opx;
        ifile.read((char*)&ipx, sizeof(ipx));
        src.R = ipx.R;
        src.G = ipx.G;
        src.B = ipx.B;
        des = ref_rgb2ycbcr(src);
//        des = rgb2ycbcr(src);
        opx.R = des.Y;
        opx.B = des.Y;
        opx.G = des.Y;
        ofileY.write((char*)&opx, sizeof(opx));
        opx.R = des.Cb;
        opx.B = des.Cb;
        opx.G = des.Cb;
        ofileCb.write((char*)&opx, sizeof(opx));
        opx.R = des.Cr;
        opx.B = des.Cr;
        opx.G = des.Cr;
        ofileCr.write((char*)&opx, sizeof(opx));
        opx.R = des.Cr;
        opx.B = des.Cb;
        opx.G = des.Y;
        ofile.write((char*)&opx, sizeof(opx));

        src = ref_ycbcr2rgb(des);
        opx.R = src.R;
        opx.G = src.G;
        opx.B = src.B;
        ofileRGB.write((char*)&opx, sizeof(opx));
    }

    ifile.close();
    ofile.close();
    ofileRGB.close();
    ofileY.close();
    ofileCb.close();
    ofileCr.close();
    cout << "done\n";
}

BMPファイルを読み込んで、変換して、BMPに保存します。実行パスが「.\rgb2ycbcr\solution1\csim\build\csim.exe」という深いところにできるので画像へのパスが長めに…(笑)
RGB→YCbCr変換はWikiに書いてある方法そのままです。ついでに画像を元に戻すYCbCr→RGBの逆変換関数も用意します。けど、なにも工夫してません。ちゃんと精度のこと考えてないので、正確でもありません。エラーチェックもろくしていない全くもっていい加減なプログラム。まぁだいたい見た目が似てればよいや、的な作りです。
後でmainでコメントアウトしているrgb2ycbcrとref_rgb2ycbcrを入れ替えてテストします。

とりあえずこのまま実行してみます。「Run C Simulation」をポチ。
Image 010.png
デフォルトのまま実行開始。
Image 011.png

こんな画像が出てきます。(※So-netブログはBMPダメ)。
左から、元画像、Y、Cb、Cr、BGR=CbYCrに置き換え、逆変換した画像
out.PNG

実行環境ができたので、ハードウェアになる対象の関数を記述していきます。

nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

Facebook コメント

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。