FC2カウンター FPGAの部屋
fc2ブログ

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

FPGAの部屋

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

Vitis HLS 2022.2 の新機能を確かめる3(performance プラグマ3)

Vitis HLS 2022.2 の新機能を確かめる2(performance プラグマ2)”の続き。

What’s New in the Vitis HLS Tool 2022.2 – Key Feature Enhancements”を参考に、Vitis HLS 2022.2 の新機能を確かめてみようということで、前回は、performance プラグマが無くても、その他のパフォーマンスに関係するプラグマが無くても、ソーベル・フィルタのソースコードでループ回数を固定すると、1 クロックで 1 ピクセル処理をパフォーマンスに関するプラグマなしの状態で達成できた。今回は、より performance プラグマを評価するために、ソフトウエアとして書いた AXI4 Master アクセスベースの sobel_fil_axim.cpp とそのテストベンチ、Vitis HLS プロジェクトを示す。

Vitis HLS 2022.2 で sobel_fil_axim プロジェクトを作成した。
Vitis_HLS_2022_2_20_221129.png

今回のテストベンチ・コードでは OpenCV ライブラリを使用している。
Vitis HLS 2022.2 には内蔵された OpenCV は無いので、別にインストールした OpenCV を指定する。
Vitis HLS の Project メニューから Project Settings... を選択して、Project Settings ダイアログを開いた。
Simulation タブを開いて、sobel_fil_axim_tb.cpp の CFLAGS に

-I/usr/local/include

を設定した。
なお、Vitis HLS 2022.2 では、CFLAG が絶対パスのままになっている。やっとバグが直ったようだ。

Linker Flags に

-L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_imgproc

を設定した。
Vitis_HLS_2022_2_21_221129.png

ソースコードの sobel_fil_axim.cpp を示す。

// sobel_fil_axim.cpp
// 2022/11/28 by marsee

#include <stdint.h>

int32_t sobel_fil(int32_t h_or_v, int32_t x0y0, int32_t x1y0, int32_t x2y0, int32_t x0y1,
        int32_t x1y1, int32_t x2y1, int32_t x0y2, int32_t x1y2, int32_t x2y2);
int32_t conv_rgb2y(int32_t rbg);
int32_t square_root8(int32_t val);

#define ROW_SIZE    600
#define COL_SIZE    800
#define HORIZONTAL  0
#define VERTICAL    1

int sobel_fil_axim(int32_t *cam_fb, int32_t *sobel_fb){
#pragma HLS INTERFACE mode=s_axilite port=return
#pragma HLS INTERFACE mode=m_axi depth=480000 port=cam_fb offset=slave
#pragma HLS INTERFACE mode=m_axi depth=480000 port=sobel_fb offset=slave
    int32_t sobel_val, sobel_h_val, sobel_v_val;
    int32_t pix[3][3];

    for(int y=0; y<ROW_SIZE; y++){
//#pragma HLS performance target_ti=800
        for(int x=0; x<COL_SIZE; x++){
            for(int i=2; i>=0; --i){
                for(int j=2; j>=0; --j){
                    if(x>=2 && y>=2)
                        pix[i][j] = conv_rgb2y(cam_fb[(y-i)*COL_SIZE+(x-j)]);
                    else
                        pix[i][j] = 0;
                }
            }
            sobel_h_val = sobel_fil(HORIZONTAL, pix[0][0], pix[0][1], pix[0][2],
                                                pix[1][0], pix[1][1], pix[1][2],
                                                pix[2][0], pix[2][1], pix[2][2]);
            sobel_v_val = sobel_fil(VERTICAL,   pix[0][0], pix[0][1], pix[0][2],
                                                pix[1][0], pix[1][1], pix[1][2],
                                                pix[2][0], pix[2][1], pix[2][2]);
            sobel_val = square_root8(sobel_h_val*sobel_h_val + sobel_v_val*sobel_v_val);
            if(x<2 || y<2)
                sobel_val = 0;
            sobel_fb[y*COL_SIZE+x] = (sobel_val<<16)+(sobel_val<<8)+sobel_val;
        }
    }
    return(0);
}

// RGBからYへの変換
// RGBのフォーマットは、{R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int32_t conv_rgb2y(int32_t rgb){
    int32_t r, g, b, y_f;
    int32_t y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8; // 256で割る

    return(y);
}

// sobel filter
// HORZONTAL
// x0y0 x1y0 x2y0  1  2  1
// x0y1 x1y1 x2y1  0  0  0
// x0y2 x1y2 x2y2 -1 -2 -1
// VERTICAL
// x0y0 x1y0 x2y0  1  0 -1
// x0y1 x1y1 x2y1  2  0 -2
// x0y2 x1y2 x2y2  1  0 -1
int32_t sobel_fil(int32_t h_or_v, int32_t x0y0, int32_t x1y0, int32_t x2y0, int32_t x0y1,
        int32_t x1y1, int32_t x2y1, int32_t x0y2, int32_t x1y2, int32_t x2y2){
    int32_t y;

    if(h_or_v == HORIZONTAL){
        y = x0y0 + 2*x1y0 + x2y0 - x0y2 - 2*x1y2 - x2y2;
    } else {
        y = x0y0 - x2y0 + 2*x0y1 - 2*x2y1 + x0y2 - x2y2;
    }
    if(y<0)
        y = -y;
        //y = 0;
    else if(y>255) // 8 bits
        y = 255;
    return(y);
}

// square_root8
// 8 bit幅のsquare_rootを求める
int32_t square_root8(int32_t val){
    int32_t temp = 0;
    int32_t square;

    for(int i=7; i>=0; --i){
        temp += (1 << i);
        square = temp * temp;

        if(square > val){
            temp -= (1 << i);
        }
    }

    return(temp);
}


ソースコードの sobel_fil_axim_tb.cpp を示す。

// sobel_fil_axim_tb.cpp
// 2022/11/29 by marsee

