亲,“电路城”已合并升级到更全、更大、更强的「新与非网」。点击查看「新与非网」

本网页已闲置超过3分钟,按键盘任意键或点击空白处,即可回到网页

基于Zybo Z7的图像处理加速项目

发布时间:2022-10-23
分享到:

基于Zybo Z7的图像处理加速项目

发布时间:2022-10-23
分享到:

创建新项目
为了创建我们的HLx图像处理块,我们将使用基于eclipse的Vivado HLS。一旦我们有Vivado HLS打开,要做的第一件事是创建一个新项目,并选择正确的目标设备。

在这种情况下,我们的目标是Zybo Z7,目标设备是XC7Z020-1CLG400C。

在这个项目中,我们将创建一个简单的函数,将彩色图像转换为灰度图像。将图像转换为灰度是许多图像处理应用中的一个常见步骤。当我们想要检测诸如边缘之类的元素时,使用灰度图像使处理变得不那么复杂。

目录
创建好项目后,我们将执行以下操作:

  • 在源代码目录下创建以下文件cvt_color .cpp和cvt_color .hpp。这些文件将用于创建RTL IP核。cpp文件将包含实际的函数,而头文件将提供函数定义以及其他公共定义和类型定义。
  • 在Test Bench目录下创建一个名为cvt_colour_tb.cpp的新cpp文件。这将是使用OpenCV函数测试加速函数性能的试验台。

与AXI流的接口
为了能够将加速IP核放入Zybo Z7设计中,我们需要能够与一个AXI流接口。

因此,我们需要输入和输出图像是AXI Streams。我们可以在代码中使用pragma来做到这一点,以确保HLS编译器实例化我们想要的接口。为了提供灵活性,我们还需要函数知道它正在处理的图像大小。

因此,函数定义变成:

void image_filter(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM, int rows, int cols)
AXI_STREAM输入是一个类型定义,它实现了带有边带信号的AXI Stream接口。需要这些边带信号来指示帧的开始(TUser)和行的结束(TLast)的指示。

为了定义带有边带信号的AXI Stream接口,我们在cvt_color .hpp文件中包含了以下类型定义:

typedef hls::stream
               
                 > AXI_STREAM;
               
ap_axiu结构由ap_axi_sdata.h定义,它支持带边带信号的有符号(ap_axis)和无符号(ap_axiu) AXI流。

上面的示例创建一个32位宽的数据总线,其中包含一位宽的TUser、TID和TDest,默认情况下包括TLast。

颜色转换与配置
随着作为AXI流接收和输出的图像,我们需要将AXI流转换为HLS::Mat格式,这是HLS::CVTColor函数所要求的。

我们在代码的必要位置使用AXIvideo2Mat和Mat2AXIvideo函数来实现这一点。

为了使AXI Stream和HLS::Mat之间的转换正常工作,我们需要预先定义HLS::Mat的大小和类型。

同样,这是通过cvt_color头文件定义的,并定义了最大宽度和高度,以及通道的数量和每个通道的深度。

因此,RGB HLS::Mat被定义为HLS_8UC3类型,它定义了一个8位、无符号、3通道结构。而Gray HLS::Mat则定义为HLS_8UC1 8位、无符号、1通道。

当从颜色转换到灰色时,我们必须能够适应一系列像素格式。使用hls::CvtColor转换函数,我们可以转换使用Red, Green, Blue或Blue, Green, Red表示格式的像素。

在这个应用程序中,当我们使用BMP输入时,像素格式是蓝色、绿色和红色。

hls: CvtColor < HLS_BGR2GRAY > (img_0 img_1);
最终代码
解释了AXI Stream IO、IO类型与颜色转换函数兼容的类型之间的转换以及转换函数本身的配置。最终要加速的代码如下:

#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(rows, cols);
GRAY_IMAGE img_1(rows, cols);
RGB_IMAGE  img_2(rows, cols);
#pragma HLS dataflow
hls::AXIvideo2Mat(INPUT_STREAM, img_0);
hls::CvtColor<HLS_BGR2GRAY>(img_0, img_1);
hls::CvtColor<HLS_GRAY2RGB>(img_1, img_2);
hls::Mat2AXIvideo(img_2, OUTPUT_STREAM);
}

而头文件包含以下内容:

#include  "hls_video.h"
#include <ap_fixed.h>
#define MAX_WIDTH  2000
#define MAX_HEIGHT 2000
typedef hls::stream<ap_axiu<32,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);

创建测试工作台
我们现在要做的就是创建一个测试台架。

在测试台架文件中,我们需要能够读取一个24位的BMP文件,将其应用到灰度转换函数中,并写出结果图像。

为了能够使用OpenCV,我们可以使用hls_opencv.h库。这提供了打开/关闭/保存图像所需的所有函数,并将它们转换为与AXI Streaming类型兼容的类型。

简单的测试平台代码如下所示:

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

C仿真
C模拟比相应的RTL模拟要快得多,并且是HLS验证过程的第一步,因为它使我们能够在进行更耗时的设计阶段之前确保设计中没有问题。

在运行C模拟之前,我们需要确保在项目源代码下有您希望转换的BMP。一旦出现,单击C模拟按钮。

C模拟将快速运行,其结果将在Solution 1/CSim/Build目录下可用。在这个目录中,您会注意到文件OP.bmp,这是C模拟的输出文件,应该显示输入图像,但是是灰度的。

一旦您对C模拟性能感到满意,下一步就是运行C合成,将C源代码转换为VHDL或Verilog IP块。

这可能需要一些时间,并将提供关于设备利用率和完成后延迟的简单报告。

在C综合之后的下一个阶段是协同仿真,这使C试验台能够对生成的RTL模型进行激励,并使结果再次被试验台捕获。

这确保了我们从RTL中获得与从Cosimulation中相同的性能。同样,你可以在路径下找到Cosimulation的结果:solution 1/Sim/Wrapc。

当我运行联合仿真并检查输出图像文件时,我得到了以下结果。

现在剩下的就是导出IP核,并将其添加到我们的Vivado设计中,就像我们做其他IP核一样。

如果您对此项目有任何想法、意见或问题,请在下方留言。

以上内容翻译自网络,原作者:Adam Taylor,如涉及侵权,可联系删除。

加入微信技术交流群

技术交流,职业进阶

关注与非网服务号

获取电子工程师福利

加入电路城 QQ 交流群

与技术大牛交朋友

讨论