使用 HLS 的 FPGA 的边缘检测
利用 HLS 功能创建图像处理解决方案,在FPGA中实现边缘检测 (Sobel)。
本项目中用到的东西
代码语言:javascript复制<center>FPGA Zybo</center>
Apeman 1080P HD Action Camera
介绍
HLS是高层综合(High level Synthesis),是将C或者c 语言编译为FPGA能够读懂和运行的RTL级别的语言。通过HLS这个过程可以显著加快FPGA的设计进程,而不用从底层的FPGA语言编起,如果是商业项目,通过HLS可以节省时间并降低成本。
HLS 的一个重要应用是图像或信号处理,我们可能已经用 C 或 C 创建了一个高级模型,或者我们希望使用开源行业标准框架,例如 OpenCV。
在本项目中,我们将研究如何使用 HLS 构建 Sobel 边缘检测 IP 核,然后将其包含在我们选择的 Xilinx FPGA 中。
所选器件可以是传统的 FPGA,例如 Spartan 7 或 Artix 7,或者也可以在异构 SoC 的可编程逻辑中实现,例如 Zynq 7000 或 Zynq MPSoC。
理论
在我们进入应用程序编写之前,应该先简单介绍一下 Sobel 算法的工作原理。Sobel 算法根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。
Sobel 边缘检测的工作原理是检测图像在水平和垂直方向上的梯度变化。为此,将两个卷积滤波器应用于原始图像,然后组合这些卷积滤波器的结果以确定梯度的大小。
Sobel卷积滤波器
执行
如果我们使用传统的 VHDL / Verilog RTL 在 FPGA 中实现这一点,那么开发时间将会很长。因为我们需要为卷积创建行缓冲区,然后实现幅度计算。我们还需要创建一个测试平台,以确保我们的代码在进行实施之前能按预期工作。
幸运的是,当我们使用 HLS 时,我们可以跳过很多繁重的工作,让 Vivado HLS 实现低级别的 Verilog/VHDL RTL 难以实现的是事情。
设计时将使用 Vivado HLS 及其 HLS_OpenCV 和 HLS_Video 库。
第一个库 HLS_OpenCV 允许使用非常流行的 OpenCV 框架。而 HLS_Video提供了许多可以加速为可编程逻辑的图像处理功能。
而HLS_Video库中也包括我们需要使用的Sobel_IP,内容包括:-
- HLS::CvtColor - 将根据其配置在颜色和灰度之间转换颜色空间。
- HLS::Gaussian - 将对图像执行高斯模糊以减少图像中存在的噪声。
- HLS::Sobel - 根据其配置在垂直或水平方向执行 Sobel 卷积。将需要在我们的 IP 核中使用两个实现。
- HLS::AddWeighted - 允许使用来自垂直和水平 Sobel 算子的结果来执行结果幅度计算。上面这些不是我们将使用的所有 HLS 函数,我们还需要使用其他函数。
互联
在FPGA内部移动图像数据的最佳方法是使用 AXI 流。AXI允许创建高性能图像处理路径,其中元素可以根据需要轻松添加或创建。
Vivado IP 库中存在多个 IP 模块,可实现视频输入和输出与 AXI 流之间的转换,以及其他图像处理功能,例如混合器(mixers)和色彩空间转换器( color space converters)。
因此,我们希望 Sobel IP 核能够接受 AXI Stream 输入并以相同的 AXI Stream 格式生成输出。为此,我们使用以下函数允许在 AXI 流和 HLS 函数使用的 HLS::Mat 格式之间进行转换。
- HLS::AXIvideo2Mat - 从 AXI 流转换为用于 AXI 流输入的 HLS::Mat 格式。
- HLS::Mat2AXIvideo - 从 HLS::Mat 格式转换为 AXI Stream 格式,用于 AXI Stream 输出。
C 综合和优化
与 Verilog 和 VHDL 设计不同,我们用来描述设计的高级语言是不可控的。这意味着当 HLS 工具将 C 转换为 Verilog 或 VHDL 时,它必须经过多个阶段才能创建输出我们需要的 RTL。
- 调度Scheduling - 确定操作及其发生的顺序。
- 绑定Binding - 将操作分配给设备内可用的逻辑资源。
- 控制逻辑提取Control Logic Extraction - 提取控制逻辑并创建控制结构,例如状态机的行为。
HLS转化成RTL流程
由于 HLS 工具在运行综合时必须在性能和逻辑资源之间进行权衡,因此在实现过程中将遵循许多规则。这些可能会影响生成的 IP 核的性能,例如流水操作(HLS 编码中的常见结构)。
当然,我们可能希望更改 HLS 工具在 C 综合期间做出的决定以获得更好的性能,可以 C 中使用 #pragmas 来做到这一点。
对于本次设计,我们将使用 Dataflow pragma 来确保我们可以达到最高的帧速率。
数据流流水线
为了能够使用此编译指示,我们需要确保 HLS 综合工具并行执行两个 Sobel 操作。如果我们先执行一个 Sobel 操作,然后按顺序执行另一个操作,这将无法应用此优化。公众号:OpenFPGA
因此,我们需要将高斯模糊的结果分成两条平行路径,然后在 AddWeighted 阶段重新组合。为此,我们使用下面函数:
- HLS::Duplicate - 这将输入图像复制到两个单独的输出图像中,我们可以并行处理这些图像。
编写代码
了解所有这些之后,我们就可以编写用于 Sobel IP 核的代码
代码语言:javascript复制#include "cvt_colour.hpp"
void image_filter(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM)//, int rows, int cols)
{
#pragma HLS INTERFACE axis port=INPUT_STREAM
#pragma HLS INTERFACE axis port=OUTPUT_STREAM
RGB_IMAGE img_0(MAX_HEIGHT, MAX_WIDTH);
GRAY_IMAGE img_1(MAX_HEIGHT, MAX_WIDTH);
GRAY_IMAGE img_2(MAX_HEIGHT, MAX_WIDTH);
GRAY_IMAGE img_2a(MAX_HEIGHT, MAX_WIDTH);
GRAY_IMAGE img_2b(MAX_HEIGHT, MAX_WIDTH);
GRAY_IMAGE img_3(MAX_HEIGHT, MAX_WIDTH);
GRAY_IMAGE img_4(MAX_HEIGHT, MAX_WIDTH);
GRAY_IMAGE img_5(MAX_HEIGHT, MAX_WIDTH);
RGB_IMAGE img_6(MAX_HEIGHT, MAX_WIDTH);
;
#pragma HLS dataflow
hls::AXIvideo2Mat(INPUT_STREAM, img_0);
hls::CvtColor<HLS_BGR2GRAY>(img_0, img_1);
hls::GaussianBlur<3,3>(img_1,img_2);
hls::Duplicate(img_2,img_2a,img_2b);
hls::Sobel<1,0,3>(img_2a, img_3);
hls::Sobel<0,1,3>(img_2b, img_4);
hls::AddWeighted(img_4,0.5,img_3,0.5,0.0,img_5);
hls::CvtColor<HLS_GRAY2RGB>(img_5, img_6);
hls::Mat2AXIvideo(img_6, OUTPUT_STREAM);
}
#include "hls_video.h"
#include <ap_fixed.h>
#define MAX_WIDTH 1280
#define MAX_HEIGHT 720
typedef hls::stream<ap_axiu<24,1,1,1> > AXI_STREAM;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1> GRAY_IMAGE;
void image_filter(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM);//int rows, int cols);
当然,希望能够同时运行 C Simulation 和 Co Simulation,还需要一个可以用来测试算法的测试台。
代码语言:javascript复制#include <hls_opencv.h>
#include "cvt_colour.hpp"
#include <iostream>
using namespace std;
int main (int argc, char** argv) {
IplImage* src;
IplImage* dst;
AXI_STREAM src_axi, dst_axi;
src = cvLoadImage("test.bmp");
dst = cvCreateImage(cvGetSize(src), src->depth, src->nChannels);
IplImage2AXIvideo(src, src_axi);
image_filter(src_axi, dst_axi);//src->height,src->width);
AXIvideo2IplImage(dst_axi, dst);
cvSaveImage("op.bmp", dst);
cvReleaseImage(&src);
cvReleaseImage(&dst);
}
当我们运行 C Simulation 时,我们可以看到测试输入图像的结果如下。
用于C和Co仿真的输入测试图像
仿真Sobel的结果
有了仿真结果,我们就可以导出内核并将其添加到 Vivado 硬件设计中。
但是,在我们执行此操作之前,还需要检查分析、在 Vivado HLS 中查看并确认两个 Sobel 函数并行运行的结果。
显示并行Sobel操作的分析视图
使用 Vivado HLS 中的导出 RTL 选项导出 IP 核
硬件实现
导出核心后,您将在/solutionX/imp 目录下找到一个 zip 文件。该目录包含刚刚新创建的 Sobel IP ,将核添加到 Vivado IP库中。
然后 Vivado IP 中,可以看到相关IP,如下:
搭建BD,如下:
将IP集成到图像处理链中(注意图中的HLS符号)
项目源码
❝https://github.com/ATaylorCEngFIET/Hackster
本项目来源:https://www.hackster.io/adam-taylor/fpga-based-edge-detection-using-hls-192ad2
版权归hackster所有