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.mzph.cn/news/584251.shtml

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

相关文章

JUC常用并发工具类

JUC常用并发工具类 1、什么是JUC? JUC 就是 java.util.concurrent 包&#xff0c;这个包俗称 JUC&#xff0c;里面都是解决并发问题的一些东西&#xff0c;该包的位置位于 java 下 面的 rt.jar 包下面。 2、4大常用并发工具类 2.1 CountDownLatch CountDownLatch&#x…

基于Java车间工时管理系统(源码+部署文档)

博主介绍&#xff1a; ✌至今服务客户已经1000、专注于Java技术领域、项目定制、技术答疑、开发工具、毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅 &#x1f447;&#x1f3fb; 不然下次找不到 Java项目精品实…

《新传奇》期刊投稿论文发表

《新传奇》杂志是经国家新闻出版总署批准、面向国内外公开发行的综合性社科期刊&#xff0c;由湖北省文联主管&#xff0c;湖北今古传奇传媒集团有限公司主办&#xff0c;湖北优秀期刊。本刊旨在坚守初心、引领创新&#xff0c;展示高水平研究成果&#xff0c;支持优秀学术人才…

如何使用 NFTScan NFT API 在 Gnosis 网络上开发 Web3 应用

Gnosis Chain 是一个兼容 EVM 的区块链&#xff0c;专注于快速且低成本的交易功能&#xff0c;采用独特的双通证模型&#xff1b;xDai 是一种用于交易、支付和手续费的稳定币&#xff0c;权益证明&#xff08;PoS&#xff09;保护将由 GNO 通过共识层 Gnosis Beacon Chain 提供…

【JVM】虚拟机的组成+字节码文件组成+类的生命周期

什么是JVM&#xff1f; JVM 本质上是一个运行在计算机上的程序&#xff0c;他的职责是运行Java字节码文件。 JVM的功能 1.解释和运行&#xff1a;对字节码文件中的指令实时的解释成机器码让计算机执行。 2.内存管理&#xff1a;自动为对象、方法等分配内存&#xff0c;自动…

【C++干货铺】STL中set和map的介绍和使用

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 序列式容器 关联式容器 键值对 树形结构的关联式容器 set set的介绍 set的使用 set的模板参数列表 set的构造 ​编辑 set的容量 set的删除和查找 mult…

web等保评测需要实机查看的操作系统、服务器、数据库和应用部分

“等保测评”全称是信息安全等级保护测评。是经公安部认证的具有资质的测评机构&#xff0c;依据国家信息安全等级保护规范规定&#xff0c;受有关单位委托&#xff0c;按照有关管理规范和技术标准&#xff0c;对信息系统安全等级保护状况进行检测评估的活动。 本文陆续将遇到的…

2023“SEED”第四届江苏大数据--新能源赛道 复赛Btop2总结

第一名是真的强&#xff01;基本都是第一&#xff0c;难以撼动。 昨天新能源赛道终于落下了帷幕&#xff0c;真的不是一般的卷。最后的排名都到了0.0几分的差距。跟队友很辛运复赛B榜单目前进入top3的行列&#xff0c;下面简单总结一下赛事过程。 初赛按照天级别预测未来一周各…

Linux iptables防火墙(一)

1.1 Linux防火墙基础 在 Internet 中&#xff0c;企业通过架设各种应用系统来为用户提供各种网络服务&#xff0c;如 Web 网站、 电子邮件系统、 FTP 服务器、数据库系统等。那么&#xff0c;如何来保护这些服务器&#xff0c;过滤企业不 需要的访问甚至是恶意的入侵呢&a…

算法设计与分析实验报告-贪心算法

校课程的简单实验报告。 算法设计与分析实验报告-递归与分治策略 算法设计与分析实验报告-动态规划算法 算法设计与分析实验报告-贪心算法 dijkstra迪杰斯特拉算法&#xff08;邻接表法&#xff09; 算法设计与分析实验报告-回溯法 算法设计与分析实验报告-分支限界法 …