#include <stdio.h>
#include <stdint.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgcodecs/imgcodecs.hpp"

#define HORIZONTAL  0
#define VERTICAL    1

int sobel_fil_axim(int32_t *cam_fb, int32_t *sobel_fb);
int sobel_filter_soft(int32_t *cam_fb, int32_t *sobel_fb,
        int32_t x_size, int32_t y_size);
int32_t square_root8_soft(int32_t val);
int32_t sobel_fil_soft(int32_t h_or_v, int32_t x0y0, int32_t x1y0, int32_t x2y0, int32_t x0y1,
        int32_t x1y1, int32_t x2y1, int32_t x0y2, int32_t x1y2, int32_t x2y2);
int32_t conv_rgb2y_soft(int32_t rbg);

const char INPUT_JPG_FILE[] = "test2.jpg";
const char OUTPUT_JPG_FILE[] = "sobel.jpg";

int main(){
    // JPG ファイルをMat に読み込む
    cv::Mat img = cv::imread(INPUT_JPG_FILE);

    // ピクセルを入れる領域の確保
    std::vector<int32_t> rd_bmp(sizeof(int32_t)*img.cols*img.rows);
    std::vector<int32_t> hw_sobel(sizeof(int32_t)*(img.cols)*(img.rows));
    std::vector<int32_t> sw_sobel(sizeof(int32_t)*(img.cols)*(img.rows));

    // rd_bmp にJPGのピクセルを代入
    cv::Mat_<cv::Vec3b> dst_vec3b = cv::Mat_<cv::Vec3b>(img);
    for (int y=0; y<img.rows; y++){
        for (int x=0; x<img.cols; x++){
            cv::Vec3b pixel;
            pixel = dst_vec3b(y,x);
            rd_bmp[y*img.cols+x] = (pixel[0] & 0xff) | ((pixel[1] & 0xff)<<8) | ((pixel[2] & 0xff)<<16); // RGB 8 bits
            // blue - pixel[0]; green - pixel[1]; red - pixel[2];
        }
    }

    sobel_fil_axim(rd_bmp.data(), hw_sobel.data()); // ハードウェアのソーベルフィルタ
    sobel_filter_soft(rd_bmp.data(), sw_sobel.data(), img.cols, img.rows);  // ソフトウェアのソーベルフィルタ

    // ハードウェアとソフトウェアのソーベルフィルタの値のチェック
    for (int y=0; y<img.rows; y++){
        for (int x=0; x<img.cols; x++){
            if (hw_sobel[y*img.cols+x] != sw_sobel[y*img.cols+x]){
                printf("ERROR HW and SW results mismatch x = %ld, y = %ld, HW = %x, SW = %x\n",
                        x, y, hw_sobel[y*img.cols+x], sw_sobel[y*(img.cols-2)+x]);
                return(1);
            }
        }
    }
    printf("Success HW and SW results match\n");

    const int sobel_row = img.rows;
    const int sobel_cols = img.cols;
    cv::Mat wbmpf(sobel_row, sobel_cols, CV_8UC3);
    // wbmpf にsobel フィルタ処理後の画像を入力
    cv::Mat_<cv::Vec3b> sob_vec3b = cv::Mat_<cv::Vec3b>(wbmpf);
    for (int y=0; y<wbmpf.rows; y++){
        for (int x=0; x<wbmpf.cols; x++){
            cv::Vec3b pixel;
            pixel = sob_vec3b(y,x);
            int32_t rbg = hw_sobel[y*wbmpf.cols+x];
            pixel[0] = ((rbg >> 8) & 0xff); // blue
            pixel[1] = (rbg & 0xff); // green
            pixel[2] = ((rbg >> 16) & 0xff); // red
            sob_vec3b(y,x) = pixel;
        }
    }

    // ハードウェアのソーベルフィルタの結果を jpg ファイルへ出力する
    cv::imwrite(OUTPUT_JPG_FILE, wbmpf);

    return(0);
}

int sobel_filter_soft(int32_t *cam_fb, int32_t *sobel_fb,
    int32_t x_size, int32_t y_size){
    int32_t sobel_val, sobel_h_val, sobel_v_val;
    int32_t pix[3][3];

    for(int y=0; y<y_size; y++){
        for(int x=0; x<x_size; x++){
            for(int i=2; i>=0; --i){
                for(int j=2; j>=0; --j){
                    if(x>=2 && y>=2)
                        pix[i][j] = conv_rgb2y_soft(cam_fb[(y-i)*x_size+(x-j)]);
                    else
                        pix[i][j] = 0;
                }
            }
            sobel_h_val = sobel_fil_soft(HORIZONTAL,pix[0][0], pix[0][1], pix[0][2],
                                                    pix[1][0], pix[1][1], pix[1][2],
                                                    pix[2][0], pix[2][1], pix[2][2]);
            sobel_v_val = sobel_fil_soft(VERTICAL,  pix[0][0], pix[0][1], pix[0][2],
                                                    pix[1][0], pix[1][1], pix[1][2],
                                                    pix[2][0], pix[2][1], pix[2][2]);
            sobel_val = square_root8_soft(sobel_h_val*sobel_h_val + sobel_v_val*sobel_v_val);
            if(x<2 || y<2)
                sobel_val = 0;
            sobel_fb[y*x_size+x] = (sobel_val<<16)+(sobel_val<<8)+sobel_val;
        }
    }
    return(0);
}

