【HLS教程】HLS入门与精通

总纲

HLS和FPGA实现是怎么一回事

HLS已有库说明

HLS语法讲解与实例

HLS自定义模板

HLS实现OpenCV函数

前言

主要还是工作中用到,HLS毕竟还是小众模版库,就目前来看,连Verilog普及都没有,希望原厂能有的放矢,一蹴而就。学语言,是程序员的一把斧,需要熟练与精通。希望今天的付出值得。

HLS的出现提高了FPGA实现逻辑的复杂程度,但同时又带来一些不确定的结果,总的来说,提高了实现的效率。

内容

1-HLS和FPGA实现是怎么一回事

FPGA是一种集成电路IC,可以编写不同算法的介质。现代FPGA包含200万个逻辑单元,可以实现不同的算法。

使用HLS编译器,很重要的一点是知道FPGA使用资源、以及他们是如何在FPGA上执行的。

FPGA架构主要由几部分组成:LUT、FF、Wires、IO pads.


(待续)


2-HLS已有库说明

  • 介绍

HLS视频库是通过Vivado HLS调用C/C++库加速机器视觉、图像处理在FPGA上实现。它包括通用的数据结构、OpenCV接口、AXI4-Stream接口和视频处理函数。视频库使用OpenCV库作为参考模型,大多数视频处理功能与相应的opencv函数有类似的界面和同等的功能。

简单来说,有两个头文件可以帮助我们进行HLS 视频库调用:

#include <hls_video.h>
#include <hls_opencv.h>

当然,这两个文件中包含了视频处理的一些头文件,具体可以查看安装目录下Include,也可以直接在工程中点击对应头文件,查看源文件。



  • 视频库数据结构

下面是HLS视频库基本数据结构的模板类,是针对FPGA进行优化设计。

hls::Mat<ROWS, COLS, T>
hls::Scalar<N, T>
hls::Window<ROWS, COLS, T>
hls::LineBuffer<ROWS, COLS, T>

  • 视频库函数

1)OpenCV接口函数

OpenCV接口函数是保证重建的OpenCV可以在FPGA实现,满足一种以AXI4-Streaming流的数据处理方式。

IplImage2AXIvideo
AXIvideo2IplImage
cvMat2AXIvideo
AXIvideo2cvMat
CvMat2AXIvideo
AXIvideo2CvMat
IplImage2hlsMat
hlsMat2IplImage
cvMat2hlsMat
hlsMat2cvMat
CvMat2hlsMat
hlsMat2CvMat
CvMat2hlsWindow
hlsWindow2CvMat


2)AXI4-Stream接口函数

IO函数是将HLS::Mat格式转化为AXI4-Stream设计的。

hls::AXIvideo2Mat
hls::Mat2AXIvideo


3)视频处理函数

下面函数是根据OpenCV相同功能进行加速设计的库函数。

hls::AbsDiff
hls::AddS
hls::AddWeighted
hls::And
hls::Avg
hls::AvgSdv
hls::Cmp
hls::CmpS
hls::CornerHarris
hls::CvtColor
hls::Dilate
hls::Duplicate
hls::EqualizeHist
hls::Erode
hls::FASTX
hls::Filter2D
hls::GaussianBlur
hls::Harris
hls::HoughLines2
hls::Integral
hls::InitUndistortRectifyMap
hls::Max
hls::MaxS
hls::Mean
hls::Merge
hls::Min
hls::MinMaxLoc
hls::MinS
hls::Mul
hls::Not
hls::PaintMask
hls::Range
hls::Remap
hls::Reduce
hls::Resize
hls::Set
hls::Scale
hls::Sobel
hls::Split
hls::SubRS
hls::SubS
hls::Sum
hls::Threshold
hls::Zero



(待续)


3-HLS语法讲解与实例

关于数据类型,HLS有自己独特的定义方式,也包含C++的大部分数据类型,但是,有些类型还没有实现可综合。

HLS Scale

hls::Scale: 该标量是HLS像素点表示方法;

HLS Mat

hls::Mat<int ROWS, int COLS, int T>: 该模板类是HLS视频处理库代表一幅图像。



(待续)


4-HLS自定义模板

例一:
// 单通道转双通道
void gray2YUYV(
		IMAGE_C1 &gray,
		IMAGE_C2 &yuyv
		)
{
    int rows = gray.rows;
    int cols = gray.cols;
    PIXEL_C1 s;
    PIXEL_C2 d;
    for (int row = 0; row < rows; row++) {
        for (int col = 0; col < cols; col++) {
#pragma HLS loop_flatten off
#pragma HLS pipeline II=1
            gray >> s;
            d.val[0] = s.val[0];
            d.val[1] = 128;
            yuyv << d;
        }
    }
}

该摸板函数实现单通道灰度图像转双通道YUV图像。上述函数只实现像素点操作,不需要进行缓存,所以,实现起来相对容易。




(待续)


5-HLS实现OpenCV函数

实例一:

hls::FASTX

该函数实现FAST算法的角点检测,并生成一副Mask图像,或者角点数组。

具体代码:

(PART ONE)

