FC2カウンター FPGAの部屋 2016年04月

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

FPGAの部屋

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

AXI VDMAのMM2Sを使用してビデオ出力4(シミュレーション2)

AXI VDMAのMM2Sを使用してビデオ出力3(シミュレーション1)”の続き。

前回はシミュレーション時にエラーが出てしまった。今回はシミュレーション時のエラーを解消した。具体的には、rgb2dvi IP を削除した。

前回、シミュレーション時のエラーは rgb2dvi IP のみで発生しているので、それを削除しようということになったが、今回、結局 rgb2dvi IP を削除した。
そして、バグを発見した reset はアクティブ・ローだったので resetn に変更した。テストベンチもリセットがアクティブ・ハイの仕様になっていたので、2日間、回路が動作しなかった。orz...
発覚後、直ぐに修正。

また、reg_set_axi_lite_master IP のバージョンも古かった。”Vivado 2014.4でのVerilog HDLで記述したROM の初期化データの扱い”を参考にして新しい reg_set_axi_lite_master IP に入れ替えた。
新しいプロジェクトとV_ZYBO_CAMDS ブロックデザインを示す。
AXI_VDMA_MM2S_9_160430.png

(2016/05/02:変更) VTC の AXI4 Lite インターフェースの Slaveを削除した。

AXI_VDMA_MM2S_17_160502.png

AXI_VDMA_MM2S_11_160430.png

AXI_VDMA_MM2S_18_160502.png

シミュレーションを行った。
AXI_VDMA_MM2S_13_160430.png

reg_set_axi_lite_master がレジスタを設定しているのが見える。
AXI_VDMA_MM2S_14_160430.png

でも、まだ動いたばかりでAXI VDMA のアドレスと設定値がいい加減なので、設定値を決める必要がある。

(2016/05/01:追記)(2016/05/02:変更)
アドレスマップを追加しておく。
AXI_VDMA_MM2S_19_160502.png
  1. 2016年04月30日 06:40 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

AXI VDMAのMM2Sを使用してビデオ出力3(シミュレーション1)

AXI VDMAのMM2Sを使用してビデオ出力2(Vivadoのブロックデザイン)”の続き。

前回、AXI VMDA の S2MM と MM2S を両方使用したブロックデザインを作製した。今回は、そのブロックデザインをシミュレーションしようと思う。だが、Zynq のPS はプロセッサのモデルが無くてそのままではシミュレーションが出来ない。そこで、reg_set_axi_lite_master IP を使用する。このIP は言ってみれば、設定ファイル(vdma_reg_set.txt)の設定内容(2行構成で、最初の行がアドレス、次の行が書き込むデータの2行構成の組がある)をAXI4 Lite Master でIP のレジスに書き込むIP である。もう1つメモリのモデルの mem_sim_axi_slave を使用する。これは、汎用のメモリ・モデルだ。(詳しくは、”ソフトウェアのCソースコードをVivado HLS 2014.4 で高位合成したIPをシミュレーション”を参照のこと)

シミュレーションを行うために、前回のVivado プロジェクト・フォルダをコピーして、CAMDS_FASTXt2_VDMAt_161 というフォルダ名を作製した。
AXI_VDMA_MM2S_5_160428.png

PS の代わりに reg_set_axi_lite_master IP と mem_sim_axi_slave IP を追加した。ブロックデザインを示す。
AXI_VDMA_MM2S_6_160428.png

MT9D111 カメラ・モジュールのモデル mt9d111_model.v と シミュレーション用トップファイル V_ZYBO_CAMDS_tb.v を追加して、シミュレーションを行った。しかし、エラーが出てしまった。エラー内容を示す。

[USF-XSim 62] 'compile' step failed with error(s). Please check the Tcl console output or 'Z:/Sim/CAMDS_FASTXt2_VDMAt_161/V_ZYBO_CAMDS_151.sim/sim_1/behav/xvhdl.log' file for more information.


xvhdl.logを見ろと言われているので、見た。xvhdl.log のエラー内容を示す。

ERROR: [VRFC 10-149] 'resetbridge' is not compiled in library xil_defaultlib [Z:/Sim/CAMDS_FASTXt2_VDMAt_161/V_ZYBO_CAMDS_151.ip_user_files/bd/V_ZYBO_CAMDS/ipshared/digilentinc.com/rgb2dvi_v1_2/src/ClockGen.vhd:56]
ERROR: [VRFC 10-149] 'syncasync' is not compiled in library xil_defaultlib [Z:/Sim/CAMDS_FASTXt2_VDMAt_161/V_ZYBO_CAMDS_151.ip_user_files/bd/V_ZYBO_CAMDS/ipshared/digilentinc.com/rgb2dvi_v1_2/src/ClockGen.vhd:64]
ERROR: [VRFC 10-1504] unit behavioral ignored due to previous errors [Z:/Sim/CAMDS_FASTXt2_VDMAt_161/V_ZYBO_CAMDS_151.ip_user_files/bd/V_ZYBO_CAMDS/ipshared/digilentinc.com/rgb2dvi_v1_2/src/ClockGen.vhd:46]


rgb2dvi IP の中のファイルがエラーと言われている様だ。IP の再パッケージを行った。
エラーの行を見ると、いずれも component 文を使用しないで下位モジュールをインスタンスしている箇所がエラーになっているようだ。
AXI_VDMA_MM2S_7_160428.png

とりあえず、シミュレーションに rgb2dvi IP は必要無いので、これを削除しようと思う。
  1. 2016年04月28日 05:13 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:2

AXI VDMAのMM2Sを使用してビデオ出力2(Vivadoのブロックデザイン)

AXI VDMAのMM2Sを使用してビデオ出力1(構想編)”の続き。

前回、AXI VDMA のMM2S も使いたいという話をしたが、今回は、Vivado 2016.1 でプロジェクトを作成して、ブロックデザインを完成させた。

まずは、ZYBO 用のプロジェクトをコピーし、ペーストした。
AXI_VDMA_MM2S_1_160426.png

ブロックデザインを修正した。具体的には camera_module から AXI VDMA を分離して、MM2S を生かした。
AXI_VDMA_MM2S_2_160426.png

camera_module を示す。image_filter はFASTX コーナー検出IP だ。
AXI_VDMA_MM2S_3_160426.png

video_module を示す。rgb2dvi IP, rgb2vga IP は Digilent 社の出しているIPだ。Digilent Vivado library の中の一部だ。
AXI_VDMA_MM2S_4_160426.png

なお、Slice と Concat だが、これは、私の作った AXI4-Stream 対応カメラ・インターフェース ( mt9d111_inf_axis ) のAXI4-Stream の32ビット幅データのバイトレーンの割当は、Dummy, Red, Green, Blue である。ところが、Xilinx 社のビデオIP のデータのバイトレーンの割当は Red, Blue, Green になっているので、それを変換するためである。(AXI4-Stream to Video Out v4.0 LogiCORE IP Product Guide Vivado Design Suite PG044 November 18, 2015 を参照のこと)

ブロックデザインはできたが、論理合成するには早過ぎるので、シミュレーションをしたいのだが、PSがあるとシミュレーションが出来ないので、違うプロジェクトでシミュレーションを行っていこうと思う。
  1. 2016年04月26日 22:50 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

AXI VDMAのMM2Sを使用してビデオ出力1(構想編)

FASTX コーナー検出の改良3(threshold をソフトウェアで変更可能にする)”でリアルタイムでFASTX コーナー検出をすることができた。現在のMT9D111 カメラのフレームレートは 800 x 600 の 15 fps なのだが、AXI VDMA IPでカメラ画像のAXI4-Stream から DMA する時に 3 つ以上のフレームバッファの時にしかDMA されない。ビデオ出力は私の作ったbitmap_disp_cntrler_axi_master で出力しているのだが、それは 1 つのフレームバッファにしか対応していない。よって、AXI VDMA IP で書かれた 3 つのフレームバッファの内の最初の 1 つをbitmap_disp_cntrler_axi_master で表示している。したがって、フレームレートが 1/3 になってしまう。
今回は、今まで S2MM のみ使用したAXI VDMA のMM2S も使って、ビデオ出力をしようと思う。

AXI VDMA のMM2S 出力のAXI4-Stream をビデオ信号にするためには、Xilinx社のIP の AXI4-Stream to Video Out (v_axi4_vid_out)、Video Timing Controller (vtc) が使えると思う。
これは、以前やったとこがある。”ビデオ信号にAXI4 Stream版のラプラシアンフィルタを通して画像出力3(Vivadoプロジェクト)”が参考になるはずだ。
そのブロック図を示す。前述した2つのIP が使用されている。
dvi2lap2vga_33_150905.png

