1. 程式人生 > >HLS圖像處理總結(一)

HLS圖像處理總結(一)

特征提取 分析 參數 gets eight xilinx 意思 導入 vhdl

HLS工具

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

主要從三個工程分析

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

主要函數的具體定義:

#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。

測試函數:

#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

HLS圖像處理總結(一)