// RGBからYへの変換
// RGBのフォーマットは、{R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int32_t conv_rgb2y_soft(int32_t rgb){
    int32_t r, g, b, y_f;
    int32_t y;

    b = rgb & 0xff;
    g = (rgb>>8) & 0xff;
    r = (rgb>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8; // 256で割る

    return(y);
}
// sobel filter
// HORZONTAL
// x0y0 x1y0 x2y0  1  2  1
// x0y1 x1y1 x2y1  0  0  0
// x0y2 x1y2 x2y2 -1 -2 -1
// VERTICAL
// x0y0 x1y0 x2y0  1  0 -1
// x0y1 x1y1 x2y1  2  0 -2
// x0y2 x1y2 x2y2  1  0 -1
int32_t sobel_fil_soft(int32_t h_or_v, int32_t x0y0, int32_t x1y0, int32_t x2y0, int32_t x0y1,
        int32_t x1y1, int32_t x2y1, int32_t x0y2, int32_t x1y2, int32_t x2y2){
    int32_t y;

    if(h_or_v == HORIZONTAL){
        y = x0y0 + 2*x1y0 + x2y0 - x0y2 - 2*x1y2 - x2y2;
    } else {
        y = x0y0 - x2y0 + 2*x0y1 - 2*x2y1 + x0y2 - x2y2;
    }
    if(y<0)
        y = -y;
        //y = 0;
    else if(y>255)
        y = 255;
    return(y);
}

// square_root8_soft
// 8 bit幅のsquare_rootを求める
int32_t square_root8_soft(int32_t val){
    int32_t temp = 0;
    int32_t square;

    for(int i=7; i>=0; --i){
        temp += (1 << i);
        square = temp * temp;

        if(square > val){
            temp -= (1 << i);
        }
    }

    return(temp);
}

  1. 2022年11月29日 05:21 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis HLS 2022.2 の新機能を確かめる2(performance プラグマ2)

Vitis HLS 2022.2 の新機能を確かめる1(performance プラグマ)”の続き。

What’s New in the Vitis HLS Tool 2022.2 – Key Feature Enhancements”を参考に、Vitis HLS 2022.2 の新機能を確かめてみようということで、前回は、 performance プラグマについて調査した。今回は、 performance プラグマの続きをやってみる。

performance プラグマを入れた場合は、C コードの合成を行った時の Synthesis Summary Report の Pragma Report に次の記述があった。

Cannot apply performance pragma target_ti=800 cycles for loop 'LOOP_Y' in function 'sobel_axis_RGB24'. The target requires a pipeline II less than the minimal achievable II of 4 determined by the number of conflicting accesses on local memory 'tmp.i.i47' (4 per iteration), the number of conflicting accesses on local memory 'tmp.i.i29' (4 per iteration), the number of conflicting accesses on local memory 'tmp.i.i192' (4 per iteration)


Vitis_HLS_2022_2_17_221128.png

performance プラグマを正しく評価したいということで、”What’s New in the Vitis HLS Tool 2022.2 – Key Feature Enhancements”の例と同じように、ループの回数を固定にして、sobel_axis_RGB24 関数を書き換えた。なお、パフォーマンスに関わるプラグマはコメントアウトした。

// sobel_axis_RGB24.cpp
// 2022/03/20 by marsee
// Up to HD resolution
// 2022/03/23 : Added keep and strb
// 2022/03/26 : axi_vdma と axi dma 用に分けた
// 2022/11/27 : perfomance プラグマを追加
// 2022/11/28 : row_size を 600、col_size を 800 に固定した

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

#include "sobel_axis_RGB24.h"

ap_int<32> sobel_fil(ap_int<32> h_or_v, ap_int<32> x0y0, ap_int<32> x1y0, ap_int<32> x2y0, ap_int<32> x0y1,
        ap_int<32> x1y1, ap_int<32> x2y1, ap_int<32> x0y2, ap_int<32> x1y2, ap_int<32> x2y2);
ap_int<32> conv_rbg2y(ap_int<32> rbg);
ap_int<32> square_root8(ap_int<32> val);

#define row_size 600
#define col_size 800

int sobel_axis_RGB24(hls::stream<ap_axiu<24,1,1,1> >& ins,
        hls::stream<ap_axiu<24,1,1,1> >& outs, int32_t function){
#pragma HLS INTERFACE mode=s_axilite port=function
#pragma HLS INTERFACE mode=axis register_mode=both port=outs register
#pragma HLS INTERFACE mode=axis register_mode=both port=ins register
#pragma HLS INTERFACE mode=s_axilite port=return

    ap_axiu<24,1,1,1> pix;
    ap_axiu<24,1,1,1> sobel;
    ap_int<32> sobel_val, sobel_h_val, sobel_v_val;

    ap_int<32> line_buf[2][1920]; // Up to HD resolution
//#pragma HLS array_partition variable=line_buf block factor=2 dim=1

    ap_int<32> pix_mat[3][3];
//#pragma HLS array_partition variable=pix_mat complete

    LOOP_WAIT_USER : do {   // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
        if(function==ORG_IMGwAxiDma || function==SOBELwAxiDma)
            break;
    } while(pix.user == 0);

    LOOP_Y: for(int y=0; y<row_size; y++){
#pragma HLS LOOP_TRIPCOUNT avg=600 max=600 min=600
//#pragma HLS performance target_ti=800
        LOOP_X: for(int x=0; x<col_size; x++){
#pragma HLS LOOP_TRIPCOUNT avg=800 max=800 min=800
//#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix; // AXI4-Stream からの入力

            LOOP_PIX_MAT_K: for(int k=0; k<3; k++){
                LOOP_PIX_MAT_M: 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];
            ap_int<32> y_val = conv_rbg2y(pix.data);
            pix_mat[2][2] = y_val;

            line_buf[0][x] = line_buf[1][x];    // 行の入れ替え
            line_buf[1][x] = y_val;

            sobel_h_val = sobel_fil(HORIZONTAL, pix_mat[0][0], pix_mat[0][1], pix_mat[0][2],
                                                pix_mat[1][0], pix_mat[1][1], pix_mat[1][2],
                                                pix_mat[2][0], pix_mat[2][1], pix_mat[2][2]);
            sobel_v_val = sobel_fil(VERTICAL,   pix_mat[0][0], pix_mat[0][1], pix_mat[0][2],
                                                pix_mat[1][0], pix_mat[1][1], pix_mat[1][2],
                                                pix_mat[2][0], pix_mat[2][1], pix_mat[2][2]);
            sobel_val = square_root8(sobel_h_val*sobel_h_val + sobel_v_val*sobel_v_val);
            sobel.data = (sobel_val<<16)+(sobel_val<<8)+sobel_val;

            if(x<2 || y<2)
                sobel.data = 0;

            if(function==ORG_IMGwAxiVdma || function == SOBELwAxiVdma){
                if(x==0 && y==0) // 最初のピクセル
                    sobel.user = 1;
                else
                    sobel.user = 0;
                if(x == (col_size-1)) // 行の最後
                    sobel.last = 1;
                else
                    sobel.last = 0;
            }else{
                sobel.user = 0;
                sobel.last = pix.last;
            }
            sobel.keep = 0x7;
            sobel.strb = 0x7;
            if(function==SOBELwAxiVdma || function==SOBELwAxiDma)
                outs << sobel;
            else
                outs << pix;
        }
    }
    return(0);
}