halcon字符识别结果为“\x1A”

最近在做OCR字符识别&#xff0c;遇到了点小问题&#xff0c;记录一下。 由于是项目初期&#xff0c;所以我就打算调halcon自带库去识别一下看看效果如何&#xff0c;结果分类器的结果显示为“\x1A”。如下图 百度搜了一圈没有这个解答&#xff0c;所以就在halcon帮助文档里…

AtCoder Beginner Contest 334 G

G.Christmas Color Grid 2&#xff08;枚举&#xff0c;Tarjan&#xff09; 题意&#xff1a; 本题与问题 E E E类似。有一个 H H H行和 W W W列的网格&#xff0c;每个单元格都被涂成红色或绿色。用 ( i , j ) (i,j) (i,j)表示从上到下第 i i i行、从左到右第 j j j列的单元…

【LeetCode】修炼之路-0001-Two Sum(两数之和)【python】【简单】

前言 计算机科学作为一门实践性极强的学科,代码能力的培养尤为重要。当前网络上有非常多优秀的前辈分享了LeetCode的最佳算法题解,这对于我们这些初学者来说提供了莫大的帮助,但对于我这种缺乏编程直觉的学习者而言,这往往难以消化吸收。&#xff08;为什么别人就能想出这么优雅…

《异常检测——从经典算法到深度学习》25 基于深度隔离林的异常检测算法

《异常检测——从经典算法到深度学习》 0 概论1 基于隔离森林的异常检测算法 2 基于LOF的异常检测算法3 基于One-Class SVM的异常检测算法4 基于高斯概率密度异常检测算法5 Opprentice——异常检测经典算法最终篇6 基于重构概率的 VAE 异常检测7 基于条件VAE异常检测8 Donut: …

一篇文章掌握 NestJS 所有的生命周期以及生命周期的执行时机

前言 NestJS 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的框架&#xff0c;它使用 TypeScript 作为开发语言&#xff0c;也支持原生的 JavaScript。在 NestJS 中&#xff0c;生命周期事件是一个重要的概念。在我们构建和管理应用程序时&#xff0c;有时需要在特定…

Prometheus快速入门实战

Prometheus快速入门实战 1. 介绍 prometheus受启发于Google的Brogmon监控系统&#xff08;相似kubernetes是从Brog系统演变而来&#xff09;。 2016年5月继kubernetes之后成为第二个加入CNCF基金会的项目&#xff0c;同年6月正式发布1.0版本。2017年底发布基于全新存储层的2.…

【数据结构】C语言实现双链表的基本操作

双链表及其基本操作的实现 导言一、单链表与双链表二、双链表类型的创建三、双链表的初始化四、双链表的创建五、双链表的遍历六、双链表的查找七、双链表的插入八、双链表的删除结语 导言 大家好&#xff0c;很高兴又和大家见面啦&#xff01;&#xff01;&#xff01; 经过…

反射讲解(有图有真相)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、反射是什么&#xff1f;二、反射有啥好处&#xff1f;1. 没反射2. 有反射 三、反射的常用方法1. 获取 Class 对象&#xff1a;2. 获取类的构造方法&#xf…

数组的声明

概要&#xff1a; 数组的声明分为三个部分 第一部分&#xff1a;数组中元素的数据类型 第二部分&#xff1a;数组名 第三部分&#xff1a;数组标识符(方括号)和数组大小 一、测试代码 #include<stdio.h> int main() {int arr_int[10];char* arr_str[10];arr_in…

软件测试/测试开发丨Selenium环境安装配置

一、selenium 环境配置 1、下载浏览器 目前比较常用的浏览器是 Google Chrome 浏览器&#xff0c;所以本教程以 chrome 为主&#xff0c;后面简介一下其他浏览器的环境配置。 chrome 下载: www.google.cn/chrome/ 2、chromedriver 环境配置 chromedriver 是chromedriver提…