Xilinx社のAXI4-Stream to Video Out の資料ページ
FPGAの部屋の記事、”Video Timing Controller (VTC)
Xilinx 社のVideo Timing Controller (ビデオ タイミング コントローラー)の資料ページ

ビデオ出力は、。”ビデオ信号にAXI4 Stream版のラプラシアンフィルタを通して画像出力3(Vivadoプロジェクト)”で使っている rgb2vga や rgb2dvi を使うことにしよう。これらは、Digilent 社のGitHubの Digilent Vivado library からダウンロードすることができる。
  1. 2016年04月25日 04:50 |
  2. ZYBO
  3. | トラックバック:0
  4. | コメント:0

FASTX コーナー検出の改良3(threshold をソフトウェアで変更可能にする)

FASTX コーナー検出の改良2(threshold をソフトウェアで変更可能にする)”の続き。

前回は、Vivado HLS 2016.1 でC コードの合成とIP へのパッケージを行った。今回は、その threshold を変更できるFASTX コーナー検出IP をカメラ表示システムに組み込んで、ZYBO にダウンロードしてやってみた。

threshold を変更できるFASTX コーナー検出IP を組み込んだ、Vivado 2016.1 のプロジェクトを示す。もう、ビットストリームの生成まで済んでいる。
Vivado_HLS_OpenCV_123_160423.png

Summary を示す。タイミング制約も満足している。
Vivado_HLS_OpenCV_124_160423.png

ハードウェアをエクスポートして、SDKを立ち上げた。
Vivado_HLS_OpenCV_125_160423.png

fastx_on_serial.c を作製した。下にソースコードを示す。

// fastx_on_serial.c
// 2016/04/13 by marsee
//
// Refered to http://japan.xilinx.com/support/documentation/sw_manuals_j/xilinx2014_4/ug902-vivado-high-level-synthesis.pdf
//
// 2016/04/21 : threshold をシリアル経由で入力する
//

#include <stdio.h>
#include "ximage_filter.h"
#include "xparameters.h"

int main(){
    XImage_filter Ximage_filter;
    XImage_filter_Config *Ximage_filterPtr;
    int threshold;

    // Look Up the device configuration
    Ximage_filterPtr = XImage_filter_LookupConfig(0);
    if (!Ximage_filterPtr){
        fprintf(stderr, "XImage_filter configuration failed.\n");
        return(-1);
    }

    // Initialize the Device
    int Xlap_status = XImage_filter_CfgInitialize(&Ximage_filter, Ximage_filterPtr);
    if (Xlap_status != XST_SUCCESS){
        fprintf(stderr, "Could not Initialize XImage_filter\n");
        return(-1);
    }

    // image_filter rows, cols set
    XImage_filter_Set_cols(&Ximage_filter, (u32)800);
    XImage_filter_Set_rows(&Ximage_filter, (u32)600);

    // first shreshold set
    printf("\n\rfirst threshold = ");
    scanf("%d", &threshold);
    XImage_filter_Set_threshold(&Ximage_filter, (u32)threshold);

    // axis_switch_1, 1to2 ,Select M01_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x40), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x44), 0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers

    // fastx filter AXIS Start
    XImage_filter_Start(&Ximage_filter);
    XImage_filter_EnableAutoRestart(&Ximage_filter);

    // axis_switch_0, 2to1, Select S01_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR+0x40), 0x1);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR), 0x2); // Commit registers

    // threshold set
    while(1){
        printf("\n\rthreshold = ");
        scanf("%d", &threshold);
        if (threshold == 999)
            break;
        
        XImage_filter_Set_threshold(&Ximage_filter, (u32)threshold);
    }

    return(0);
}


Tera Term を起動して、ZYBO とシリアル経由で接続した。シリアルの設定は 115200 bps, 8 bits, 1 stop bit, local echo にした。
Vivado_HLS_OpenCV_126_160423.png

いろいろと threshold を変更してやってみた。999 を入力するとアプリケーションが終了する。

threshold = 2 の場合の画像を下に示す。
Vivado_HLS_OpenCV_127_160423.jpg

threshold = 5 の場合
Vivado_HLS_OpenCV_128_160423.jpg

threshold = 10 の場合
Vivado_HLS_OpenCV_129_160423.jpg

threshold = 20 の場合
Vivado_HLS_OpenCV_130_160423.jpg

threshold = 40 の場合
Vivado_HLS_OpenCV_131_160423.jpg

threshold = 60 の場合
Vivado_HLS_OpenCV_132_160423.jpg

threshold = 80 の場合
Vivado_HLS_OpenCV_133_160423.jpg

このように、FASTX コーナー検出を threshold を変更して、リアルタイムに 行うことができた。但し、残念だが今のところのカメラの fps は 5 fps だ。回路を変更して、15 fps にしたいと思う。

FASTX コーナー検出IP は 280 MHz まで動作するというレポートが出ているので、HD 解像度の 148.5 MHz も問題なくできると思う。そのようにすればHD解像度で、60 fps のFASTX コーナー検出ができると思う。
  1. 2016年04月23日 04:31 |
  2. OpenCV
  3. | トラックバック:0
  4. | コメント:0

FASTX コーナー検出の改良2(threshold をソフトウェアで変更可能にする)

FASTX コーナー検出の改良1(threshold をソフトウェアで変更可能にする)”の続き。

前回は、threshold をソフトウェアで変更可能にして、いろいろなthreshold の値でC シミュレーションを行った。今回はC コードの合成、IPへのパッケージを行う。

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

Latency、Interval は前回の threshold を 20 に固定した時と変化はない。

リソース使用量を示す。左が今回のリソース使用量で、右が前回の threshold を 20 に固定した時のリソース使用量だ。
Vivado_HLS_OpenCV_121_160422.pngVivado_HLS_OpenCV_102_160418.png

FFとLUT が今回の方が増えているのが分かる。

threshold のレジスタを確認した。

//------------------------Address Info-------------------
// 0x00 : Control signals
//        bit 0  - ap_start (Read/Write/COH)
//        bit 1  - ap_done (Read/COR)
//        bit 2  - ap_idle (Read)
//        bit 3  - ap_ready (Read)
//        bit 7  - auto_restart (Read/Write)
//        others - reserved
// 0x04 : Global Interrupt Enable Register
//        bit 0  - Global Interrupt Enable (Read/Write)
//        others - reserved
// 0x08 : IP Interrupt Enable Register (Read/Write)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x0c : IP Interrupt Status Register (Read/TOW)
//        bit 0  - Channel 0 (ap_done)
//        bit 1  - Channel 1 (ap_ready)
//        others - reserved
// 0x10 : Data signal of rows
//        bit 31~0 - rows[31:0] (Read/Write)
// 0x14 : reserved
// 0x18 : Data signal of cols
//        bit 31~0 - cols[31:0] (Read/Write)
// 0x1c : reserved
// 0x20 : Data signal of threshold
//        bit 31~0 - threshold[31:0] (Read/Write)
// 0x24 : reserved
// (SC = Self Clear, COR = Clear on Read, TOW = Toggle on Write, COH = Clear on Handshake)

//------------------------Parameter----------------------


0x20 番地に threshold レジスタが追加されていた。

IP へのパッケージを行った。
IP が生成された。
Vivado_HLS_OpenCV_122_160422.png
  1. 2016年04月22日 04:25 |
  2. OpenCV
  3. | トラックバック:0
  4. | コメント:0

FASTX コーナー検出の改良1(threshold をソフトウェアで変更可能にする)

FASTXコーナー検出IPのカメラ表示システム4(Vivado HLS 2016.1でやってみた2)
FASTXコーナー検出IPのカメラ表示システム5(Vivado HLS 2016.1でやってみた3)
で出来上がったFASTX コーナー検出だが、Xilinx Wiki の HLS FASTX の threshold は 20 に固定したままだ。そこで、threshold を変更できるようにしてみた。
具体的には、opencv_ex_ug.cpp の image_filter() に int threshold を追加した。

void image_filter(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM, int rows, int cols, int threshold)


opencv_ex_ug.cpp を貼っておく。

// opencv_ex_ug.cpp
// 2016/04/02 by marsee
// 2016/04/09 : FAST Corners Detection

#include "opencv_ex_ug.h"

