HLS圖像處理總結(一)

HLS工具

以我的的理解,xilinx將HLS(高層次綜合)定位於更方便的將複雜算法轉化爲硬件語言,經過添加某些配置條件HLS工具能夠把可並行化的C/C++的代碼轉化爲vhdl或verilog,相比於純人工使用vhdl實現圖像算法,該工具綜合出的代碼的硬件資源佔用可能較多,但並無相差太大(見論文:基於HLS的 SURF特徵提取硬件加速單元設計與實現),而純人工用硬件描述語言實現一個複雜的圖像處理算法要求十分深厚的FPGA功底,下面簡單總結下好早以前作的一個在zybo開發板上的HLS圖像處理通路。html

主要從三個工程分析ios

1.sobel邊緣檢測算法

頭文件部分涉及到‘hls_video.h’:主要就作這麼幾件事情,定義頭文件名,導入須要的庫文件,定義參數,重定義數據結構,聲明函數數據結構

#ifndef _TOP_H_
#define _TOP_H_
#include"hls_video.h" //這裏調用能夠綜合的視頻庫
// maximum image size
#define MAX_WIDTH 1920
#define MAX_HEIGHT 1080
// I/O Image Settings
#define INPUT_IMAGE "test_1080p.jpg"
#define OUTPUT_IMAGE "result_1080p.jpg"
#define OUTPUT_IMAGE_GOLDEN "result_1080p_golden.jpg"
// typedef video library core structures
typedef hls::stream<ap_axiu<24,1,1,1> > AXI_STREAM_IN;
typedef hls::stream<ap_axiu<24,1,1,1> > AXI_STREAM_OUT;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
// top level function for HW synthesis
void hls_sobel(AXI_STREAM_IN& src_axi, AXI_STREAM_OUT& dst_axi, int rows, int cols);

#endif

主要函數的具體定義:ide

#include "top.h"
void hls_sobel(AXI_STREAM_IN& input, AXI_STREAM_OUT& output, int rows, int cols) {
#pragma HLS RESOURCE variable=input core=AXI4Stream metadata="-bus_bundle INPUT_STREAM"
#pragma HLS RESOURCE variable=output core=AXI4Stream metadata="-bus_bundle OUTPUT_STREAM"
#pragma HLS INTERFACE ap_none port=cols
#pragma HLS INTERFACE ap_none port=rows
//AP_CONTROL_BUS_AXI(CONTROL_BUS);
//set_directive_interface -mode ap_ctrl_none hls_sobel
#pragma HLS interface ap_ctrl_none port=return

RGB_IMAGE img_0(rows, cols);//輸入圖像的存儲空間
RGB_IMAGE img_1(rows, cols);//輸出圖像內存大小的指定
#pragma HLS DATAFLOW // must use data flow to stream the data
hls::AXIvideo2Mat(input, img_0); //read video stream by frames
hls::Sobel<1,0,3>(img_0, img_1);//use Hls Sobel
//根據模板,<1,0,3>中前兩個部分規定了x方向的檢測仍是y方向的檢測,<1,0>表示的是y方向的檢測,size=3表明sobel算子的維數規定。
//hls::Erode<>();//use Hls Sobel
hls::Mat2AXIvideo(img_1, output); //write the frames to video stream
}

除了優化部分之外,主要作的就是定義輸入輸出圖像的內存空間,而後將輸入的數據格式轉換爲mat格式,用於實現ip1image的處理,而後就是直接調用庫裏的sobel函數,最後圖像轉化爲video-stream輸出便可。函數

須要注意優化部分:視頻輸入輸出接口優化爲stream格式,將控制接口分配到AXI4 Lite接口,指定「rows」可經過AXI4-Lite接口進行訪問而且聲明在函數執行過程當中 「rows」不會改變,其實這幾句優化語句中最關鍵的一條指令爲啓用數據流優化:#pragma HLS dataflow,它使得任務之間以流水線的方式執行,還有就是#pragma HLS interface ap_ctrl_none port=return。工具

測試函數:oop

#include "top.h"
#include "opencv/cv.h"
#include "opencv/cxcore.h"
#include "opencv/highgui.h"
#include "hls_opencv.h"
int main (int argc, char** argv) //argc 是 argument count的縮寫,表示傳入main函數的參數個數;
//argv 是 argument vector的縮寫,表示傳入main函數的參數序列或指針,而且第一個參數argv[0]必定是程序的名稱,而且包含了程序所在的完整路徑,因此確切的說須要咱們輸入的main函數的參數個數應該是argc-1個;
{
    IplImage* src = cvLoadImage(INPUT_IMAGE);//爲輸入圖像定義好存儲空間
    IplImage* dst = cvCreateImage(cvGetSize(src), src->depth, src->nChannels);//爲輸出圖像定義好存儲空間
    AXI_STREAM_IN src_axi;//定義輸入輸出AXI數據流
    AXI_STREAM_OUT dst_axi;//
    IplImage2AXIvideo(src, src_axi); //將圖像轉爲視頻流結構
    hls_sobel(src_axi, dst_axi, src->height, src->width);
    AXIvideo2IplImage(dst_axi, dst);
    cvSaveImage(OUTPUT_IMAGE, dst);
    cvReleaseImage(&src);
    cvReleaseImage(&dst);
}//不加符號

