FC2カウンター FPGAの部屋 Vivado HLS 2015.4 で OpenCV を使ってみた2(テストベンチに Mat を使って実装した)

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

FPGAの部屋

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

Vivado HLS 2015.4 で OpenCV を使ってみた2(テストベンチに Mat を使って実装した)

Vivado HLS 2015.4 で OpenCV を使ってみた1”の続き。

前回のOpenCVコードの正解というか、書き方は、”Accelerating OpenCV Applications with Zynq-7000 All Programmable SoC using Vivado HLS Video Libraries”の英語資料に詳しく書いてあった。という訳で、前回のコードを書き換えた。
それとは別にテストベンチを OpenCV 2.X のMat フォーマットを使う書き方に変更したいという欲求があって、いろいろと試していたが、上手く行ったので、書いておく。
opencv_ex_ug_tb.cpp のソースコードを示す。

// opencv_ex_ug_tb.cpp
// 2016/04/02 by marsee
// OpenCV 2 の Mat を使用したバージョン

#include <iostream>
#include "hls_opencv.h"
#include "opencv_ex_ug.h"

using namespace cv;

#define INPUT_IMAGE            "test.bmp"
#define OUTPUT_IMAGE        "test_result.bmp"
#define OUTPUT_IMAGE_CV    "test_result_cv.bmp"

void image_filter(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM, int rows, int
cols);
void opencv_imaga_filter(Mat& src, Mat& dst);

int main (int argc, char** argv) {
    // OpenCV で 画像を読み込む
    Mat src = imread(INPUT_IMAGE);
    AXI_STREAM src_axi, dst_axi;

    // Mat フォーマットから AXI4 Stream へ変換
    cvMat2AXIvideo(src, src_axi);

    // image_filter() 関数をコール
    image_filter(src_axi, dst_axi, src.rows, src.cols);

    // AXI4 Stream から Mat フォーマットへ変換
    // dst は宣言時にサイズとカラー・フォーマットを定義する必要がある
    Mat dst(src.rows, src.cols, CV_8UC3);
    AXIvideo2cvMat(dst_axi, dst);

    // Mat フォーマットからファイルに書き込み
    imwrite(OUTPUT_IMAGE, dst);

    // opencv_image_filter() をコール
    Mat dst_cv(src.rows, src.cols, CV_8UC3);
    opencv_imaga_filter(src, dst_cv);
    imwrite(OUTPUT_IMAGE_CV, dst_cv);

    // dst と dst_cv が同じ画像かどうか?比較する
    for (int y=0; y<src.rows; y++){
        Vec3b* dst_ptr = dst.ptr<Vec3b>(y);
        Vec3b* dst_cv_ptr = dst_cv.ptr<Vec3b>(y);
        for (int x=0; x<src.cols; x++){
            Vec3b dst_bgr = dst_ptr[x];
            Vec3b dst_cv_bgr = dst_cv_ptr[x];

            // bgr のどれかが間違っていたらエラー
            if (dst_bgr[0] != dst_cv_bgr[0] || dst_bgr[1] != dst_cv_bgr[1] || dst_bgr[2] != dst_cv_bgr[2]){
                printf("x = %d, y = %d, Error\n", x, y);
                return 1;
            }
        }
    }
    printf("Test with 0 errors.\n");

    return 0;
}

void opencv_imaga_filter(Mat& src, Mat& dst){
    src.copyTo(dst); // 深いコピー
}


次に、opencv_ex_ug.c なのだが、これは、”Accelerating OpenCV Applications with Zynq-7000 All Programmable SoC using Vivado HLS Video Libraries”の 7 ページ下から、8 ページ上の image_filter() を参照している。しかし、最初はテストベンチが正常に比較しているかを確認するために、hls::SubS() を使用して、各画素から 50 を引いた値をAXI4 Stream で戻すようにした。その C シミュレーションの結果を下に示す。
Vivado_HLS_OpenCV_9_160402.png

きちんとエラーが出ている。テストベンチは問題無さそうだ。