void image_filter(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM, int rows, int cols, int threshold) {
#pragma HLS INTERFACE ap_stable port=threshold
#pragma HLS INTERFACE s_axilite port=threshold
#pragma HLS DATAFLOW
#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);
    RGB_IMAGE img_1(rows, cols);
    RGB_IMAGE img_1_(rows, cols);
#pragma HLS STREAM variable=img_1_.data_stream depth=8192
    // FASTX に最大 7 ラインのレイテンシ、Dilate に最大 3 ラインのレイテンシがあるそうだ
    // 1ラインのピクセル数X10 ラインのFIFO バッファが必要 800x10 < 8192 (2の13乗)
    // http://japan.xilinx.com/support/documentation/application_notes/xapp1167.pdf
    // の 10 ページ参照

    GRAY_IMAGE img_1g(rows, cols);
    GRAY_IMAGE mask(rows, cols);
    GRAY_IMAGE dmask(rows, cols);
    GRAY_IMAGE img_2g(rows, cols);
    RGB_IMAGE img_3(rows, cols);
    RGB_PIXEL color(25500);

    hls::AXIvideo2Mat(INPUT_STREAM, img_0);
    hls::Duplicate(img_0, img_1, img_1_);
    hls::CvtColor<HLS_BGR2GRAY>(img_1, img_1g);
    hls::FASTX(img_1g, mask, threshold, true);
    hls::Dilate(mask, dmask);
    hls::PaintMask(img_1_, dmask, img_3, color);
    hls::Mat2AXIvideo(img_3, OUTPUT_STREAM);
}


opencv_ex_ug.h を貼っておく。

// 2016/04/03 : GRAY_IMAGE を追加

#ifndef __opencv_ex_ug_H__
#define __opencv_ex_ug_H__

#include "ap_axi_sdata.h"
#include "hls_video.h"

#define MAX_HEIGHT    600
#define MAX_WIDTH    800

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef hls::Scalar<3unsigned char> RGB_PIXEL;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1> GRAY_IMAGE;
#endif


opencv_ex_ug_tb.cpp を貼っておく。

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

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

using namespace cv;

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

#define THESHOLD_LEVEL    60

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

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, THESHOLD_LEVEL);

    // 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, THESHOLD_LEVEL);
    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 dst=%d,%d,%d dst_cv=%d,%d,%d\n", x, y,
                        dst_bgr[0], dst_bgr[1], dst_bgr[0], dst_cv_bgr[0], dst_cv_bgr[1], dst_cv_bgr[2]);
                //return 1;
            }
        }
    }
    printf("Test with 0 errors.\n");

    return 0;
}

void opencv_imaga_filter(Mat& src, Mat& dst, int threshold){
     src.copyTo(dst); // 深いコピー
     std::vector<Mat> layers;
     std::vector<KeyPoint> keypoints;
     split(src, layers);
     FAST(layers[0], keypoints, threshold, true);
     for (int i = 0; i < keypoints.size(); i++) {
         rectangle(dst,
                 Point(keypoints[i].pt.x-1, keypoints[i].pt.y-1),
                 Point(keypoints[i].pt.x+1, keypoints[i].pt.y+1),
                 Scalar(255,0), CV_FILLED);
     }
}


Vivado HLS 2016.1 のプロジェクトで試した。
Vivado_HLS_OpenCV_114_160421.png

C シミュレーションを行った。今回は threshold を 10, 20, 40, 60, 80 に変更しながら、C シミュレーションを行った。
まずは、threshold = 10 の時の test_result.jpg を示す。
Vivado_HLS_OpenCV_115_160421.jpg

threshold = 20 の時の test_result.jpg を示す。
Vivado_HLS_OpenCV_116_160421.jpg

threshold = 40 の時の test_result.jpg を示す。
Vivado_HLS_OpenCV_117_160421.jpg

threshold = 60 の時の test_result.jpg を示す。
Vivado_HLS_OpenCV_118_160421.jpg

threshold = 80 の時の test_result.jpg を示す。
Vivado_HLS_OpenCV_119_160421.jpg

threshold が大きくなるに従って、青い点が減っていくのが分かった。
  1. 2016年04月21日 04:51 |
  2. OpenCV
  3. | トラックバック:0
  4. | コメント:0

FASTXコーナー検出IPのカメラ表示システム5(Vivado HLS 2016.1でやってみた3)

FASTXコーナー検出IPのカメラ表示システム4(Vivado HLS 2016.1でやってみた2)”の続き。

前回は、hls::Duplicate() で複製された img_1_ の data_stream に hls::FASTX() と hls::Dilate() の最大レイテンシの 7 行と 3 行を足した 10 行分より多い 2 の 13 乗の 8196 個の FIFO バッファを付けるように STREAM ディレクティブを指定した。
今回は、前回作製した FASTX コーナー検出IP を使用して、Vivado 2016.1 で論理合成、インプリメントを行って、SDKでZYBO にコンフィグレーションしアプリケーションを起動して、 FASTX コーナー検出できるかどうかを確かめた。

まずは、Vivado HLS 2016.1 で IP としてパッケージした C:\Users\Masaaki\Documents\Vivado_HLS\ZYBO\test\opencv_ex_ug2_161\solution1\impl\ip\xilinx_com_hls_image_filter_1_0.zip を展開して、Z:\V_ZYBO_CAMDS_FASTX_161\FASTX_OpenCV_154 に貼り付け直した。
Vivado_HLS_OpenCV_107_160420.png

Vivado 2016.1 の V_ZYBO_CAMDS ブロックデザインを示す。
Vivado_HLS_OpenCV_112_160420.png

camera_interface サブモジュールを示す。image_filter が FASTX コーナー検出IP だ。
Vivado_HLS_OpenCV_113_160420.png

論理合成、インプリメント、ビットストリーム生成後のVivado を示す。
Vivado_HLS_OpenCV_108_160420.png

Summary を示す。
Vivado_HLS_OpenCV_109_160420.png

ハードウェアをエクスポートして、SDK を立ち上げた。
Vivado_HLS_OpenCV_110_160420.png

Program FPGA を行って、cam_disp.elf を右クリックメニューの Run As -> 4 Launch on Hardware (GDB) を選択して、ZYBO 上で実行するとカメラ画像が表示された。
fast_on.elf を右クリックメニューの Run As -> 4 Launch on Hardware (GDB) を選択して、ZYBO 上で実行すると青い点でFASTX コーナー検出された。できました~。。。
Vivado_HLS_OpenCV_111_160420.jpg

特にスタジャンのエンブレムの所に青い点が集まっている。カメラの画像がリアルタイムにFASTX コーナー検出された。
  1. 2016年04月20日 03:57 |
  2. OpenCV
  3. | トラックバック:0
  4. | コメント:0

FASTXコーナー検出IPのカメラ表示システム4(Vivado HLS 2016.1でやってみた2)

”FASTXコーナー検出IPのカメラ表示システム3(Vivado HLS 2016.1でやってみた1)”の続き。

Accelerating OpenCV Applications with Zynq-7000 All Programmable SoC using Vivado HLS Video Libraries, XAPP1167 (v3.0) June 24, 2015”の 10 ページを見ると、FASTX のレイテンシが最大 7 lines で、Dilate レイテンシが最大 3 lines なので、10 ライン分のFIFO バッファを入れる必要があったようだ。今回の実装では、カメラの画像が 800 ピクセル / line なので、800 ピクセル X 10 lines = 8000 以上のFIFO バッファが必要になるようだ。これを忘れていたので、実装しよう。なお、FIFO バッファ数は8000 以上の 2 の n 乗の数を選定した。8196 で 2 の 13 乗だ。挿入するディレクティブは、

#pragma HLS STREAM variable=img_1_.data_stream depth=8192

になった。

opencv_ex_ug.cpp を貼っておく。

// opencv_ex_ug.cpp
// 2016/04/02 by marsee
// 2016/04/09 : FAST Corners Detection

#include "opencv_ex_ug.h"

void image_filter(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM, int rows, int
cols) {
#pragma HLS DATAFLOW
#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);
    RGB_IMAGE img_1(rows, cols);
    RGB_IMAGE img_1_(rows, cols);
#pragma HLS STREAM variable=img_1_.data_stream depth=8192
    // FASTX に最大 7 ラインのレイテンシ、Dilate に最大 3 ラインのレイテンシがあるそうだ
    // 1ラインのピクセル数X10 ラインのFIFO バッファが必要 800x10 < 8192 (2の13乗)
    // http://japan.xilinx.com/support/documentation/application_notes/xapp1167.pdf
    // の 10 ページ参照

    GRAY_IMAGE img_1g(rows, cols);
    GRAY_IMAGE mask(rows, cols);
    GRAY_IMAGE dmask(rows, cols);
    GRAY_IMAGE img_2g(rows, cols);
    RGB_IMAGE img_3(rows, cols);
    RGB_PIXEL color(25500);

    hls::AXIvideo2Mat(INPUT_STREAM, img_0);
    hls::Duplicate(img_0, img_1, img_1_);
    hls::CvtColor<HLS_BGR2GRAY>(img_1, img_1g);
    hls::FASTX(img_1g, mask, 20true);
    hls::Dilate(mask, dmask);
    hls::PaintMask(img_1_, dmask, img_3, color);
    hls::Mat2AXIvideo(img_3, OUTPUT_STREAM);
}