// RBGからYへの変換
// RBGのフォーマットは、{R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y =  0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
ap_int<32> conv_rbg2y(ap_int<32> rbg){
    ap_int<32> r, g, b, y_f;
    ap_int<32> y;

    b = rbg & 0xff;
    g = (rbg>>8) & 0xff;
    r = (rbg>>16) & 0xff;

    y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
    y = y_f >> 8; // 256で割る

    return(y);
}

// sobel filter
// HORZONTAL
// x0y0 x1y0 x2y0  1  2  1
// x0y1 x1y1 x2y1  0  0  0
// x0y2 x1y2 x2y2 -1 -2 -1
// VERTICAL
// x0y0 x1y0 x2y0  1  0 -1
// x0y1 x1y1 x2y1  2  0 -2
// x0y2 x1y2 x2y2  1  0 -1
ap_int<32> sobel_fil(ap_int<32> h_or_v, ap_int<32> x0y0, ap_int<32> x1y0, ap_int<32> x2y0, ap_int<32> x0y1,
        ap_int<32> x1y1, ap_int<32> x2y1, ap_int<32> x0y2, ap_int<32> x1y2, ap_int<32> x2y2){
    ap_int<32> y;

    if(h_or_v == HORIZONTAL){
        y = x0y0 + 2*x1y0 + x2y0 - x0y2 - 2*x1y2 - x2y2;
    } else {
        y = x0y0 - x2y0 + 2*x0y1 - 2*x2y1 + x0y2 - x2y2;
    }
    if(y<0)
        y = -y;
        //y = 0;
    else if(y>255) // 8 bits
        y = 255;
    return(y);
}

// square_root8
// 8 bit幅のsquare_rootを求める
ap_int<32> square_root8(ap_int<32> val){
    ap_int<32> temp = 0;
    ap_int<32> square;

    for(int i=7; i>=0; --i){
        temp += (1 << i);
        square = temp * temp;

        if(square > val){
            temp -= (1 << i);
        }
    }

    return(temp);
}


このソースコードで C コードの合成を行った。
結果を示す。
Vitis_HLS_2022_2_18_221128.png

1 クロックで 1 ピクセル処理をパフォーマンスに関するプラグマなしの状態で達成できてしまった。
Vitis HLS も進化しているのだね。
このソースコードは、ハードウエアを想定しているコードなので、想定していないコードで試す必要があるようだ。

C/RTL 協調シミュレーションを行ったが、問題なく 1 クロックで 1 ピクセル処理をパフォーマンスに関するプラグマなしの状態で達成できている。
Vitis_HLS_2022_2_19_221128.png
  1. 2022年11月28日 04:56 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

Vitis HLS 2022.2 の新機能を確かめる1(performance プラグマ)

What’s New in the Vitis HLS Tool 2022.2 – Key Feature Enhancements”を参考に、Vitis HLS 2022.2 の新機能を確かめてみよう。最初は performance プラグマだ。

RGB 24 ビット・データ入出力対応のソーベル・フィルタを KV260 で試してみよう。
Vitis HLS 2022.2 で Vitis_HLS/KIRA_KV260/2022.2/sobel_axis_RGB24 プロジェクトを作成したのだが、以前はダメだったPart 指定時のボードの指定ができるようになったようだ。
Vitis_HLS_2022_2_1_221127.png

さて、ソースコードは”RGB 24 ビット・データ入出力対応のソーベル・フィルタを Vitis HLS 2021.2 で作成する3”で使用したソースコードをそのまま使用したが、array_partition、PIPELINE プラグマはコメントアウトした。
Vitis_HLS_2022_2_2_221127.png

このソースコードで C コードの合成を行った。結果を示す。
Vitis_HLS_2022_2_3_221127.png

Performance & Resouce Estimates の sobel_axis_RGB_Pipelinue_LOOP_Y_LOOP_X の LOOP_Y_LOOP_X のインターバルは 2 になっている。1 にする必要がある。
しかし、プラグマを入れなくても Vitis HLS はだいぶ性能がチューニングされているな。。。

次に、LOOP_X の前の行に”#pragma HLS performance target_ti=800”を挿入した。
Vitis_HLS_2022_2_4_221127.png

C コードの合成を行った。結果を示す。
結果は変わらなかった。
Vitis_HLS_2022_2_5_221127.png

line_buf[2][1920] の ”#pragma HLS array_partition variable=line_buf block factor=2 dim=1”を挿入した。
Vitis_HLS_2022_2_6_221127.png

C コードの合成を行った。結果を示す。
レイテンシが短くなって約半分になった。
Vitis_HLS_2022_2_7_221127.png

paformance プラグマだけを入れた時の Function Call Graph (左側)と更に array_partition プラグマを入れた時の Function Call Graph (右側)を示す。
Vitis_HLS_2022_2_8_221127.pngVitis_HLS_2022_2_9_221127.png