元のtest.bmp と結果の test_result.bmp を並べて置く。左が test.bmp だ。(PNGファイルになっているが。。。)
Vivado_HLS_OpenCV_4_160401.pngVivado_HLS_OpenCV_10_160402.png

次に、コピーするだけの opencv_ex_ug.c に戻した。
opencv_ex_ug.c を下に示す。

// opencv_ex_ug.c
// 2016/04/02 by marsee
// "Accelerating OpenCV Applications with Zynq-7000 All Programmable SoC using Vivado HLS Video Libraries"
// の  7 ページ下から、8 ページ上の image_filter() を参照しています。
// http://japan.xilinx.com/support/documentation/application_notes/xapp1167.pdf

#include "opencv_ex_ug.h"

void image_filter(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM, int rows, int
cols) {
#pragma HLS INTERFACE ap_stable port=cols
#pragma HLS INTERFACE ap_stable port=rows
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis port=OUTPUT_STREAM
#pragma HLS INTERFACE axis port=INPUT_STREAM
#pragma HLS INTERFACE s_axilite port=cols
#pragma HLS INTERFACE s_axilite port=rows

    RGB_IMAGE img_0(rows, cols);

    hls::AXIvideo2Mat(INPUT_STREAM, img_0);
    hls::Mat2AXIvideo(img_0, OUTPUT_STREAM);
}


これで、C シミュレーションを行った。
Vivado_HLS_OpenCV_11_160402.png

正常に終了した。image_fiter() と opencv_image_filter() の画像は等しかった。

Cコードの合成を行った。
Vivado_HLS_OpenCV_12_160402.png

Target 10 ns に対して、Estimated は 4.38 ns だった。動作周波数に直すと、228 MHz だ。
Latency の min は 16、max は 964207 だった。 964207/(800 x 600) = 2 なので、2 クロックに 1 ピクセルの処理を行えるようだ。

次にリソース使用量を示す。
Vivado_HLS_OpenCV_13_160402.png

BRAM_18K、DSP48E の使用量は 0 だった。FF は 405、LUT は 558 だった。

C/RTL コシミュレーションを行ったが、2時間たっても終了しないので、諦めてキャンセルした。

次に、DATAFLOWディレクティブを入れてみた。DATAFLOWディレクティブを入れると関数を並列実行することができる。
Vivado_HLS_OpenCV_14_160402.png

C コードの合成を行った。
Vivado_HLS_OpenCV_15_160402.png

動作周波数はDATAFLOW ディレクティブなしと変化が無かった。
Latency の min は 16、max は 964207 だったところ、min が 4、max は 482403 になった。 482403 / (800 x 600) = 1 なので、1 クロックに 1 ピクセルの処理を行えるようだ。

リソース使用量を示す。左がDATAFLOW ディレクティブなしの時のリソース使用量、右がDATAFLOW ディレクティブありの時のリソース使用量を示す。
Vivado_HLS_OpenCV_13_160402.pngVivado_HLS_OpenCV_16_160402.png

FF は 1 減っている。LUT は 4 増えているが、リソース使用量に大した違いはない。

(2016/04/05:追記)
Intel プロセッサの早いパソコンでやったら C/RTL コシミュレーションができた。でもすぐに終わったので、家のパソコンでもできそうなものなんだが、RTL シミュレーションが終わらない。(家のパソコンはAMDプロセッサ)(DATAFLOW 指示子付き)
Vivado_HLS_OpenCV_31_160405.png

Latency が 3271 クロックだった。 3271 / (64 x 48) ピクセル = 1.06 位なので、1ピクセルが 1 クロックで動いているようだ。

C/RTL コシミュレーション波形を示す。
Vivado_HLS_OpenCV_32_160405.png

画像を1行処理するのに 680 ns だった。つまり、クロックは 100 MHz、10 ns なので、68 クロックということになる。
Vivado_HLS_OpenCV_33_160405.png
  1. 2016年04月02日 14:10 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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