C シミュレーションを行った。これは結果は前と同じだ。
Vivado_HLS_OpenCV_100_160418.png

C コードからの合成を行った。
Vivado_HLS_OpenCV_101_160418.png

Latency は変化が無い。

リソース使用量を示す。やはり、FIFO 分の 12 個の BRAM_18K が増えている。
Vivado_HLS_OpenCV_102_160418.png

C/RTL コシミュレーションが動作した。Latency は 32700 クロックだった。
Vivado_HLS_OpenCV_103_160418.png

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

1ライン分拡大してみた。
Vivado_HLS_OpenCV_105_160418.png

1クロックで1ピクセル処理ができているようだ。

IP のパッケージを行った。IP ができた。
Vivado_HLS_OpenCV_106_160419.png
  1. 2016年04月19日 05:04 |
  2. OpenCV
  3. | トラックバック:0
  4. | コメント:0

OpenCV の X軸方向 Sobel フィルタ IP のカメラ表示システム

Vivado HLS 2016.1 で OpenCV の Sobel フィルタを試してみた”で作った X軸方向 Sobel フィルタの IP をカメラ表示システムに入れて、Sobel フィルタ出力が表示されるかどうか?を調べてみた。これが表示されれば、 hls::AXIvideo2Mat() や hls::Mat2AXIvideo() の動作は正しく、FASTX が問題なのでは?という可能性が高まる。

FASTXコーナー検出IPのカメラ表示システム1(プロジェクト作成)”のブロックデザインで、FASTX の IP の代わりに、X軸方向 Sobel フィルタの IP を入れた。
FASTXコーナー検出IP も Sobel フィルタの IP も名前は同じ image_filter なので、ブロックデザインは全く見た目が一緒だ。
これを論理合成、インプリメントを行った。結果を示す。
Vivado_HLS_OpenCV_95_160417.png

Vivado_HLS_OpenCV_96_160417.png

やはり、FASTXよりもX軸方向 Sobel フィルタの方がリソース使用量が少ない。

ハードウェアをエクスポートして、SDKを立ち上げた。
cam_disp, sobel_on, cam_return を作りなおした。
Vivado_HLS_OpenCV_97_160417.png

cam_disp_axis.elf を起動すると、カメラの画像が表示された。
Vivado_HLS_OpenCV_98_160417.jpg

sobel_on.elf を起動すると、リアルタイムに X軸方向 Sobel フィルタ処理をすることができた。
Vivado_HLS_OpenCV_99_160417.jpg

やはり、Vivado HLS 2016.1 のHLS ビデオライブラリの動作は問題ないようだ。
全く同じブロックデザイン構成で、FASTXは画像が表示されなかった。問題はFASTX かもしくは、FASTX でしか使っていないライブラリのどれかにバグがあるのかもしれない?

最後に sobel_on.c を貼っておく。

// sobel_fil_on.c
// 2016/04/13 by marsee
//
// Refered to http://japan.xilinx.com/support/documentation/sw_manuals_j/xilinx2014_4/ug902-vivado-high-level-synthesis.pdf
//

#include <stdio.h>
#include "ximage_filter.h"
#include "xparameters.h"

int main(){
    XImage_filter Ximage_filter;
    XImage_filter_Config *Ximage_filterPtr;

    // Look Up the device configuration
    Ximage_filterPtr = XImage_filter_LookupConfig(0);
    if (!Ximage_filterPtr){
        fprintf(stderr, "XImage_filter configuration failed.\n");
        return(-1);
    }

    // Initialize the Device
    int Xlap_status = XImage_filter_CfgInitialize(&Ximage_filter, Ximage_filterPtr);
    if (Xlap_status != XST_SUCCESS){
        fprintf(stderr, "Could not Initialize XImage_filter\n");
        return(-1);
    }

    // image_filter rows, cols set
    XImage_filter_Set_rows(&Ximage_filter, (u32)600);
    XImage_filter_Set_cols(&Ximage_filter, (u32)800);

    // axis_switch_1, 1to2 ,Select M01_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x40), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x44), 0);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers

    // sobel filter AXIS Start
    XImage_filter_Start(&Ximage_filter);
    XImage_filter_EnableAutoRestart(&Ximage_filter);

    // axis_switch_0, 2to1, Select S01_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR+0x40), 0x1);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR), 0x2); // Commit registers

    return(0);
}

  1. 2016年04月18日 04:26 |
  2. OpenCV
  3. | トラックバック:0
  4. | コメント:0

かすみがうらマラソンに出場しました

今日はかすみがうらマラソンに出場しました。

私は5km に出場して、奥さんは 10 マイル(16 km)に出場しました。

朝起きると、凄い風で、なんかマラソン大会に出るのが嫌になってきましたが、朝7時ころ家を出発して臨時駐車場へ行きました。
臨時駐車場からは、バスが会場まで運んでいってくれます。85年の万博の時に作った土浦駅の高架橋があるんですが、その途中でいつも降ろしてくれます。後は徒歩で会場まで行っていますが、途中でいろいろと出店が出ているんですよ。帰りにいろいろと買って帰りました。

かすみがうらマラソンは土浦市の川口運動公園で行われます。いつもお世話になっている編みアスリートクラブでは、ブースサポートをしていただいています。J:COMのブースです。
Kasumigaura_marathon_2_160418.jpg

これが周りの様子です。人がいっぱいいます。2万3千人くらいの参加人数だそうです。
Kasumigaura_marathon_1_160418.jpg

川口運動公園の道路はランニング関連のショップの出店が出ていますよ。
Kasumigaura_marathon_3_160418.jpg

県道 263 号線がスタート地点です。それに向かって歩いて行くと、船が係留されています。
Kasumigaura_marathon_4_160418.jpg

写真奥に見えているのが、スタート地点です。たくさん人がいます。
Kasumigaura_marathon_5_160418.jpg

5km のスタートは10時30分でした。マラソンは10時、奥さんの出た 10 マイルは9時20分スタートでした。

去年は腰が痛くて、かすみがうらマラソンはデフォルトしたのですが、今年は足が痛くて出られないかな?と思っていたのですが、鍼とマッサージでどうにか足も良くなってきて、出られてとても嬉しかったです。という訳で参加することが楽しいのですよ。。。

5km はかなり後ろの方からスタートしたので、最初のタイムが遅く、しかも抜いていくのが大変でした。花粉症があるので、マスクをしながら走りました。走っているとマスクが濡れて走りにくくなるので、交換しながら走りました。2枚交換しましたね。最後の500mは苦しくなったので、マスクを外して走りました。風も強かったですね。最初は追い風で良かったんですが、当然ながら、帰り道は向かい風で、苦しかったです。
ともかく、走れて良かったです。30分を切れなかったのは残念ですが、5km 走れたので満足です。
Kasumigaura_marathon_6_160418.jpg

奥さんは、10 マイルを1時間23分43秒で走ったそうです。自己ベストが出たそうです。

今度は、つくばマラソンの 10km に出てみたいですね。10km 以上走る練習をしようと思います。
  1. 2016年04月17日 20:42 |
  2. 日記
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2016.1 で OpenCV の Sobel フィルタを試してみた

Vivado HLS 2016.1 が出たので、
Vivado HLS 2015.4 で OpenCV を使ってみた3(Sobelフィルタを試した1)
Vivado HLS 2015.4 で OpenCV を使ってみた4(Sobelフィルタを試した2)
Vivado HLS 2015.4 で OpenCV を使ってみた5(Sobelフィルタを試した3)
Vivado HLS 2015.4 で OpenCV を使ってみた6(Sobelフィルタを試した4)
でやってみた Sobel フィルタをVivado 2016.1 でやってみた。

X軸方向のSobel フィルタをVivado 2016.1 で試してみた。まずはC シミュレーション結果を示す。
Vivado_HLS_OpenCV_86_160417.png

C シミュレーションの結果はVivado 2015.4 と同じのようだ。

C コードの合成結果を示す。
Vivado_HLS_OpenCV_87_160417.png

Target が 10 (ns) でも、Estimated は 9.40 (ns) でタイミング制約を満足した。Vivado 2015.4 ではタイミングを満足していないので、良くなった。

リソース使用量を示す。
Vivado_HLS_OpenCV_88_160417.png

LUT使用率は 8 % 程度だった。LUT使用率は、Vivado 2015.4 よりも多くなった

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

C/RTL コシミュレーション波形全体を示す。Vivado 2016.1 では、波形のカテゴリごとにグループ分けされている。
Vivado_HLS_OpenCV_90_160417.png

AXI4-Lite インターフェースの設定付近を拡大した。
Vivado_HLS_OpenCV_91_160417.png