paformance プラグマだけを入れて C/RTL 協調シミュレーションを行った。
やはり、2 クロックで 1 ピクセル処理となっていた。
Vitis_HLS_2022_2_10_221127.png

C/RTL 協調シミュレーション波形を示す。
Vitis_HLS_2022_2_11_221127.png

paformance プラグマと array_partition プラグマを入れて C/RTL 協調シミュレーションを行った。
1 クロックで 1 ピクセルの処理ができている。
Vitis_HLS_2022_2_12_221127.png

C/RTL 協調シミュレーション波形を示す。
Vitis_HLS_2022_2_13_221127.png

paformance プラグマだけを入れた時の Implementation 結果 (左側)と更に array_partition プラグマを入れた時の Implementation 結果 (右側)を示す。
Vitis_HLS_2022_2_14_221127.pngVitis_HLS_2022_2_15_221127.png

このソースコードでは、performance プラグマは効いていないようだ。しかし、Vitis HLS 2022.2 は最初からかなり最適化が効いている。
  1. 2022年11月27日 06:55 |
  2. Vitis HLS
  3. | トラックバック:0
  4. | コメント:0

”実践的!FPGA開発セミナー vol.16”のスライド PDF を公開します

”実践的!FPGA開発セミナー vol.16”のスライド PDF を公開します。

今日は時間の関係上、すべてのスライドを説明できないかもしれません?もしくはさらっと流す可能性があります。
スライドには該当するFPGAの部屋のブログ記事へのリンクが張ってあるので、リンクをクリックしてより詳しい説明をご覧ください。

本日はよろしくお願いいたします。
  1. 2022年11月24日 17:00 |
  2. Vitis
  3. | トラックバック:0
  4. | コメント:0

”実践的!FPGA開発セミナー vol.16”で発表します

今日、”実践的!FPGA開発セミナー vol.16”が開催されますが、そこで私が発表します。
皆さん、よろしくお願いいたします。

題は、”Vitisをエッジで思う存分活用したい。。。w”です。近頃ブログで書いている Vitis のアクセラレーション・アプリケーションの話題をお話ししたいと思います。

ふるってご参加ください。まだ申し込み可能かもしれません?
なお、午後 5 時ころに今日の発表スライド PDF を公開します。
  1. 2022年11月24日 04:51 |
  2. Vitis
  3. | トラックバック:0
  4. | コメント:0

kv260_median アクセラレーション・プラットフォームに Vitis-AI の DPU を追加する15

kv260_median アクセラレーション・プラットフォームに Vitis-AI の DPU を追加する14”の続き。

kv260_median アクセラレーション・プラットフォームに Vitis-AI の DPU を追加してみたいということで、前回は、できあがった compiled ディレクトリを KV260 の Petalinux 2022.1 にアップロードした。アプリケーション・ソフトウエアを git clone し、ビルドした。古いモジュールをアンロードして、dpuprj をロードした。アプリケーション・ソフトウエアを実行したところ成功した。今回は、前回までで”KV260向けにVitisプラットフォームを作成してDPUを動かす その2 (Vitis 2022.1 + Vitis-AI v2.5)”のチュートリアルは成功した。そこで、kv260_median アクセラレーション・プラットフォームに含まれるメディアン・フィルタを使って、ノイズを除去してから YOLOV4 で物体を認識してみたところ成功した。

前回使用したソフトウエアは vitis_ai_dpu_yolo/demo_yolov4.cpp だが、これをベースに”kv260_median_platform のメディアン・フィルタを KV260 の Petalinux から動作させる23”の median_pf.cpp のコードを追加して、median_demo_yolov4.cpp を作成した。

build.sh は demo_yolov4 を median_demo_yolov4 に置換して、使用する。

KV260 の Petalinux 2022.1 の ~/kv260_median/dpu ディレクトリに vitis_ai_dpu_median_yolo ディレクトリを作成した。
kv260_median_DPU_113_221123.png

vitis_ai_dpu_median_yolo ディレクトリに median_demo_yolov4.cpp と変更した build.sh を用意した。
ノイズ入りの test2.jpg をコピーした。

vitis_ai_dpu_median_yolo ディレクトリに u-dma-buf.ko を用意した。

ビルドを行った。
cd ~/kv260_median/dpu/vitis_ai_dpu_median_yolo
sh build.sh


median_demo_yolov4 実行形式ファイルが生成された。

アプリケーションを動作させるには /run/media/mmcblk0p1/ 以下にも dpu.xclbin が存在している必要があるそうなのでコピーする。
sudo mkdir -p /run/media/mmcblk0p1/
sudo cp dpu.xclbin /run/media/mmcblk0p1/


u-dma-buf をロードする。
sudo insmod u-dma-buf.ko udmabuf0=3000000

すでにロードされているハードウェアをアンロードして、dpuprj をロードする。
sudo xmutil unloadapp
sudo xmutil loadapp dpuprj


ビルドで作成された demo_yolo4 を実行した。
sudo ./median_demo_yolov4 ../compiled/yolov4_leaky_416_tf.prototxt ../compiled/yolov4_leaky_416_tf.xmodel test2.jpg image
成功して result.jpg が生成された。
kv260_median_DPU_114_221123.png

xilinx-k26-starterkit-20221:~/kv260_median/dpu/vitis_ai_dpu_median_yolo$ sudo ./median_demo_yolov4 ../compiled/yolov4_leaky_416_tf.prototxt ../compiled/yolov4_leaky_416_tf.xmodel test2.jpg image
../compiled/yolov4_leaky_416_tf.prototxt ../compiled/yolov4_leaky_416_tf.xmodel test2.jpgModel Initialize Done
phys_addr = 40000000
in_img_total_bytes = 1441792
tvmonitor 0.985391 151.825 451.825 50.2466 335.823
tvmonitor 0.599225 0.870854 197.449 150.53 287.931
mouse 0.939509 462.855 526.316 313.673 346.846
mouse 0.877212 624.63 663.122 234.817 254.938
mouse 0.318674 621.84 666.77 227.559 259.926
keyboard 0.998828 277.435 500.512 314.605 444.413
cell phone 0.332537 354.317 476.452 415.798 503.778