注意頭文件導入時路徑的問題,思路是定義好圖像空間後定義視頻圖像的變量,而後將輸入的圖像轉換爲視頻流結構,用本身寫的函數處理後,觀察處理後的結果便可測試

最後生成IP核的摸樣優化

 

2.skin-detect部分

頭文件

#ifndef _TOP_H_
#define _TOP_H_

#include "hls_video.h"
// maximum image size
#define MAX_WIDTH  1920
#define MAX_HEIGHT 1080

typedef unsigned char uchar;

// I/O Image Settings
#define INPUT_IMAGE           "test.jpg"
#define OUTPUT_IMAGE          "result_1080p.bmp"
#define OUTPUT_IMAGE_GOLDEN   "test_img1.jpg"

// typedef video library core structures
typedef hls::stream<ap_axiu<24,1,1,1> >               AXI_STREAM;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3>     RGB_IMAGE;
typedef hls::Scalar<3, unsigned char>                 RGB_PIXEL;

//函數聲明,,,用到了命名空間,這就是一個虛擬文件夾的意思
namespace hls
{
    void hls_skin_dection(RGB_IMAGE& src, RGB_IMAGE& dst,int rows, int cols,
                          int y_lower,int y_upper,int cb_lower,int cb_upper,int cr_lower,int cr_upper);
}

void ImgProcess_Top(AXI_STREAM& input, AXI_STREAM& output,int rows, int cols,
        int y_lower,int y_upper,int cb_lower,int cb_upper,int cr_lower,int cr_upper);

#endif

涉及到的mat以及scalar格式的數據結構可參考:

主要函數:

#include "top.h"
#include <string.h>