最初に 0x10 番地の行に値 (十進数で 146 )をセットしている。(WDATA は Unsigned Decimal に設定してある)
次に、0x18 番地の列に値(十進数で 189)をセットしている。
その後、0x00 番地に 1 を書いてスタートしている。

X軸方向Sobel フィルタIP のIP化を行った。
Vivado_HLS_OpenCV_92_160417.png

次に、Target を 2.5 (ns) にして、C コードの合成を行った。限界を探るためだ。
Vivado_HLS_OpenCV_93_160417.png

3.44 (ns) で限界のようだ。 1 / 3.44 (ns) ≒ 291 MHz で動作するという予測のようだ。

その時のリソース使用量を示す。
Vivado_HLS_OpenCV_94_160417.png
  1. 2016年04月17日 04:31 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

FASTXコーナー検出IPのカメラ表示システム3(Vivado HLS 2016.1でやってみた1)

FASTXコーナー検出IPのカメラ表示システム2(論理合成、インプリメント)”の続き。

一昨日、Vivado 2016.1 が出たので、FASTX コーナー検出IP をVivado HLS 2016.1 で再度やってみることにした。

2016/04/20 追記:FASTX コーナー検出IP が動作しなかったのは、hls::Duplicate() で複製された img_1_ の data_stream に hls::FASTX() と hls::Dilate() の最大レイテンシの 7 行と 3 行を足した 10 行分より多い 2 の 13 乗の 8196 個の FIFO バッファを付けるように STREAM ディレクティブを指定する必要があったからでした。それを行ったところ、”FASTXコーナー検出IPのカメラ表示システム5(Vivado HLS 2016.1でやってみた3)”で動作しました)

最初にC シミュレーションをやってみた。
Vivado_HLS_OpenCV_80_160416.png

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

Clock Period が 10 (ns) のところ、Estimated が 9.40 (ns) でタイミング制約を満足した。
Vivado_HLS_OpenCV_81_160416.png

Latency も max が 498913 で一番レイテンシが短い。
Vivado_HLS_OpenCV_82_160416.png

C/RTL コシミュレーションは相変わらず8時間やっても完了しないが、RTLシミュレーションでレポートが出るようになった。(今やってみたら Fail で落ちてしまった。まだ不安定なのか?)
Vivado_HLS_OpenCV_83_160416.png

IP 化を行った。
Vivado_HLS_OpenCV_84_160416.png

これでVivado 2016.1でプロジェクトを変換してもう一度、FASTX コーナー検出ができるかどうかを確かめてみよう。

(追記)
FASTXコーナー検出IPのカメラ表示システム1(プロジェクト作成)”、”FASTXコーナー検出IPのカメラ表示システム2(論理合成、インプリメント)”のプロジェクトに入れて、論理合成、インプリメントして、SDKを起動してZYBOの実機で試してみましたが、同様に画像が更新されませんでした。

Vivado HLS のOpenCV で作ったIP 全体が動作しない可能性があるので、Sobel フィルタのIPで確かめてみます。
  1. 2016年04月16日 07:23 |
  2. OpenCV
  3. | トラックバック:0
  4. | コメント:0

FASTXコーナー検出IPのカメラ表示システム2(論理合成、インプリメント)

”FASTXコーナー検出IPのカメラ表示システム1(プロジェクト作成)”の続き。

前回は、ブロックデザインに FASTX コーナー検出IP をラプラシアンフィルタIP と入れ替えた。今回は、論理合成、インプリメントを行う。

2016/04/20 追記:FASTX コーナー検出IP が動作しなかったのは、hls::Duplicate() で複製された img_1_ の data_stream に hls::FASTX() と hls::Dilate() の最大レイテンシの 7 行と 3 行を足した 10 行分より多い 2 の 13 乗の 8196 個の FIFO バッファを付けるように STREAM ディレクティブを指定する必要があったからでした。それを行ったところ、”FASTXコーナー検出IPのカメラ表示システム5(Vivado HLS 2016.1でやってみた3)”で動作しました)

まずは、入っていたVivado Analyzer を削除した。
Vivado_HLS_OpenCV_68_160412.png

論理合成、インプリメントを行うと、Timing のWorst Negative Slack が赤くなって、エラーが出ている。
Vivado_HLS_OpenCV_69_160412.png

Project Summary を示す。LUT は44 % 使用している。やはりもう少し大きいZynq が欲しいところだ。。。
Vivado_HLS_OpenCV_70_160412.png

Implemented Design を開いてみると、BUFR_pixel_clk_io_n_0 から clk_fpga_0 までと、 clk_fpga_0 から BUFR_pixel_clk_io_n_0 までのパスがダメのようだ。
Vivado_HLS_OpenCV_71_160412.png

BUFR_pixel_clk_io_n_0 から clk_fpga_0 までと、 clk_fpga_0 から BUFR_pixel_clk_io_n_0 までのパスを無視するようにタイミング制約をするのだが、その前にReport CDC を見てみよう。
Implemented Design は開いてあるので、Tools メニュー -> Timing -> Report CDC... を選択した。(”Vivado のImplemented Design で Report CDC を確認する”参照)

BUFR_pixel_clk_io_n_0 から clk_fpga_0 までと、 clk_fpga_0 から BUFR_pixel_clk_io_n_0 までのパスを確認してみると、Unsafe が 1 つある。
Vivado_HLS_OpenCV_76_160413.png

From は V_ZYBO_CAMDS_i/bitmap_disp_cntrler_axi_master_0/inst/bitmap_disp_eng_inst/cs_rdg_reg[0]/C
To は V_ZYBO_CAMDS_i/bitmap_disp_cntrler_axi_master_0/inst/bitmap_disp_eng_inst/hv_cnt_ena_d1_reg/D
だった。
Vivado_HLS_OpenCV_77_160413.png

これは、”ビットマップ・ディスプレイ・コントローラのReport CDC unsafe 箇所を確認し、修正する”で修正したのと同じところだ。よって同様に修正するのだが、修正する前にTiming制約違反を防ぐために clk_fpga_0 とBUFR_pixel_clk_io_n_0 相互のパスを無視する制約を追加しよう。

Edit Timing Constraints で、BUFR_pixel_clk_io_n_0 から clk_fpga_0 までのパスを無視する設定にする。
Vivado_HLS_OpenCV_72_160412.png

次に、clk_fpga_0 から BUFR_pixel_clk_io_n_0 までのパスを無視する設定にする。
Vivado_HLS_OpenCV_73_160412.png

これでタイミング制約の修正は終了した。
Vivado_HLS_OpenCV_74_160412.png

次に、”ビットマップ・ディスプレイ・コントローラのReport CDC unsafe 箇所を確認し、修正する”で修正したのと同様に bitmap_disp_cntrler_axi_master を修正した。

さて、もう一度、論理合成、インプリメントを行った。

タイミング制約も満足して、論理合成、インプリメントは成功した。Summary を示す。
Vivado_HLS_OpenCV_75_160412.png

次にハードウェアをSDK にエクスポートして、SDKを立ち上げた。
ビットストリームをFPGAにダウンロードして、cam_disp_axis.elf を起動した。
VGAポートに接続されたディスプレイにカメラ画像が表示された。
fastx_on.elf を起動して、FASTXコーナー検出IP を通して画像を出力しようとしたが、画面表示が変更されない。以前のフレームバッファの内容を表示している感じだ。FASTXコーナー検出IP はカメラ画像入力ー>DDR3 SDRAMフレームバッファへWrite するパスに入っているので、そこが止まっている感じだ。
Vivado_HLS_OpenCV_79_160414.png

(追記)
元のラプラシアンフィルタのプロジェクトでは、カメラ画像をリアルタイムにラプラシアンフィルタ処理できました。やはり、FASTXコーナー検出IP にバグがありそうです。
  1. 2016年04月13日 05:19 |
  2. OpenCV
  3. | トラックバック:0
  4. | コメント:0

FASTXコーナー検出IPのカメラ表示システム1(プロジェクト作成)

Vivado HLS 2015.4 で OpenCV を使ってみた8(FAST Corners Detection 2)”のFASTXコーナー検出IPを使って、カメラ表示システムを作ることにした。
AXI4-Stream版ラプラシアンフィルタIPのカメラ表示システム4(ブロックデザイン2)”のブロックデザインを使用してラプラシアンフィルタIPの代わりにFASTXコーナー検出IPを入れ替えようと思う。

最初にプロジェクトをコピーして、V_ZYBO_CAMDS_FASTX_154 というフォルダ名をつけた。

Vivado HLS 2015.4 で OpenCV を使ってみた8(FAST Corners Detection 2)”の xilinx_com_hls_image_filter_1_0.zip の中身を新規作成した FASTX_OpenCV_154 フォルダに入れた。
Vivado_HLS_OpenCV_65_160412.png