kv260_median_DPU_115_221123.png

result.jpg を示す。
メガネケースは cell phone として認識されているようだ。
kv260_median_DPU_116_221123.jpg

median_demo_yolov4.cpp を貼っておく。公開を許可していただいた lp6m さんに感謝いたします。

// median_demo_yolo4.cpp
// 2022/11/23
// Combined demo_yolo4.cpp and median_pf.cpp.
// https://github.com/lp6m/vitis_ai_dpu_yolo/blob/master/demo_yolov4.cpp
// https://marsee101.blog.fc2.com/blog-entry-5747.html

#include <glog/logging.h>
#include <google/protobuf/text_format.h>

#include <cmath>
#include <iostream>
#include <numeric>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <vitis/ai/dpu_task.hpp>
#include <vitis/ai/nnpp/yolov3.hpp>
#include <fstream>
#include <map>
#include <vector>
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

#define BLOCK_SIZE    4096
#define MIDEAIN_REG_ADDR        0x80020000
#define AXI_DMA_REG_ADDR        0x80010000
#define IMAGE_WIDTH         800
#define IMAGE_HEIGHT            600
#define MAT_IMGAGE_BUF      (IMAGE_WIDTH*IMAGE_HEIGHT*3)

#define MM2S_CONTROL_REG    0x00
#define MM2S_STATUS_REG (0x4 >> 2)
#define MM2S_START_ADDR (0x18 >> 2)
#define MM2S_LENGTH_REG (0x28 >> 2)
#define S2MM_CONTROL_REG    (0x30 >> 2)
#define S2MM_STATUS_REG (0x34 >> 2)
#define S2MM_DESTINATION_ADDR   (0x48 >> 2)
#define S2MM_LENGTH_REG (0x58 >> 2)
// bits 1 - idle
#define MM2S_IDLE_MASK  0x2
#define S2MM_IDLE_MASK  0x2

#define MEDIAN_CONTROL      0x00
#define MEDIAN_FUNCTION_R   (0x18 >> 2)
#define MEDIAN_ROW_SIZE     (0x20 >> 2)
#define MEDIAN_COL_SIZE     (0x28 >> 2)

volatile uint32_t *reg;

using namespace std;
using namespace cv;

// The parameters of yolov3_voc, each value could be set as actual needs.
// Such format could be refer to the prototxts in /etc/dpu_model_param.d.conf/.

const string readFile(const char *filename){
  ifstream ifs(filename);
  return string(istreambuf_iterator<char>(ifs),
                istreambuf_iterator<char>());
}

class YoloRunner{
  public:
    unique_ptr<vitis::ai::DpuTask> task;
    vitis::ai::proto::DpuModelParam modelconfig;
    cv::Size model_input_size;
    vector<vitis::ai::library::InputTensor> input_tensor;
    struct bbox{
      int label;
      float xmin;
      float ymin;
      float width;
      float height;
      float score;
      bbox(vitis::ai::YOLOv3Result::BoundingBox yolobbox, float img_width, float img_height){
        this->label = yolobbox.label;
        this->score = yolobbox.score;
        // does not clamp here
        this->xmin = yolobbox.x * img_width;
        this->ymin = yolobbox.y * img_height;
        this->width = yolobbox.width * img_width;
        this->height = yolobbox.height * img_height;
      }
    };

  public: YoloRunner(const char* modelconfig_path, const char* modelfile_path){
    const string config_str = readFile(modelconfig_path);
    auto ok = google::protobuf::TextFormat::ParseFromString(config_str, &(this->modelconfig));
    if (!ok) {
      cerr << "Set parameters failed!" << endl;
      abort();
    }
    this->task = vitis::ai::DpuTask::create(modelfile_path);
    this->input_tensor = task->getInputTensor(0u);
    int width = this->input_tensor[0].width;
    int height = this->input_tensor[0].height;
    this->model_input_size = cv::Size(width, height);
    this->task->setMeanScaleBGR({0.0f, 0.0f, 0.0f},
                        {0.00390625f, 0.00390625f, 0.00390625f});
  }
  private: cv::Mat Preprocess(cv::Mat img){
    cv::Mat resized_img;
    cv::resize(img, resized_img, this->model_input_size);
    return resized_img;
  }
  public: vector<bbox> Run(cv::Mat img){
    cv::Mat resized_img = this->Preprocess(img);
    vector<int> input_cols = {img.cols};
    vector<int> input_rows = {img.rows};
    vector<cv::Mat> inputs = {resized_img};
    task->setImageRGB(inputs);
    task->run(0);

    auto output_tensor = task->getOutputTensor(0u);
    auto results = vitis::ai::yolov3_post_process(
        input_tensor, output_tensor, this->modelconfig, input_cols, input_rows);
    auto result = results[0]; //batch_size is 1
    vector<bbox> bboxes;
    for(auto& yolobbox: result.bboxes){
      bboxes.push_back(bbox(yolobbox, img.cols, img.rows));
    }
    return bboxes;
  }

};

std::string get_basename(std::string& path) {
  int l = path.find_last_of('/')+1;
  int r = path.find_last_of('.');
    return path.substr(l, r-l);
}

map<string, string> bbox_to_map(YoloRunner::bbox bbox, int frame_id){
  map<string, string> res;
  res["frame_id"] = to_string(frame_id);
  res["prob"] = to_string(bbox.score);
  res["x"] = to_string(bbox.xmin);
  res["y"] = to_string(bbox.ymin);
  res["width"] = to_string(bbox.width);
  res["height"] = to_string(bbox.height);
  return res;
}