void hls::hls_skin_dection(RGB_IMAGE& src, RGB_IMAGE& dst,int rows, int cols,
        int y_lower,int y_upper,int cb_lower,int cb_upper,int cr_lower,int cr_upper)
{

    LOOp_ROWS:for(int row = 0; row < rows ; row++)
    {
    //#pragma HLS loop_flatten off
        LOOp_COLS:for(int col = 0; col < cols; col++)
        {
        #pragma HLS pipeline II=1 off               //--------------------------優化指令1
            //變量定義
            RGB_PIXEL src_data;
            RGB_PIXEL pix;
            RGB_PIXEL dst_data;
            bool skin_region;

            if(row < rows && col < cols) {
                src >> src_data;
            }

            //獲取RGB圖像通道數據
            uchar B = src_data.val[0];
               uchar G = src_data.val[1];
               uchar R = src_data.val[2];

               //RGB-->YCbCr顏色空間轉換
            uchar y  = (76 * R + 150 * G + 29 * B) >> 8;
            uchar cb = ((128*B -43*R - 85*G)>>8) + 128;
            uchar cr = ((128*R -107*G - 21 * B)>>8)+ 128;

            //膚色區域斷定
            if (y > y_lower && y < y_upper && cb > cb_lower && cb < cb_upper && cr > cr_lower && cr < cr_upper)
                skin_region = 1;
            else
                skin_region = 0;
            //將膚色區域的值大小置爲255,即白色
            uchar temp0= (skin_region == 1)? (uchar)120: B;
            uchar temp1= (skin_region == 1)? (uchar)120: G;
            uchar temp2= (skin_region == 1)? (uchar)120: R;

            dst_data.val[0] = temp0;
            dst_data.val[1] = temp1;
            dst_data.val[2] = temp2;
            //複製處理完成後輸出圖像
            dst << dst_data;
        }
    }
}
//該函數的做用主要是把上面的skin-detect函數的輸入輸出端口作一個封裝,以便處理視頻輸入信號
void ImgProcess_Top(AXI_STREAM& input, AXI_STREAM& output,int rows, int cols,
                    int y_lower,int y_upper,int cb_lower,int cb_upper,int cr_lower,int cr_upper)
    //定義該函數的功能用於將上面的膚色檢測用AXI_STREAM結構封裝端口
{
    ///*
    #pragma HLS RESOURCE variable=input core=AXIS metadata="-bus_bundle INPUT_STREAM"
    #pragma HLS RESOURCE variable=output core=AXIS metadata="-bus_bundle OUTPUT_STREAM"

    #pragma HLS RESOURCE core=AXI_SLAVE variable=rows metadata="-bus_bundle CONTROL_BUS"
    #pragma HLS RESOURCE core=AXI_SLAVE variable=cols metadata="-bus_bundle CONTROL_BUS"
    #pragma HLS RESOURCE core=AXI_SLAVE variable=y_lower metadata="-bus_bundle CONTROL_BUS"
    #pragma HLS RESOURCE core=AXI_SLAVE variable=y_upper metadata="-bus_bundle CONTROL_BUS"
    #pragma HLS RESOURCE core=AXI_SLAVE variable=cb_lower metadata="-bus_bundle CONTROL_BUS"
    #pragma HLS RESOURCE core=AXI_SLAVE variable=cb_upper metadata="-bus_bundle CONTROL_BUS"
    #pragma HLS RESOURCE core=AXI_SLAVE variable=cr_lower metadata="-bus_bundle CONTROL_BUS"
    #pragma HLS RESOURCE core=AXI_SLAVE variable=cr_upper metadata="-bus_bundle CONTROL_BUS"
    #pragma HLS RESOURCE core=AXI_SLAVE variable=return metadata="-bus_bundle CONTROL_BUS"

    #pragma HLS INTERFACE ap_stable port=rows
    #pragma HLS INTERFACE ap_stable port=cols
    #pragma HLS INTERFACE ap_stable port=y_lower
    #pragma HLS INTERFACE ap_stable port=y_upper
    #pragma HLS INTERFACE ap_stable port=cb_lower
    #pragma HLS INTERFACE ap_stable port=cb_upper
    #pragma HLS INTERFACE ap_stable port=cr_lower
    #pragma HLS INTERFACE ap_stable port=cr_upper
    ///*/
    RGB_IMAGE img_0(rows, cols);
    RGB_IMAGE img_1(rows, cols);
    
    #pragma HLS dataflow
    hls::AXIvideo2Mat(input,img_0);//將video圖像數據結構轉換爲mat型(RGB圖像)數據,以便於輸入到下面的檢測函數中。
    hls::hls_skin_dection(img_0,img_1,rows,cols,y_lower,y_upper,cb_lower,cb_upper,cr_lower,cr_upper);
    hls::Mat2AXIvideo(img_1, output);//將mat型(RGB圖像)數據轉換爲video圖像數據結構輸出
}

咱們對視頻接口的約束以下:將src和dst指定爲爲以 「INPUT_STREAM」 命名的AXI4 Stream,將控制接口分配到AXI4 Lite接口,指定「rows」可經過AXI4-Lite接口進行訪問而且聲明在函數執行過程當中 「rows」不會改變,其實這幾句優化語句中最關鍵的一條指令爲啓用數據流優化:#pragma HLS dataflow,它使得任務之間以流水線的方式執行,即hls::AXIvideo2Mat(src,img_0);hls::skin_detect(img_0,img_1,rows,cols ,cb_lower,cb_upper,cr_lower,cr_upper);hls::Mat2AXIvideo(img_1, dst),這三個函數之間爲流水線方式。

 

測試文件:思路也是導入圖像文件,而後建立好保存結果的空間,而後定義視頻流變量,將圖像輸入轉換爲axi-stream格式,而後用本身編寫的函數處理後,輸出轉換爲IplImage格式後,輸出圖像觀察便可。

#include "top.h"
#include "hls_opencv.h"
#include "iostream"
#include <time.h>

using namespace std;
using namespace cv;

int main (int argc, char** argv)
{
    //IplImage* src = cvLoadImage(INPUT_IMAGE);
    IplImage* src = cvLoadImage("test.jpg");
    //IplImage* src = cvLoadImage("test_img1.jpg");
    IplImage* dst = cvCreateImage(cvGetSize(src), src->depth, src->nChannels);

    AXI_STREAM  src_axi, dst_axi;
    IplImage2AXIvideo(src, src_axi);

    ImgProcess_Top(src_axi, dst_axi, src->height, src->width,0,255,75,125,131,185);
    AXIvideo2IplImage(dst_axi, dst);

    cvShowImage("src",src);
    cvShowImage("dst_hls",dst);
    waitKey(0);//參數<=0時等待按鍵事件發生,按下鍵的話返回按鍵的值, 不然返回-1;

    return 0;
}

 下一篇介紹:

3.FFT的HLS實現

3.HOG實現部分https://www.cnblogs.com/zhazhiqiang/p/3595266.html

相關文章
相關標籤/搜索