FASTXコーナー検出IP は、image_filter という名前になっている。それをIP Catalog に登録した。
Vivado_HLS_OpenCV_61_160412.png

image_filter を camera_interface モジュールのラプラシアンフィルタIP の代わりに置いて配線を行った。
Vivado_HLS_OpenCV_63_160412.png

ブロックデザイン全体を示す。
Vivado_HLS_OpenCV_62_160412.png

Address Editor を示す。image_filter をアドレスマップした。
Vivado_HLS_OpenCV_64_160412.png

現在、synth_design の最中だ。ブロックデザインにMark Debug してあったので、すべての配線をUnmark Debug した。synth_design を行ってから、Vivado Analyzer を取り外そうと思う。
Vivado_HLS_OpenCV_66_160412.png
  1. 2016年04月12日 04:39 |
  2. OpenCV
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2015.4 で OpenCV を使ってみた8(FAST Corners Detection 2)

Vivado HLS 2015.4 で OpenCV を使ってみた7(FAST Corners Detection 1)”の続き。

前回は、C シミュレーションまで行った。今回は、C コードの合成とIP化を行った。

最初にC コードの合成を行った。Target を 10 (ns) にしたら、例によって、11.12 (ns) だったので、制約が満足しなかった。
100 MHz で動かしたかったので、Target を 9 (ns) にした。そうすると、Estimated は、9.4 (ns) だった。これで、100 MHz で動作するので、これで良しとした。
Vivado_HLS_OpenCV_56_160409.png

Latncy は 500726 クロックだった。これは最大解像度の 800 x 600 ピクセル(これは、自分で設定した)なので、クロックをピクセル数で割ってみると、500726 クロック / (800 x 600) ピクセル ≒ 1.04 倍だった。約 1 ピクセルを 1 クロックで処理できている。これは良い。
動作周波数を 100 MHz とすると、100 / 1.04 ≒ 96.2 MHz 位の計算になる。

リソース使用量を下に示す。
Vivado_HLS_OpenCV_57_160409.png

BRAM_18K は 11 個、DSP48E は 3 個、FF は 5379 個、LUT は 6267 個だった。

次に、Target を 10 (ns) の時なのだが、C/RTL コシミュレーションをやってみたが、5時間立ってもまだ、RTL シミュレーションが終わらないので、諦めてキャンセルした。まあ、だいたい合成の Latency の値が参考になるので良しとした。

次に、Target を 9 (ns) にした時の合成結果をIP 化した。
Vivado_HLS_OpenCV_58_160409.png

C コードの合成での、最大動作周波数を探ってみた。Target を 2.5 (ns) にして、合成した。
Vivado_HLS_OpenCV_59_160409.png

結果は、3.56 (ns) だった。これは約 281 MHz に相当する。
Latency の最大値は、541200 クロックで、541200 / (800 x 600) ≒ 1.13 倍
よって、281 / 1.13 ≒ 249 MHz になる。

リソース使用量を下に示す。
Vivado_HLS_OpenCV_60_160409.png

FF は約2倍に増えている。LUT もそれほどでは無いが増えている。
OpenCVのハードウェア化をすると、Zynq-7010 では小さいかもしれない? 7020のZYBOを作ってくれないだろうか?
  1. 2016年04月10日 05:00 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2015.4 で OpenCV を使ってみた7(FAST Corners Detection 1)

Vivado HLS 2015.4 で OpenCV を使ってみた6(Sobelフィルタを試した4)”の続き。

前回までは、Sobel フィルタの動作について試してみたが、今回は、その次の”FAST Corners Detection”を試してみる。

XAPP1167 (v3.0) June 24, 2015 ”Accelerating OpenCV Applications with Zynq-7000 All Programmable SoC using Vivado HLS Video Libraries”の9ページの Figure 5 を引用させて頂く。
Vivado_HLS_OpenCV_52_160409.png

機能としては、グレースケールに変換して、FAST Corners Detection を行い、原画像に FAST Corners Detection をマージする。
テストベンチの opencv_image_filter() としては、”XAPP1167 (v3.0) June 24, 2015”の 9 ページの関数そのものを引用させて頂いた。
テストベンチの opencv_ex_ug_tb.cpp を示す。

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

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

using namespace cv;

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

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 dst=%d,%d,%d dst_cv=%d,%d,%d\n", x, y,
                        dst_bgr[0], dst_bgr[1], dst_bgr[0], dst_cv_bgr[0], dst_cv_bgr[1], dst_cv_bgr[2]);
                //return 1;
            }
        }
    }
    printf("Test with 0 errors.\n");

    return 0;
}

void opencv_imaga_filter(Mat& src, Mat& dst){
     src.copyTo(dst); // 深いコピー
     std::vector<Mat> layers;
     std::vector<KeyPoint> keypoints;
     split(src, layers);
     FAST(layers[0], keypoints, 20true);
     for (int i = 0; i < keypoints.size(); i++) {
         rectangle(dst,
                 Point(keypoints[i].pt.x-1, keypoints[i].pt.y-1),
                 Point(keypoints[i].pt.x+1, keypoints[i].pt.y+1),
                 Scalar(255,0), CV_FILLED);
     }
}


opencv_ex_ug.cpp を示す。

// opencv_ex_ug.cpp
// 2016/04/02 by marsee
// 2016/04/09 : FAST Corners Detection

#include "opencv_ex_ug.h"

void image_filter(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM, int rows, int
cols) {
#pragma HLS DATAFLOW
#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);
    RGB_IMAGE img_1(rows, cols);
    RGB_IMAGE img_1_(rows, cols);
    GRAY_IMAGE img_1g(rows, cols);
    GRAY_IMAGE mask(rows, cols);
    GRAY_IMAGE dmask(rows, cols);
    GRAY_IMAGE img_2g(rows, cols);
    RGB_IMAGE img_3(rows, cols);
    RGB_PIXEL color(25500);

    hls::AXIvideo2Mat(INPUT_STREAM, img_0);
    hls::Duplicate(img_0, img_1, img_1_);
    hls::CvtColor<HLS_BGR2GRAY>(img_1, img_1g);
    hls::FASTX(img_1g, mask, 20true);
    hls::Dilate(mask, dmask);
    hls::PaintMask(img_1_, dmask, img_3, color);
    hls::Mat2AXIvideo(img_3, OUTPUT_STREAM);
}


どちらも、XAPP1167 から自分の環境で動作するように変換してある。合っているのかどうかはよくわからないが、結果はそれらしく出力されている。
最後に、opencv_ex_ug.h を貼っておく。

// 2016/04/03 : GRAY_IMAGE を追加

#ifndef __opencv_ex_ug_H__
#define __opencv_ex_ug_H__

#include "ap_axi_sdata.h"
#include "hls_video.h"

#define MAX_HEIGHT    600
#define MAX_WIDTH    800

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef hls::Scalar<3unsigned char> RGB_PIXEL;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1> GRAY_IMAGE;
#endif


これで、C シミュレーションを行った。結果を示す。
Vivado_HLS_OpenCV_53_160409.png

かなりエラーが出ている。OpenCVでやった結果とVivado HLS の HLS ビデオライブラリでの結果がだいぶ違っているようだ。

Vivado HLS の HLS ビデオライブラリを使用して出力した test_result.jpg を示す。
Vivado_HLS_OpenCV_54_160409.jpg

OpenCV で出力した test_result_cv.jpg を示す。
Vivado_HLS_OpenCV_55_160409.jpg

拡大して見比べてみると、大体は合っているのだが、やはり点が違っているところがあるようだ。
  1. 2016年04月09日 05:40 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2015.4 で OpenCV を使ってみた6(Sobelフィルタを試した4)

Vivado HLS 2015.4 で OpenCV を使ってみた5(Sobelフィルタを試した3)”の続き。

前々回はX軸方向のSobel フィルタ前回はY軸方向のSobel フィルタをやってみた。今回はXY軸方向のSobel フィルタをやってみた。
実は、HLS Sobel では、”Only SIZE=3, 5, or 7 is supported. Only (XORDER=1 and YORDER=0, horizontal derivative) or (XORDER=0 and YORDER=1, vertical derivative) is supported”と書いてあるので、両方 1 はダメだとは思うのだが、一応確かめる。

ソースコードの opencv_ex_ug.cpp の変更部分を示す。X軸とY軸を両方 1 にした。

hls::Sobel<1,1,3>(img_1g, img_2g);


テストベンチの opencv_ex_ug_tb.cpp の変更部分を示す。同様にX軸とY軸を両方 1 にした。

Sobel(gray, img0g, IPL_DEPTH_16S, 1, 1, 3);


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

