写在前面
本人是参考赛灵思官方文档、正点原子教程和网上相关内容,所有参考、转载会标明出处,希望文章对大家有所帮助,感谢各位!
目的
使用 Vivado HLS 实现一个图像处理的 IP 核,该 IP 核能利用 xfopencv 将 OV5640 摄像头产生的 RGB 彩色图像转换成灰度图像,在 Vivado 中对 IP 核进行验证。因为上海疫情,购买不到LCD屏,最后通过 HDMI 实时显示。
工具
1.Vivado HLS 2019.1
2.xfopencv-2019.1_release.zip
一、创建项目
打开 Vivado HLS 2019.1,创建项目 ov5640_rgb2gray。创建好之后,在项目文件夹的根目录下添加一个 src 文件夹。
二、步骤
1.下载xfopencv库
在 GitHub 上下载 https://github.com/Xilinx/xfopencv
2.引入xfopencv库
下载之后,解压在 Xilinx 文件下。
3.参考示例代码
如下图所示,打开文件,里面是示例的模板和代码,虽然文件较多,但这是官方固定的代码架构,代码内容不难,可以顺便学习别人的编程风格。
重点:一定要按照这个示例来,否则进行 Synthesis 的时候可能会报错,目前还不清楚原因。
报错的报告如下图所示
4.代码
下图是本人依照示例编写的该项目代码架构
4.1 xf_headers.h
本人查询了相关的文章,简单的对一些代码进行解释,不一定正确,仅供参考。
#ifndef _XF_HEADERS_H_
#define _XF_HEADERS_H_
/*与C环境相关的头文件*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
/*与HLS相关的头文件,需要包含HLS的include的path*/
#include "ap_int.h"
#include "hls_stream.h"
/*针对SDSCC*/
#if __SDSCC__
#undef __ARM_NEON__
#undef __ARM_NEON
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#define __ARM_NEON__
#define __ARM_NEON
#else
//在CSIM中的tb中包含的H文件
#include "opencv2/opencv.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#endif
#if __SDSCC__
#include "sds_lib.h"
#define TIME_STAMP_INIT unsigned int clock_start, clock_end; clock_start = sds_clock_counter();
#define TIME_STAMP { clock_end = sds_clock_counter(); printf("elapsed time %lu \n", clock_end-clock_start); clock_start = sds_clock_counter(); }
#endif
/*在CSIM中的tb中包含的H文件*/
#include "common/xf_sw_utils.h"
#include "common/xf_axi.h"
#endif//_XF_HEADERS_H_
4.2 xf_config_params.h
xf_config_params.h 头文件中的内容似乎可以和 xf_rgb2gray_config.h 合并,但是本人先留下来了。
/* Optimization type */
#define RO 0 // Resource Optimized (8-pixel implementation)
#define NO 1 // Normal Operation (1-pixel implementation)
4.3 xf_rgb2gray_config.h
配置相关参数等的头文件,重要!
#ifndef _XF_RGB2GRAY_CONFIG_H_
#define _XF_RGB2GRAY_CONFIG_H_
/*与HLS相关的头文件,需要包含HLS的include的path*/
#include "hls_stream.h"
#include "ap_int.h"
/*与xfopencv相关的头文件*/
#include "common/xf_common.h"
#include "common/xf_utility.h"
#include "common/xf_infra.h"
/*与图像处理相关的头文件*/
#include "imgproc/xf_cvt_color.hpp"
#include "imgproc/xf_cvt_color_1.hpp"
//#include "imgproc/xf_rgb2hsv.hpp"
//#include "imgproc/xf_bgr2hsv.hpp"
// Has to be set when synthesizing
#include "xf_config_params.h"
/* config width and height */
#define WIDTH 1024
#define HEIGHT 800
#if NO
#define NPC1 XF_NPPC1
#else
#define NPC1 XF_NPPC8
#endif
/*声明主函数*/
void ov5640_rgb2gray(hls::stream< ap_axiu<24,1,1,1> >& _src,hls::stream< ap_axiu<24,1,1,1> >& _dst,int height,int width);
#endif // _XF_RGB2GRAY_CONFIG_H_
4.4 xf_rgb2gray_accel.cpp
cvtcolor_rgb2gray 函数,核心的内置函数,重要!
#include "xf_rgb2gray_config.h"
void cvtcolor_rgb2gray(xf::Mat<XF_8UC3, HEIGHT, WIDTH, NPC1> &imgInput,xf::Mat<XF_8UC1, HEIGHT, WIDTH, NPC1> &imgOutput )
{
xf::rgb2gray<XF_8UC3,XF_8UC1, HEIGHT, WIDTH, NPC1>(imgInput, imgOutput);
}
void cvtcolor_gray2rgb(xf::Mat<XF_8UC1, HEIGHT, WIDTH, NPC1> &imgInput,xf::Mat<XF_8UC3, HEIGHT, WIDTH, NPC1> &imgOutput )
{
xf::gray2rgb<XF_8UC1,XF_8UC3, HEIGHT, WIDTH, NPC1>(imgInput, imgOutput);
}
4.5 ov5640_rgb2gray.cpp
主函数 Top Function,定义变量,调用函数等等。
#include "xf_rgb2gray_config.h"
typedef hls::stream< ap_axiu<24,1,1,1> > AXI_STREAM_24;
void cvtcolor_rgb2gray(xf::Mat<XF_8UC3, HEIGHT, WIDTH, NPC1> &imgInput,xf::Mat<XF_8UC1, HEIGHT, WIDTH, NPC1> &imgOutput);
void cvtcolor_gray2rgb(xf::Mat<XF_8UC1, HEIGHT, WIDTH, NPC1> &imgInput,xf::Mat<XF_8UC3, HEIGHT, WIDTH, NPC1> &imgOutput);
void ov5640_rgb2gray(AXI_STREAM_24& _src,AXI_STREAM_24& _dst,int height,int width)
{
#pragma HLS INTERFACE axis register both port=_src
#pragma HLS INTERFACE axis register both port=_dst
xf::Mat<XF_8UC3, HEIGHT, WIDTH, NPC1> imgInput1(height,width);
xf::Mat<XF_8UC1, HEIGHT, WIDTH, NPC1> imgGray(height,width);
xf::Mat<XF_8UC3, HEIGHT, WIDTH, NPC1> imgOutput1(height,width);
#pragma HLS stream variable=imgInput1.data dim=1 depth=1
#pragma HLS stream variable=imgGray.data dim=1 depth=1
#pragma HLS stream variable=imgOutput1.data dim=1 depth=1
#pragma HLS dataflow
xf::AXIvideo2xfMat(_src, imgInput1);
cvtcolor_rgb2gray(imgInput1,imgGray);
cvtcolor_gray2rgb(imgGray,imgOutput1);
xf::xfMat2AXIvideo(imgOutput1, _dst);
}
4.6
在进行综合之前,强烈建议调试和测试。
#include "xf_headers.h"
#include "xf_rgb2gray_config.h"
int main(int argc, char** argv){
uint16_t img_width;
uint16_t img_height;
cv::Mat inputimg1, inputimg;
cv::Mat error_img0;
cv::Mat outputimg,ocv_outputimg;
inputimg1 = cv::imread(argv[1], 1);
if(!inputimg1.data)
{
return -1;
}
cv::cvtColor(inputimg1,inputimg,CV_BGR2RGB);
outputimg.create(inputimg.rows, inputimg.cols, CV_8UC1);
static xf::Mat<XF_8UC3, HEIGHT, WIDTH, NPC1> imgInput(inputimg.rows,inputimg.cols);
static xf::Mat<XF_8UC1, HEIGHT, WIDTH, NPC1> imgOutput(outputimg.rows,outputimg.cols);
imgInput.copyTo((unsigned short int*)inputimg.data);
#if __SDSCC__
hw_ctr.start();
#endif
cvtcolor_rgb2gray(imgInput,imgOutput);
#if __SDSCC__
hw_ctr.stop();
uint64_t hw_cycles = hw_ctr.avg_cpu_cycles();
#endif
outputimg.data = imgOutput.copyFrom();
//OpenCV reference
cv::cvtColor(inputimg,ocv_outputimg,CV_RGB2GRAY,1);
cv::imwrite("ocv_out.jpg",ocv_outputimg);
cv::imwrite("hls_out.jpg",outputimg);
absdiff(outputimg,ocv_outputimg,error_img0);
/* ## *************************************************************** ##*/
return 0;
}
5.设置
在Xilinx官方文档ug1233中介绍了相关配置。另外,进行测试的时候,可以在 testbench 中引入库中 example 下 data 文件夹中图片。
Synthesis 设置
Simulation设置
6.结果
1.C-Sim 结果
在该项目文件目录下可以查看结果
2.Synthesis 成功后,会弹出report ,含有接口等信息。
问题
小问题
-
Include 后面的引号问题引起重视,中英文下的引号需要注意。
-
头文件中要声明使用到的函数。
-
配置的时候粗心,本来2个下划线,写成3个,导致了如下图的error。
Synthesis 过程中产生的问题
-
cvtcolor_bgr2gray sythesis issue
参考了GitHub上的文章解决了。
参考该链接解决下图所示error https://github.com/Xilinx/xfopencv/issues/82
-
待解决的警告:
希望有解决方案的大佬评论留言,感激不尽。
D:/Xilinx/xfopencv/include\imgproc/xf_cvt_color.hpp:2171:57: warning: default template arguments for a function template are a C++11 extension [-Wc++11-extensions]
template <int SRC_T, int DST_T, int ROWS, int COLS, int NPC=1>
^ ~
总结
个人理解:之所以用 FPGA 进行图像处理,第一是因为FPGA并行计算和硬件加速的特点可以获得更快的响应。第二是因为有利于减少产品研发时间,快速形成产品,比如车牌识别,目标跟踪等。
Xilinx OpenCV 是一个为 FPGA 高级合成(HLS)优化的模板库,允许以与使用 OpenCV 库相同的方式轻松创建图像处理渠道。
推荐一篇文章,介绍 Xilinx OpenCV ,并且给每段代码进行了解释,值得收藏一看。
Introduction to Xilinx OpenCV
自己不怎么写文章,也有很多不会的地方(如3 和4.2),可能写的不好,欢迎批评、指正和交流。
第二部分 已经更新
https://blog.csdn.net/m0_49474265/article/details/124511504
后续还会更新…