int main(int argc, char* argv[]) {
    int fd;
    volatile uint32_t *median_reg, *axi_dma_reg;
    volatile uint8_t *pict_buf;
    uint32_t phy_addr;
    uint32_t phy_addr_base;
    int addr, wd;
    uint32_t write_data;
    cv::Mat in_img, median_img;
    int fd_udmabuf;
    u_int32_t fd_paddr;
    unsigned char  attr[1024];
    unsigned long  phys_addr;

    if (argc != 5) {
    cerr << "usage ./a.out config(.prototxt) modelfile(.xmodel) image(.jpg) image" << endl;
    }
    char* configfile  = argv[1];
    char* modelfile = argv[2];
    string img_or_video_file = string(argv[3]);

    cout << configfile << " " << modelfile << " " << img_or_video_file;
    auto runner = YoloRunner(configfile, modelfile);
    cout << "Model Initialize Done" << endl;
    std::string img_or_video_mode = std::string(argv[4]);
    if (img_or_video_mode == "image") {
        in_img = cv::imread(img_or_video_file);
        median_img.create(cv::Size(in_img.cols, in_img.rows), CV_8UC3);

        fd = open("/dev/mem", O_RDWR | O_SYNC);
        if (fd == -1){
            fprintf(stderr, "/dev/mem open error\n");
            exit(-1);
        }
        
        // median_filter registers
        median_reg = (uint32_t *)mmap(NULL, BLOCK_SIZE,
                    PROT_READ | PROT_WRITE, MAP_SHARED,
                    fd, MIDEAIN_REG_ADDR );
        if ((int64_t)median_reg == -1){
            fprintf(stderr,"/dev/mem map error for median_filter registers\n");
            exit(-1);
        }

        // axi_dma registers
        axi_dma_reg = (uint32_t *)mmap(NULL, BLOCK_SIZE,
                    PROT_READ | PROT_WRITE, MAP_SHARED,
                    fd, AXI_DMA_REG_ADDR );
        if ((int64_t)axi_dma_reg == -1){
            fprintf(stderr,"/dev/mem map error for axi_dma registers\n");
            exit(-1);
        }
        
        // udmabuf0
        fd_udmabuf = open("/dev/udmabuf0", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled. 
        if (fd_udmabuf == -1){
            fprintf(stderr, "/dev/udmabuf0 open errorn");
            exit(-1);
        }

        // phys_addr of udmabuf0
        fd_paddr = open("/sys/class/u-dma-buf/udmabuf0/phys_addr", O_RDONLY);
        if (fd_paddr == -1){
            fprintf(stderr, "/sys/class/u-dma-buf/udmabuf0/phys_addr open errorn");
            exit(-1);
        }
        read(fd_paddr, (void *)attr, 1024);
        sscanf((const char *)attr, "%lx", &phys_addr);  
        close(fd_paddr);
        printf("phys_addr = %x\n", (unsigned int)phys_addr);

        uint32_t total_bytes = in_img.total()*in_img.channels();
        uint32_t in_img_total_bytes = (in_img.total()*in_img.channels()+4096) & 0xfffff000; // 4k byte boundary
        printf("in_img_total_bytes = %d\n", in_img_total_bytes);

        pict_buf = (volatile uint8_t *)mmap(NULL, in_img_total_bytes*2, PROT_READ|PROT_WRITE, MAP_SHARED, fd_udmabuf, 0);
        if (pict_buf == MAP_FAILED){
            fprintf(stderr, "org_mat mmap error\n");
            exit(-1);
        }
        
        // Copy Mat data from in_img to org_mat
        uint8_t *in_img_data = in_img.data;
        for(int i=0; i<total_bytes; i++){
            pict_buf[i] = in_img_data[i];
        }
        
        // Halting Run DMA
        axi_dma_reg[MM2S_CONTROL_REG] = 1; // MM2S DMA Controll Reg. Run
        axi_dma_reg[S2MM_CONTROL_REG] = 1; // S2MM DMA Control Reg. Run
        
        uint32_t median_mat_addr = (uint32_t)phys_addr+in_img_total_bytes;
        uint32_t org_mat_addr = (uint32_t)phys_addr;
        // axi dma settings
        axi_dma_reg[S2MM_DESTINATION_ADDR] = median_mat_addr;
        axi_dma_reg[MM2S_START_ADDR] = org_mat_addr;
        axi_dma_reg[S2MM_LENGTH_REG] = total_bytes;
        axi_dma_reg[MM2S_LENGTH_REG] = total_bytes;
        
        // median filter start
        median_reg[MEDIAN_COL_SIZE] = in_img.cols;
        median_reg[MEDIAN_ROW_SIZE] = in_img.rows;
        median_reg[MEDIAN_FUNCTION_R] = 3;  // median filter for AXI DMA
        median_reg[MEDIAN_CONTROL] = 1;         // ap_start
        
        // DMA completion detection
        uint32_t mm2s_status_reg = axi_dma_reg[MM2S_STATUS_REG] & MM2S_IDLE_MASK;
        while(mm2s_status_reg != MM2S_IDLE_MASK){
            mm2s_status_reg = axi_dma_reg[MM2S_STATUS_REG] & MM2S_IDLE_MASK;
        }
        
        uint32_t s2mm_status_reg = axi_dma_reg[S2MM_STATUS_REG] & S2MM_IDLE_MASK;
        while(s2mm_status_reg != S2MM_IDLE_MASK){
            s2mm_status_reg = axi_dma_reg[S2MM_STATUS_REG] & S2MM_IDLE_MASK;
        }
        
        // Copy median image data from median_mat to megian_img
        uint8_t *median_img_data = median_img.data;
        for(int i=0; i<total_bytes; i++){
            median_img_data[i] = pict_buf[in_img_total_bytes+i];
        }
        cv::Mat img;
        resize(median_img, img, cv::Size(), 768.0/median_img.cols, 576.0/median_img.rows);

        vector<YoloRunner::bbox> bboxes = runner.Run(median_img);
        string label_names[] = {"person", "bicycle", "car", "motorbike", "aeroplane", "bus", "train", "truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "sofa", "pottedplant", "bed", "diningtable", "toilet", "tvmonitor", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush"};
        for (auto& box : bboxes) {
            int label = box.label;
            float confidence = box.score;
            float xmin = max(0.0f, box.xmin);
            float ymin = max(0.0f, box.ymin);
            float xmax = min(box.xmin + box.width, (float)img.cols-1.0f);
            float ymax = min(box.ymin + box.height, (float)img.rows-1.0f);
            cout << label_names[box.label] << " " << box.score << " " << xmin << " " << xmax << " " << ymin << " " << ymax << endl;
            rectangle(img, Point(xmin, ymin), Point(xmax, ymax),
                    Scalar(0, 255, 0), 3, 1, 0);
    }
    imwrite("result.jpg", img);
    } else {
    cerr << "unknown mode :" << img_or_video_mode << endl;
    }


    return 0;
}


変更した build.sh を貼っておく。

#
# Copyright 2019 Xilinx Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# 2022/11/23 : Changed demo_yolov4 to median_demo_yolov4. by marsee

result=0 && pkg-config --list-all | grep opencv4 && result=1
if [ $result -eq 1 ]; then
    OPENCV_FLAGS=$(pkg-config --cflags --libs-only-L opencv4)
else
    OPENCV_FLAGS=$(pkg-config --cflags --libs-only-L opencv)
fi

CXX=${CXX:-g++}
$CXX -std=c++17 -O3 -I. -o median_demo_yolov4 median_demo_yolov4.cpp -lglog -lvitis_ai_library-xnnpp -lvitis_ai_library-model_config -lprotobuf -lvitis_ai_library-dpu_task ${OPENCV_FLAGS} -lopencv_core -lopencv_video -lopencv_videoio -lopencv_imgproc -lopencv_imgcodecs -lopencv_highgui

  1. 2022年11月23日 07:08 |
  2. Vitis
  3. | トラックバック:0
  4. | コメント:0

kv260_median アクセラレーション・プラットフォームに Vitis-AI の DPU を追加する14

kv260_median アクセラレーション・プラットフォームに Vitis-AI の DPU を追加する13”の続き。

kv260_median アクセラレーション・プラットフォームに Vitis-AI の DPU を追加してみたいということで、前回は、コンパイルと prototxtの用意 を行った。今回は、できあがった compiled ディレクトリを KV260 の Petalinux 2022.1 にアップロードした。アプリケーション・ソフトウエアを git clone し、ビルドした。古いモジュールをアンロードして、dpuprj をロードした。アプリケーション・ソフトウエアを実行したところ成功した。

参考にさせていただくのは、”KV260向けにVitisプラットフォームを作成してDPUを動かす その2 (Vitis 2022.1 + Vitis-AI v2.5)”だ。

できあがった Vitis-AI/tf_yolov4_coco_416_416_60.3G_2.5/compiled ディレクトリごと FileZIlla で KV260 の Petalinux 2022.1 の ~/kv260_median/dpu ディレクトリに SFTP でアップロードした。
kv260_median_DPU_106_221121.png

なお、~/kv260_median/dpu ディレクトリは”kv260_median アクセラレーション・プラットフォームに Vitis-AI の DPU を追加する9”で作成済みだ。

ビルド用ツールをインストールした。
sudo dnf install -y cmake g++ g++-symlinks gcc-symlinks binutils pkgconfig git
すでにインストールしてあったモジュールも多いので、pkgconfig と git がインストールされた。
kv260_median_DPU_107_221121.png

アプリケーションを動作させるには /run/media/mmcblk0p1/ 以下にも dpu.xclbin が存在している必要があるそうなのでコピーする。
sudo mkdir -p /run/media/mmcblk0p1/
sudo cp dpu.xclbin /run/media/mmcblk0p1/

kv260_median_DPU_108_221121.png

lp6m さんのアプリケーション・ソフトウエアを git clone して、ビルドする。
git clone https://github.com/lp6m/vitis_ai_dpu_yolo
cd vitis_ai_dpu_yolo
sh build.sh

kv260_median_DPU_109_221121.png

ロードされているモジュールをアンロードして、dpuprj をロードした。
sudo xmutil unloadapp
sudo xmutil loadapp dpuprj


ビルドで作成された demo_yolo4 を実行した。
./demo_yolov4 ../compiled/yolov4_leaky_416_tf.prototxt ../compiled/yolov4_leaky_416_tf.xmodel dog.jpg image
成功して result.jpg が生成された。
kv260_median_DPU_110_221121.png

~/kv260_median/dpu/vitis_ai_dpu_yolo ディレクトリの中身を示す。
kv260_median_DPU_111_221121.png

result.jpg を示す。
kv260_median_DPU_112_221121.jpg

demo_yolo4 実行時のログを貼っておく。

xilinx-k26-starterkit-20221:~/kv260_median/dpu/vitis_ai_dpu_yolo$ ./demo_yolov4 ../compiled/yolov4_leaky_416_tf.prototxt ../compiled/yolov4_leaky_416_tf.xmodel dog.jpg image
../compiled/yolov4_leaky_416_tf.prototxt ../compiled/yolov4_leaky_416_tf.xmodel dog.jpgModel Initialize Done
bicycle 0.951898 132.472 550.138 140.873 414.652
car 0.763319 458.46 700.775 71.8596 174.588
truck 0.558414 472.915 687.069 72.4816 169.532
dog 0.988311 133.963 300.746 210 548.74

  1. 2022年11月22日 04:21 |
  2. Vitis
  3. | トラックバック:0
  4. | コメント:0
»