1つエラーが出ている。

Sobel フィルタ結果を示す。ソースの方の test_result.jpg を示す。
Vivado_HLS_OpenCV_49_160408.jpg

テストベンチの出力の test_result_cv.jpg を示す。
Vivado_HLS_OpenCV_50_160408.jpg

エッジの検出が弱い気がする。

C コードからの合成を行った。
Vivado_HLS_OpenCV_46_160408.png

これはX軸方向、Y軸方向とも一緒だ。

リソース使用量は異なる。最初にX軸方向、次にY軸方向、最後にXY軸方向のリソース使用量を示す。
Vivado_HLS_OpenCV_20_160404.pngVivado_HLS_OpenCV_36_160406.pngVivado_HLS_OpenCV_47_160408.png

XY軸方向の方が FF、LUT ともに少ないので、やはりおかしいかもしれない?XY軸方向だとX軸、Y軸ともに2乗して平方根を取っているはずなので、増えるはずだと思う。

C/RTL コシミュレーションを行った。
Vivado_HLS_OpenCV_48_160408.png

Latency は 29319 クロックかかった。Y軸方向と同じだ。ちなみにX軸方向は 30207 クロックかかっている。
C/RTL コシミュレーション波形は同じだと思うので省略する。

Gimp でSobel を掛けてみた結果を下に示す。やはり綺麗に輪郭が出ている。
Vivado_HLS_OpenCV_51_160408.jpg
  1. 2016年04月08日 04:47 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2015.4 で OpenCV を使ってみた5(Sobelフィルタを試した3)

”Vivado HLS 2015.4 で OpenCV を使ってみた4(Sobelフィルタを試した2)”の続き。

前回は、X 軸方向のSobel フィルタをやっていたのだが、今回は Y 軸方向のSobel フィルタをテストしてみる。
ソースコードの opencv_ex_ug.cpp の変更部分を示す。

hls::Sobel<0,1,3>(img_1g, img_2g);

とした。X 軸方向の時は、

hls::Sobel<1,0,3>(img_1g, img_2g);

だった。

テストベンチの opencv_ex_ug_tb.cpp の変更部分を示す。同様に、

Sobel(gray, img0g, IPL_DEPTH_16S, 0, 1, 3);

とした。X 軸方向の時は

、Sobel(gray, img0g, IPL_DEPTH_16S, 1, 0, 3);

だった。

C シミュレーションを行った。X 軸方向の時よりもエラーが 2 個増えた。
Vivado_HLS_OpenCV_34_160406.png

C シミュレーション後の Sobel フィルタ結果を示す。
まずは元画像の test.jpg から。
Vivado_HLS_OpenCV_41_160406.jpg

ソースの方の test_result.jpg を示す。
Vivado_HLS_OpenCV_42_160406.jpg

テストベンチの出力の test_result_cv.jpg を示す。
Vivado_HLS_OpenCV_43_160406.jpg

C ソースコードからの合成を行った。
Vivado_HLS_OpenCV_35_160406.png

Estimated も Latency も前回の X 軸方向の Sobel フィルタと同じなので、同じライブラリのようだ。Estimated は 11.12 (ns) で制約を満たしていない。

リソース使用量を示す。左に今回の Y 軸方向、右に前回の X 軸方向の Sobel フィルタの場合の結果を示す。
Vivado_HLS_OpenCV_36_160406.pngVivado_HLS_OpenCV_20_160404.png

Estimated と Latency が前回と違っていないのに、リソース使用量はX 軸方向の Sobel フィルタの方が FF と LUT で多くなっている。

Analysis 表示を示す。
Vivado_HLS_OpenCV_40_160406.png

次々にライブラリをコールしているのが分かった。

C/RTL コシミュレーションを行った。
Vivado_HLS_OpenCV_37_160406.png

Latency は 29319 クロックかかった。前回の X 軸方向のC/RTL コシミュレーション結果はクロックのTarget が 5 (ns) の時の結果で、クロック数/ピクセル数は 1.09 だった。今回の Y 軸方向の場合は、クロックのTarget が 10 (ns) の時の結果で、クロック数/ピクセル数は 29319 / (189 x 146) ≒ 1.06 倍となって、こちらのほうが倍率が少ない。

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

拡大してみた。
Vivado_HLS_OpenCV_39_160406.png

INPUT_STREAM_TVALID と OUTPUT_STREAM_TREADY の波形はX 軸方向の場合と同様のようだ。

最後に、RTL シミュレーション結果の test_result.jpg を示す。
Vivado_HLS_OpenCV_44_160406.jpg
  1. 2016年04月06日 05:09 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2015.4 で OpenCV を使ってみた4(Sobelフィルタを試した2)

Vivado HLS 2015.4 で OpenCV を使ってみた3(Sobelフィルタを試した1)”の続き。

前回はC シミュレーションまでだったので、今回は、Cコードの合成、C/RTL コシミュレーションを行った。

最初にCコードの合成を行った。
Vivado_HLS_OpenCV_19_160404.png

Target の 10 (ns) に対して、Estimated は 11.12 (ns) で制約を満たしていない。

リソース使用量を示す。
Vivado_HLS_OpenCV_20_160404.png

BRAM_18K が 3 、DSP48E が 3 、FF が 849 、LUT が1384 使用されていた。

タイミングが満足しないので、soulution2 を新たに作って、Clock Period を 5 (ns) に設定した。
それで、もう一度 C への合成を行った。
Vivado_HLS_OpenCV_21_160404.png

Target の 5 (ns9 に対して、Estimated は 4.36 (ns) で満足している。タイミング制約を満足できないわけではなく、たまたま満足できなかったようだ。

リソース使用量を示す。左に Target が 10 (ns) の時のリソース使用量、右に Target が 5 (ns) の時のリソース使用量を示す。
Vivado_HLS_OpenCV_20_160404.pngVivado_HLS_OpenCV_22_160404.png

Target が 5 (ns) の時は、特に FF が増えているが分かる。

OpenCV の合成では、HLS ビデオライブラリの関数ごとに合成レポートが出るようだ。
Vivado_HLS_OpenCV_23_160404.png

合成された Verilog HDL ファイルを示す。HLS ビデオライブラリの関数ごとに Verilog HDL ファイルが生成されている。ファイルが多い。
Vivado_HLS_OpenCV_24_160404.png

次に C/RTL コシミュレーションを行った。前回はRTL シミュレーションが終了しなかったので、寝る前に仕掛けて、寝たのだが、10分足らずですぐに終わった。前回のC/RTL コシミュレーションを違うパソコンでやるとすぐ終わる。このパソコンはAMDだから、なのか?謎が残る。
Vivado_HLS_OpenCV_18_160404.png

Latency は、30207 クロックかかっている。画像のサイズは 189 x 146 ピクセルなので、27594 ピクセルだ。
30207 / 27594 ≒ 1.095 になった。1 クロックに 1 ピクセルを処理できているが、待ちもあるようだ。

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

終了時刻が 151.3225 us になっているが、これは 200 MHz で回路が動作した場合である。
INPUT_STREAM_TREADY と OUTPUT_STREAM_TVALID に待ちが入っていて、少しWait していることが分かる。

そのWait しているところを拡大してみよう。
TLAST が 1 にアサートされて、画像のラインが終了してる所で、Wait が入っているのが分かった。
Vivado_HLS_OpenCV_26_160404.png

だが、性能が 1.095 倍だけ落ちるので、200MHz が 200 / 1.095 ≒ 183 MHz になると考えると良いと思う。

最後に、C/RTL コシミュレーションで出力された test_result.jpg を示す。問題ないようだ。
Vivado_HLS_OpenCV_30_160404.jpg
  1. 2016年04月05日 04:32 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2015.4 で OpenCV を使ってみた3(Sobelフィルタを試した1)

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

前回はテストベンチをOpenCV 2.X のMat で書き直した。今回はSobel フィルタを試してみた。
初めにカラーの状態でSobel フィルタを試してみたところ、上手く行ったのだが、GRAY変換してSobel フィルタを掛ける所で、いろいろと試行錯誤してしまった。最初はhls::CvtColor の書き方が分からなかったのだが、”Output of hls::CvtColor appears gray scale.”を見たら分かった。このWebページからは、GRAY_IMAGEの定義も引用させて頂いた。ありがとうございます。ただ、このWebページではHLS_RGB2GRAY を使っているようだが、Mat フォーマットを考えても HLS_BGR2GRAY のようだ。(ツィターで教えて頂いた皆さん、ありがとうございました)
しかし、多少問題があって、HLS_RGB2GRAY を使った時は(当然、OpenCVのテストベンチの方も CV_RGB2GRAY を使用している)エラーが 0 なのだが、HLS_BGR2GRAY を使った時はエラーが 3 つほど出てしまう。どのエラーも誤差は 1 か 2 なので、演算誤差なのかな?と思うので、これで良しとした。(演算の時の型が違うのだろうか?浮動小数点演算と整数演算の違いとか?まだ分からないが。。。)
なお、Xilinx Wiki の hls::CvtColor のページを参考にした。

昨日1日がかりで出来上がったテストベンチの opencv_ex_ug_tb.cpp を示す。Sobel フィルタは X 軸方向のみとなっている。

// opencv_ex_ug_tb.cpp
// 2016/04/02 by marsee
// OpenCV 2 の Mat を使用したバージョン
// 2016/04/03 : グレー変換あり Sobel フィルタ

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

using namespace cv;

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

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 dst=%d,%d,%d dst_cv=%d,%d,%d\n", x, y,
                        dst_bgr[0], dst_bgr[1], dst_bgr[0], dst_cv_bgr[0], dst_cv_bgr[1], dst_cv_bgr[2]);
                //return 1;
            }
        }
    }
    printf("Test with 0 errors.\n");

    return 0;
}