template<int SRC_T,int ROWS,int COLS>
void FASTX(
        Mat<ROWS,COLS,SRC_T>    &_src,
        Mat<ROWS,COLS,HLS_8UC1> &_mask,
        HLS_TNAME(SRC_T)    _threshold,
        bool   _nomax_supression
        )
{
#pragma HLS INLINE
    int flag[16][2]={{3,0},{4,0},{5,1},{6,2},{6,3},{6,4},{5,5},{4,6},
        {3,6},{2,6},{1,5},{0,4},{0,3},{0,2},{1,1},{2,0}};
    FAST_t_opr<16,7>(_src,_mask,_threshold,_nomax_supression,flag);
}
(PART TWO)

//generate array 
template<int PSize,int KERNEL_SIZE,typename T, int N, int SRC_T,int ROWS,int COLS>
void FAST_t_opr(
        Mat<ROWS,COLS,SRC_T>    &_src,
        Point_<T>                    (&_keypoints)[N],
        HLS_TNAME(SRC_T)                    _threshold,
        bool                    _nonmax_supression,
        int                     (&flag)[PSize][2]
        )
{
    typedef typename pixel_op_type<HLS_TNAME(SRC_T)>::T INPUT_T;
    LineBuffer<KERNEL_SIZE-1,COLS,INPUT_T>    k_buf;
    LineBuffer<2,COLS+KERNEL_SIZE,ap_int<16> >         core_buf;
    Window<3,3,ap_int<16> >                            core_win;
    Window<KERNEL_SIZE,KERNEL_SIZE,INPUT_T>       win;
    Scalar<HLS_MAT_CN(SRC_T), HLS_TNAME(SRC_T)>             s;
    int rows= _src.rows;
    int cols= _src.cols;
    assert(rows <= ROWS);
    assert(cols <= COLS);
    int kernel_half=KERNEL_SIZE/2;
    ap_uint<2> flag_val[PSize+PSize/2+1];
    int  flag_d[PSize+PSize/2+1];
#pragma HLS ARRAY_PARTITION variable=flag_val dim=0
#pragma HLS ARRAY_PARTITION variable=flag_d dim=0
    int index=0;
    int offset=KERNEL_SIZE/2;

    if(_nonmax_supression)
    {
        offset=offset+1;
    }
 loop_height: for(HLS_SIZE_T i=0;i<rows+offset;i++) {
    loop_width: for(HLS_SIZE_T j=0;j<cols+offset;j++) {
#pragma HLS LOOP_FLATTEN off
#pragma HLS PIPELINE II=1
            if(i<rows&&j<cols) {
                for(int r= 0;r<KERNEL_SIZE;r++) {
                    for(int c=0;c<KERNEL_SIZE-1;c++) {
                        win.val[r][c]=win.val[r][c+1];//column left shift
                    }
                }
                win.val[0][KERNEL_SIZE-1]=k_buf.val[0][j];
                for(int buf_row= 1;buf_row< KERNEL_SIZE-1;buf_row++) {
                    win.val[buf_row][KERNEL_SIZE-1]=k_buf.val[buf_row][j];
                    k_buf.val[buf_row-1][j]=k_buf.val[buf_row][j];
                }
                //-------
                _src>>s;
                win.val[KERNEL_SIZE-1][KERNEL_SIZE-1]=s.val[0];
                k_buf.val[KERNEL_SIZE-2][j]=s.val[0];
            }
            //------core
            for(int r= 0;r<3;r++)
            {
                for(int c=0;c<3-1;c++)
                {
                    core_win.val[r][c]=core_win.val[r][c+1];//column left shift
                }
            }
            core_win.val[0][3-1]=core_buf.val[0][j];
            for(int buf_row= 1;buf_row< 3-1;buf_row++)
            {
                core_win.val[buf_row][3-1]=core_buf.val[buf_row][j];
                core_buf.val[buf_row-1][j]=core_buf.val[buf_row][j];
            }
            int core=0;
            //output
            //if(i>=KERNEL_SIZE-1&&j>=KERNEL_SIZE-1)
            if(i>=KERNEL_SIZE-1 && i<rows && j>=KERNEL_SIZE-1 & j<cols)
            {
                //process
                bool iscorner=fast_judge<PSize>(win,(INPUT_T)_threshold,flag_val,flag_d,flag,core,_nonmax_supression);
                if(iscorner&&!_nonmax_supression)
                {
                    if(index<N)
                    {
                    _keypoints[index].x=j-offset;
                    _keypoints[index].y=i-offset;
                    index++;
                    }
                }
            }
            if(i>=rows||j>=cols)
            {
                core=0;
            }
            if(_nonmax_supression)
            {
                core_win.val[3-1][3-1]=core;
                core_buf.val[3-2][j]=core;
                if(i>=KERNEL_SIZE&&j>=KERNEL_SIZE&&core_win.val[1][1]!=0)
                {
                    bool iscorner=fast_nonmax(core_win);
                    if(iscorner)
                    {
                    if(index<N)
                    {
                        _keypoints[index].x=j-offset;
                        _keypoints[index].y=i-offset;
                        index++;
                    }
                    }
                }
            }

        }
    }
}



(待续)


注意该函数的使用:

hls::Duplicate(img_0, img_1, img_1_); 该函数可以避免 Hanging Up和read empty.



后续

学习一门语言,编成一系列博客,这个过程可以看出一个工程师的思维逻辑。所以,每一次这样去做的时候,总是不断修正自己的逻辑,达到一种与时俱进的状态。



猜你喜欢

转载自blog.csdn.net/wangbaodong070411209/article/details/78180995
HLS