vitis HLS中实现canny算法的IP核

一、前言

        canny边缘检测主要用于提取图像的边缘,是最常用且有效的边缘检测算法。在AMD赛灵思提供的库函数中,使用xf::cv::Canny和xf::cv::EdgeTracing两个函数实现canny边缘提取。本文举例说明如何在vitis HLS 2023.1中实现canny算法。

二、xf::cv::Canny和xf::cv::EdgeTracing函数解析

        首先看一下这两个函数的调用接口:

1、xf::cv::Canny函数

template<int FILTER_TYPE, //sobel滤波宽度,仅支持3和5int NORM_TYPE,   //范数类型,支持L1范数和L2范数int SRC_T,       //输入图像类型,仅支持8UC1int DST_T,       //输出图像类型,仅支持2UC1int ROWS,       //最大图像行数int COLS,      //最大图像列数int NPC,      //输入图像的单个时钟处理像素数int NPC1,      //输出图像的单个时钟处理像素数bool USE_URAM=false, //是否使用URAMint XFCVDEPTH_IN_1 = _XFCVDEPTH_DEFAULT, //输入图像深度int XFCVDEPTH_OUT_1 = _XFCVDEPTH_DEFAULT> //输出图像深度
void Canny(xf::cv::Mat<SRC_T, ROWS, COLS, NPC, XFCVDEPTH_IN_1> &_src_mat, //输入图像矩阵xf::cv::Mat<DST_T, ROWS, COLS, NPC1, XFCVDEPTH_OUT_1> & _dst_mat, //输出图像矩阵unsigned char _lowthreshold,   //边缘提取低阈值unsigned char _highthreshold)  //边缘提取高阈值

        在xf::cv::Canny算法中,首先通过3*3的高斯噪声滤波器对图像进行滤波;此后使用Sobel梯度函数计算沿着x和y方向的梯度,用以计算像素的幅度和相位;然后使用最大值抑制算法,得到对应的边缘点进行输出,输出结果的单个像素的位宽为2bits,,然后经过打包输出。

        xf::cv::Canny函数输出的图像像素2bits,含义表示如下:

                00-表示背景

                01-表示弱边缘

                11-表示强边缘

2、xf::cv::EdgeTracing函数

template<int SRC_T,  //输入图像类型int DST_T,  //输出图像类型int ROWS,   //图像最大行数int COLS,   //图像最大列数int NPC_SRC,//输入图像的NPPC,每个时钟处理像素数int NPC_DST,//输出图像的NPPC,每个时钟处理像素数bool USE_URAM=false, //是否使用URAMint depthm = -1> //图像深度
void EdgeTracing(xf::cv::Mat<SRC_T, ROWS, COLS, NPC_SRC, depthm> & _src,//输入图像矩阵xf::cv::Mat<DST_T, ROWS, COLS, NPC_DST, depthm> & _dst) //输出图像矩阵

        xf::cv::EdgeTracing函数主要用于处理canny算法,将离散的强边缘和弱边缘进行边缘跟踪,将离散的边缘点串联起来,最终将2UC1的图像输出为一个8UC1的图像。

        对于此函数需要特别注意,其无法实现数据的DATAFLOW,只能采取内存映射读取的方式进行读写访问。并且在综合的时候,需要在cflag总添加编译指令"-D__SDA_MEM_MAP__",否则综合时会报错。具体可以参考后面的示例。

        关于其余详细信息,可以参考:xilinx.github.io/Vitis_Libraries/vision/2022.1/api-reference.html#canny-edge-detection

三、vitis HLS canny算法中的具体代码实现

        这部分的代码实现不难,在赛灵思提供的示例程序中就有现成的参考示例,不过是在L2文件夹下,主要是vitis下的实现demo。不过稍微进行更改,就可以在vitisHLS中成功完成综合和联合仿真了。

        若想要查看赛灵思提供的参考示例,请访问Xilinx/Vitis_Libraries: Vitis Libraries (github.com)。

        下面主要描述在vitisHLS中是如何完成vitisHLS代码的。

1、首先提供一下头文件define.h代码

#include "ap_int.h"
#include "common/xf_common.hpp"
#include "common/xf_utility.hpp"
#include "hls_stream.h"
#include "imgproc/xf_canny.hpp"
#include "imgproc/xf_edge_tracing.hpp"#define FILTER_WIDTH 3#define NORM_TYPE XF_L1NORM#define XF_USE_URAM false#define IMAGE_PTR_WIDTH 64#define WIDTH 512
#define HEIGHT 512#define THRES_LOW 120 //边缘提取低阈值
#define THRES_HIGH 180 边缘提取高阈值void canny_accel(ap_uint<IMAGE_PTR_WIDTH>* img_inp,ap_uint<IMAGE_PTR_WIDTH>* img_out,int rows,int cols,int low_threshold,int high_threshold) ;void edgetracing_accel(ap_uint<IMAGE_PTR_WIDTH>* img_inp,ap_uint<IMAGE_PTR_WIDTH>* img_out,int rows,int cols);

2、xf::cv::Canny的代码示例

#include "define.h"static constexpr int _XF_DEPTH_I = (HEIGHT * WIDTH * (XF_PIXELWIDTH(XF_8UC1, XF_NPPC8))) / (IMAGE_PTR_WIDTH);
static constexpr int _XF_DEPTH_O = (HEIGHT * WIDTH * (XF_PIXELWIDTH(XF_2UC1, XF_NPPC32))) / (IMAGE_PTR_WIDTH);void canny_accel(ap_uint<IMAGE_PTR_WIDTH>* img_inp,ap_uint<IMAGE_PTR_WIDTH>* img_out,int rows,int cols,int low_threshold,int high_threshold) {
// clang-format off#pragma HLS INTERFACE m_axi     port=img_inp  depth=_XF_DEPTH_I bundle=gmem1#pragma HLS INTERFACE m_axi     port=img_out  depth=_XF_DEPTH_O bundle=gmem2// clang-format on// clang-format off#pragma HLS INTERFACE s_axilite port=rows     #pragma HLS INTERFACE s_axilite port=cols     #pragma HLS INTERFACE s_axilite port=low_threshold     #pragma HLS INTERFACE s_axilite port=high_threshold     #pragma HLS INTERFACE s_axilite port=return// clang-format onint npcCols = cols;int divNum = (int)(cols / 32);int npcColsNxt = (divNum + 1) * 32;if (cols % 32 != 0) {npcCols = npcColsNxt;}//printf("actual number of cols is %d \n", npcCols);xf::cv::Mat<XF_8UC1, HEIGHT, WIDTH, XF_NPPC8> in_mat(rows, cols);xf::cv::Mat<XF_2UC1, HEIGHT, WIDTH, XF_NPPC32> dst_mat(rows, npcCols);#pragma HLS DATAFLOWxf::cv::Array2xfMat<IMAGE_PTR_WIDTH, XF_8UC1, HEIGHT, WIDTH, XF_NPPC8>(img_inp, in_mat);xf::cv::Canny<FILTER_WIDTH, NORM_TYPE, XF_8UC1, XF_2UC1, HEIGHT, WIDTH, XF_NPPC8, XF_NPPC32, XF_USE_URAM>(in_mat, dst_mat, low_threshold, high_threshold);xf::cv::xfMat2Array<IMAGE_PTR_WIDTH, XF_2UC1, HEIGHT, WIDTH, XF_NPPC32>(dst_mat, img_out);
}

        此部分代码注意几点如下:

        1、#pragma HLS DATAFLOW 实现数据的流水线处理。

        2、输出图像为XF_2UC1格式,无法直接在opencv中显示。

        3、图像列数需要为32的整数倍

        4、CFLAG正常设置即可,我这边设置的是 "-D__SDSVHLS__ -IE:/vitis_hls_image/vitis_hls_tutorial/include -std=c++0x -O3"

3、xf::cv::EdgeTracing的代码示例

#include "define.h"static constexpr int _XF_DEPTH_I = (HEIGHT * WIDTH * (XF_PIXELWIDTH(XF_2UC1, XF_NPPC32))) / (IMAGE_PTR_WIDTH);
static constexpr int _XF_DEPTH_O = (HEIGHT * WIDTH * (XF_PIXELWIDTH(XF_8UC1, XF_NPPC8))) / (IMAGE_PTR_WIDTH);void edgetracing_accel(ap_uint<IMAGE_PTR_WIDTH>* img_inp,ap_uint<IMAGE_PTR_WIDTH>* img_out,int rows,int cols) {
// clang-format off#pragma HLS INTERFACE m_axi     port=img_inp  depth=_XF_DEPTH_I bundle=gmem3#pragma HLS INTERFACE m_axi     port=img_out  depth=_XF_DEPTH_O bundle=gmem4
// clang-format on// clang-format off#pragma HLS INTERFACE s_axilite port=rows     #pragma HLS INTERFACE s_axilite port=cols     #pragma HLS INTERFACE s_axilite port=return// clang-format onint npcCols = cols;int divNum = (int)(cols / 32);int npcColsNxt = (divNum + 1) * 32;if (cols % 32 != 0) {npcCols = npcColsNxt;}int npcCols_8 = cols;int divNum_8 = (int)(cols / 8);int npcColsNxt_8 = (divNum_8 + 1) * 8;if (cols % 8 != 0) {npcCols_8 = npcColsNxt_8;}// printf("actual number of cols is %d \n", npcCols);// printf("actual number of cols is multiple 8 :%d \n", npcCols_8);// printf("\nbefore allocate\n");xf::cv::Mat<XF_2UC1, HEIGHT, WIDTH, XF_NPPC32> _dst1(rows, npcCols, img_inp);xf::cv::Mat<XF_8UC1, HEIGHT, WIDTH, XF_NPPC8> _dst2(rows, npcCols_8, img_out);// printf("\nbefore kernel call\n");xf::cv::EdgeTracing<XF_2UC1, XF_8UC1, HEIGHT, WIDTH, XF_NPPC32, XF_NPPC8, XF_USE_URAM>(_dst1, _dst2);// printf("\nafter kernel call\n");
}

        此部分代码注意几点如下:

        1、不能添加指令:#pragma HLS DATAFLOW 。否则综合会报错

        2、图像列数需要为32的整数倍

        3、CFLAG设置需要额外注意添加__SDA_MEM_MAP指令,否则综合报错。我这边设置的是 "-D__SDSVHLS__ -D__SDA_MEM_MAP__ -IE:/vitis_hls_image/vitis_hls_tutorial/include -std=c++0x -O3"

4、综合注意事项

        我刚开始编译的时候,总以为可以将xf::cv::Canny和xf::cv::EdgeTracing两个函数综合到1个IP核里,但是最终我失败了。这两个函数,一个是DATAFLOW形式,一个是__SDA_MEM_MAP__的编译方式,是无法在一个IP核中编译成功的,需要将2个函数分别编译成一个IP,在vivado中按照下图的方式相连。

5、testbench的代码示例


#include <iostream>
#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <opencv/cv.h>
#include <opencv2/highgui.hpp>
#include <opencv/cxcore.h>
#include <opencv2/imgproc.hpp>#include "define.h"
#include "common/xf_sw_utils.hpp"int main(int argc, char* argv[])
{//------------1、读取图像转化为灰度图像---------------------------printf("argc == %d \n",argc);if (argc != 2) {printf("input error: PLEASE INPUT IMAGE PATH 1\n");return 1;}//opencv canny边缘处理cv::Mat img_in; //输入图像img_in = cv::imread(argv[1],cv::IMREAD_GRAYSCALE);//按照GRAY图读取图像cv::imwrite("opencv读取的图像.png",img_in);//显示//-----直接进行canny处理cv::Mat image_canny_only(img_in.rows, img_in.cols, img_in.type());cv::Canny(img_in,image_canny_only,THRES_LOW,THRES_HIGH);cv::imwrite("openCV处理后图像-无高斯滤波.png",image_canny_only);////----预先进行高斯滤波处理cv::Mat image_gaus(img_in.rows, img_in.cols, img_in.type());cv::Mat image_canny(img_in.rows, img_in.cols, img_in.type());//实际上HLS处理中,首先进行了高斯滤波,因此在opencv中也加入高斯滤波cv::GaussianBlur( img_in, image_gaus, cv::Size(3,3),0, 0,cv::BORDER_CONSTANT);cv::Canny(image_gaus,image_canny,THRES_LOW,THRES_HIGH);cv::imwrite("openCV处理后图像-高斯滤波.png",image_canny);////2、HLS canny边缘处理---------------------------xf::cv::Mat<XF_8UC1,HEIGHT,WIDTH,XF_NPPC8> image_in; //输入图像image_in = xf::cv::imread<XF_8UC1,HEIGHT,WIDTH,XF_NPPC8>(argv[1],cv::IMREAD_GRAYSCALE);//按照GRAY图读取图像xf::cv::imwrite<XF_8UC1,HEIGHT,WIDTH,XF_NPPC8>("HLS读取的图像.png",image_in);//显示xf::cv::Mat<XF_2UC1,HEIGHT,WIDTH,XF_NPPC32>hls_image_canny;canny_accel(      (ap_uint<IMAGE_PTR_WIDTH>*) image_in.data,(ap_uint<IMAGE_PTR_WIDTH>*) hls_image_canny.data,512,512,THRES_LOW,THRES_HIGH);xf::cv::Mat<XF_8UC1,HEIGHT,WIDTH,XF_NPPC8> hls_image_edge;edgetracing_accel( (ap_uint<IMAGE_PTR_WIDTH>*) hls_image_canny.data,(ap_uint<IMAGE_PTR_WIDTH>*) hls_image_edge.data,HEIGHT,WIDTH);xf::cv::imwrite<XF_8UC1,HEIGHT,WIDTH,XF_NPPC8>("HLS处理后图像.png",hls_image_edge);//cv::waitKey(0);/// 等待用户按任意按键退出程序return 0;
}

        对输入测试激励tina.png,原图、opencv处理结果(无高斯滤波),opencv处理(有高斯滤波),HLS处理结果分别如下:

原图 
opencv处理结果(无高斯滤波)

opencv处理(有高斯滤波)

 HLS处理

        分析上面的结果,可以看到HLS处理的canny图像边缘,由于包含了高斯滤波,所以与opencv处理(有高斯滤波)的处理结果最接近,且结果基本正确。

        实测,该函数综合、联合仿真结果均正确。

四、完整的vitisHLS示例工程

        有兴趣的可以参考完整的示例代码:vitis-HLScanny算法实现图像边缘检测资源-CSDN文库

        (我偷懒了点,将xf::cv::Canny和xf::cv::EdgeTracing放在一个工程的两个函数中。在实际使用时,需要分别将xf::cv::Canny和xf::cv::EdgeTracing对应的函数设置为顶层函数,分别进行综合、导出RTL文件,这样就可以得到2个IP了,把这两个IP都添加到vivado中综合编译即可。

               

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/297639.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】布隆过滤器原理详解及其代码实现

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能AI、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐--…

luceda ipkiss教程 53:在版图上加中文

要在版图上加中文&#xff0c;如&#xff1a; 可以通过如下方法实现&#xff1a; 首先&#xff0c;可以在ppt中加入文本框&#xff0c;在文本框中输入想要加到版图上的中文内容&#xff0c;如&#xff0c;复旦大学&#xff0c;并将文本框存为windows位图。 其次&#xff0c;通…

基于Java (spring-boot)的仓库管理系统

一、项目介绍 本系统的使用者一共有系统管理员、仓库管理员和普通用户这3种角色: 1.系统管理员&#xff1a;通过登录系统后&#xff0c;可以进行管理员和用户信息的管理、仓库和物品分类的管理&#xff0c;以及操作日志的查询&#xff0c;具有全面的系统管理权限。 2.仓库管理…

现代控制理论-李雅普诺夫

现代控制理论-李雅普诺夫 单输入单输出系统&#xff08;BIBO&#xff09;的系统函数如下&#xff1a; 则&#xff0c;该系统的能控标准型&#xff08;能空性&#xff09;为&#xff1a; 能观性&#xff1a; 李雅普诺夫下的稳定性&#xff1a; 李雅普诺夫下的渐进稳定性&a…

Docker部署 flowable-ui 进行流程建模

Docker部署 flowable-ui 进行流程建模 简介 安装Docker Desktop,本篇无安装步骤安装正常打开运行后&#xff0c;正式开始部署flowable-uicmd执行拉取镜像操作docker pull flowable/flowable-uicmd启动镜像docker run -d --name flowable -p 8081:8080 flowable/flowable-ui修…

【C++11/17】std::map高效插入

我们在使用stl的映射容器std::map时&#xff0c;经常需要向容器中插入数据。由于map的元素key值是唯一的&#xff0c;我们经常遇到这样的场景&#xff1a; 向map中插入元素时&#xff0c;指定的key已经存在则直接更新&#xff1b;指定的key不存在&#xff0c;然后才做插入操作…

【图的应用四:关键路径】- 用 C 语言实现关键路径

目录 一、AOE-网 二、算法的实现 2.1 - ALGraph.h 2.2 - ALGraph.c 2.3 - Test.c 一、AOE-网 与 AOV-网相对应的是 AOE-网&#xff08;Activity On Edge&#xff09;&#xff0c;即以边表示活动的网。AOE-网是一个带权的有向无环图&#xff0c;其中&#xff0c;顶点表示事…

git 使用方法自用(勿进)本地开发分支推上线上开发分支

一、//查看状态 1.git status 二、//查看改了哪个文件夹 1.git diff 2.//会出现改了哪个文件夹src/components/partials/Slider.js 三、//查看改了的文件夹里面具体改了啥内容 1.git diff src/components/partials/Slider.js 四、提交所有 1. git add . 五、写备注…

攻防世界——game 游戏

下载下来是一个exe文件&#xff0c;可以用IDA打开 我们先运行一下 这是属于第二种类型&#xff0c;完成一个操作后给你flag 这种题我更倾向于动调直接得到flag 我们查壳 没有保护壳&#xff0c;直接32打开 进入字符串界面&#xff0c;找到显示的那部分 int __cdecl main_0(…

ardupilot开发 --- 风机不停机巡检 篇

在哪里创建的siyi实例&#xff1f; 如何传递飞控的时间戳给siyi相机&#xff1f; AP_RTC_ENABLED在waf编译时配置为1&#xff1f;&#xff1f; 如何配置&#xff1f; 在lua脚本中如何获取这个时间AP::rtc().get_utc_usec(utc_usec)&#xff1f;&#xff1f;&#xff1f; inclu…

mac传输文件到windows

前言 由于mac系统与windows系统文件格式不同&#xff0c;通过U盘进行文件拷贝时&#xff0c;导致无法拷贝。官方解决方案如下&#xff0c;但是描述的比较模糊。看我的操作步骤即可。 https://support.apple.com/zh-cn/guide/mac-help/mchlp1657/12.0/mac/12.6 前提条件 mac与…

C语言沉浸式刷题【C语言必刷题】

1.猜凶手 某地发生了一起谋杀案&#xff0c;警察通过排查确定杀人凶手必为四个嫌疑犯的一个&#xff0c;以下是4个嫌犯的供词。已知&#xff08;请编写代码找出凶手&#xff09; A说&#xff1a;不是我。 B说&#xff1a;是C。C说&#xff1a;是D。D说&#xff1a;C再胡说。 程…