void opencv_imaga_filter(Mat& src, Mat& dst){
    Mat gray(src.rows, src.cols, CV_8UC1);
    Mat img0g(src.rows, src.cols, CV_8UC1);

    cvtColor(src, gray, CV_BGR2GRAY);
    Sobel(gray, img0g, IPL_DEPTH_16S, 103);
    cvtColor(img0g, dst, CV_GRAY2BGR);

}


ヘッダの opencv_ex_ug.h を示す。

// 2016/04/03 : GRAY_IMAGE を追加

#ifndef __opencv_ex_ug_H__
#define __opencv_ex_ug_H__

#include "ap_axi_sdata.h"
#include "hls_video.h"

#define MAX_HEIGHT    600
#define MAX_WIDTH    800

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef hls::Scalar<3unsigned char> RGB_PIXEL;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1> GRAY_IMAGE;
#endif


C++ソースコードの opencv_ex_ug.cpp を示す。

// opencv_ex_ug.cpp
// 2016/04/02 by marsee
// 2016/04/03 : グレー変換あり Sobel フィルタ

#include "opencv_ex_ug.h"

void image_filter(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM, int rows, int
cols) {
#pragma HLS DATAFLOW
#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);
    GRAY_IMAGE img_1g(rows, cols);
    GRAY_IMAGE img_2g(rows, cols);
    RGB_IMAGE img_3(rows, cols);

    hls::AXIvideo2Mat(INPUT_STREAM, img_0);
    hls::CvtColor<HLS_BGR2GRAY>(img_0, img_1g);
    hls::Sobel<1,0,3>(img_1g, img_2g);
    hls::CvtColor<HLS_GRAY2BGR>(img_2g, img_3);
    hls::Mat2AXIvideo(img_3, OUTPUT_STREAM);
}


C シミュレーションを行った。結果を示す。3つエラーが出ている。
Vivado_HLS_OpenCV_17_160403.png

今回から、画像を変更した今までは、A という文字だったがエッジフィルタの効果がよくわからなかったので、画像にした。自分で撮影したZYBOボードの写真の一部を使っている。test.jpg を下に示す。写真のサイズは 189 x 146 ピクセルと中途半端なサイズとなっている。
Vivado_HLS_OpenCV_27_160404.jpg

次に、test_result.jpg つまり、Vivado HLSのHLS ビデオライブラリを使用した場合のSobel フィルタ出力画像を示す。(X 軸方向成分のみです)
Vivado_HLS_OpenCV_28_160404.jpg

最後に、test_result_cv.jpg 、OpenCV での出力画像を示す。(X 軸方向成分のみです)
Vivado_HLS_OpenCV_29_160404.jpg

Vivado HLS 2015.4 で OpenCV を使ってみた4(Sobelフィルタを試した2)”に続く。
  1. 2016年04月04日 04:42 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

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

Vivado HLS 2015.4 で OpenCV を使ってみた1

Vivado HLS 2015.4 で OpenCV を使ってみようということで、やってみた。

参考にするのは、”Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2015.4) 2015 年 11 月 24 日”の 196 ページの”ビデオ関数”を参照している。
多少古い資料なのだが、XAPP1167 (v1.0) 2013 年 3 月 20 日 ”Vivado HLS ビデオライブラリを使用してZynq で OpenCV アプリケーションを高速化”とその最新英語版、XAPP1167 (v3.0) June 24, 2015 ”Accelerating OpenCV Applications with Zynq-7000 All Programmable SoC using Vivado HLS Video Libraries”も参照している。

取りあえずは、、”Vivado Design Suite ユーザー ガイド 高位合成 UG902 (v2015.4) 2015 年 11 月 24 日”の 198 ページのテストベンチと 199 ページの image_filter() を試してみることにした。

今から使用する Vivado HLSのOpenCV の実装方法は、ハードウエアにする関数の入出力部分はAXI4 Stream で行うということだ。テストベンチでは、IplImage2AXIvideo() で IplImage から AXI4 Stream に直して、image_filter() に入れて、処理後には、AXIvideo2IplImage() で AXI4 Stream から IplImage に戻している。画像処理には、AXI4 Stream を使用するのが、一番自然な流れだと思う。ただ、OpenCV 1.X で書いてあるので、できれば OpenCV 2.X に直したい。
そうそう、テストベンチの方は、"hls_opencv.h" をインクルードする必要があった。その "hls_opencv.h" をVivado HLS のソース表示画面から”CTRL+ クリック”で表示してみると、CvMat2AXIvideo() と AXIvideo2CvMat() があるので、cv::Mat で書けるかもしれない?後でやってみようと思う。

Vivado HLS 2015.4 で opencv_ex_ug プロジェクトを作成した。
テストベンチの opencv_ex_ug_tb.cpp はユーザーズガイドのテストベンチの比較部分を取り除いてある。先ほど書いた、opencv.h をインクルードして、INPUT_IMAGE、OUTPUT_IMAGE、OUTPUT_IMAGE_GOLDEN(これは今のところ使っていないが)を定義した。更に、opencv_ex_ug.h をインクルードしてある。
opencv_ex_ug_tb.cpp を図として下に示す。
Vivado_HLS_OpenCV_1_160401.png

opencv_ex_ug.h を下に示す。
Vivado_HLS_OpenCV_2_160401.png

#ifndef __opencv_ex_ug_H__
#define __opencv_ex_ug_H__

#include "ap_axi_sdata.h"
#include "hls_video.h"

#define MAX_HEIGHT    600
#define MAX_WIDTH    800

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef hls::Scalar<3unsigned char> RGB_PIXEL;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
#endif


opencv_ex_ug.cpp だが、ユーザーズガイドの 199 ページの image_filter() をほとんどそのまま流用しているが、テストベンチの OpenCV 関数との比較を省いてしまったこともあって、本当にフィルタ結果が正しいのかどうか?確信が持てなかった。そこで、入力した画像をそのまま出力してもらって同じかどうか?を見てみたいと思った。そこで、hls::SubS() のみとした。なお、hls::SubS() は、画像イメージから、スカラー値を引く OpenCV 関数である。マニュアルはXilinx Wiki にある(HLS SubS)。”hls::SubS”で、Xilinx Wiki 上で検索すれば良い。
hls::SubS() では、画像イメージから 0 を引くようにした。つまり、画像イメージのままということになる。
opencv_ex_ug.cpp を下に示す。
Vivado_HLS_OpenCV_3_160401.png

opencv_ex_ug.cpp にAXI4 バス用のディレクティブを追加した。

#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


次に、入力画像の”test.bmp”だが、以前から使用している’A’という画像を使用した。
Vivado_HLS_OpenCV_4_160401.png

これで C シミュレーションを行った。結果を示す。比較はしていないので、終了メッセージだけである。
Vivado_HLS_OpenCV_5_160401.png

”test_resullt.bmp”を示す。”test.bmp”と同じだった。
Vivado_HLS_OpenCV_6_160401.png

これで、入出力系は問題ないということが分かった。

これで、C コードを合成してみた。
Vivado_HLS_OpenCV_7_160401.png

AXIvideo2Mat と Mat2AXIvideo が 1 つずつ Verilog HDL ファイルになっていた。
Latency は min が 20 で、max が 1446007 だった。だいぶ離れているが、20 は 0 x 0 の画像の時のレイテンシだろうか?
1446007 は 800 x 600 で割ってみると、3.01 くらいなので、最大の 800 x 600 でフィルタを行った時のレイテンシかな?

リソース使用量を見てみると、画像から 0 を引いているだけので、BRAM_18K は使っていない。
Vivado_HLS_OpenCV_8_160401.png
  1. 2016年04月01日 05:07 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0