作者:harvest guo来源:xilinx dsp specilist
本文通过对opencv中图像类型和函数处理方法的介绍,通过设计实例描述在vivadohls中调用opencv库函数实现图像处理的几个基本步骤,完成从opencv设计到rtl转换综合的开发流程。
开源计算机视觉 (opencv) 被广泛用于开发计算机视觉应用,它包含2500多个优化的视频函数的函数库并且专门针对台式机处理器和gpu进行优化。opencv的用户成千上万,opencv的设计无需修改即可在 zynq器件的arm处理器上运行。但是利用opencv实现的高清处理经常受外部存储器的限制,尤其是存储带宽会成为性能瓶颈,存储访问也会限制功耗效率。使用vivadohls高级语言综合工具,可以轻松实现opencv c++视频处理设计到rtl代码的转换,输出硬件加速器或者直接在fpga上实现实时视频处理功能。同时,zynq all-programmable soc是实现嵌入式计算机视觉应用的极好方法,很好解决了在单一处理器上实现视频处理性能低功耗高的限制,zynq高性能可编程逻辑和嵌入式arm内核,是一款功耗优化的集成式解决方案。
1 opencv中图像iplimage, cvmat, mat 类型的关系和vivadohls中图像hls::mat类型介绍
opencv中常见的与图像操作有关的数据容器有mat,cvmat和iplimage,这三种类型都可以代表和显示图像,但是,mat类型侧重于计算,数学性较高,opencv对mat类型的计算也进行了优化。而cvmat和iplimage类型更侧重于“图像”,opencv对其中的图像操作(缩放、单通道提取、图像阈值操作等)进行了优化。在opencv2.0之前,opencv是完全用c实现的,但是,iplimage类型与cvmat类型的关系类似于面向对象中的继承关系。实际上,cvmat之上还有一个更抽象的基类----cvarr,这在源代码中会常见。
1.1 opencv中mat类型:矩阵类型(matrix)。
在opencv中,mat是一个多维的密集数据数组。可以用来处理向量和矩阵、图像、直方图等等常见的多维数据。
mat有3个重要的方法:
1、mat mat = imread(const string* filename); 读取图像
2、imshow(const string framename, inputarray mat); 显示图像
3、imwrite (const string& filename, inputarray img); 储存图像
mat类型较cvmat与iplimage类型来说,有更强的矩阵运算能力,支持常见的矩阵运算。在计算密集型的应用当中,将cvmat与iplimage类型转化为mat类型将大大减少计算时间花费。
1.2 opencv中cvmat类型与iplimage类型:“图像”类型
在opencv中,mat类型与cvmat和iplimage类型都可以代表和显示图像,但是,mat类型侧重于计算,数学性较高,opencv对mat类型的计算也进行了优化。而cvmat和iplimage类型更侧重于“图像”,opencv对其中的图像操作(缩放、单通道提取、图像阈值操作等)进行了优化。
补充:iplimage由cvmat派生,而cvmat由cvarr派生即cvarr -》 cvmat -》 iplimage
cvarr用作函数的参数,无论传入的是cvmat或iplimage,内部都是按cvmat处理。
在opencv中,没有向量(vector)的数据结构。任何时候,但我们要表示向量时,用矩阵数据表示即可。
但是,cvmat类型与我们在线性代数课程上学的向量概念相比,更抽象,比如cvmat的元素数据类型并不仅限于基础数据类型,比如,下面创建一个二维数据矩阵:
cvmat* cvcreatmat(int rows ,int cols , int type);
这里的type可以是任意的预定义数据类型,比如rgb或者别的多通道数据。这样我们便可以在一个cvmat矩阵上表示丰富多彩的图像了。
1.3 opencv中iplimage类型
在opencv类型关系上,我们可以说iplimage类型继承自cvmat类型,当然还包括其他的变量将之解析成图像数据。
iplimage类型较之cvmat多了很多参数,比如depth和nchannels。在普通的矩阵类型当中,通常深度和通道数被同时表示,如用32位表示rgb+alpha.但是,在图像处理中,我们往往将深度与通道数分开处理,这样做是opencv对图像表示的一种优化方案。
iplimage的对图像的另一种优化是变量origin----原点。在计算机视觉处理上,一个重要的不便是对原点的定义不清楚,图像来源,编码格式,甚至操作系统都会对原地的选取产生影响。为了弥补这一点,opencv允许用户定义自己的原点设置。取值0表示原点位于图片左上角,1表示左下角。
1.4 vivadohls中图像数据类型hls::mat《》
vivadohls视频处理函数库使用hls::mat《》数据类型,这种类型用于模型化视频像素流处理,实质等同于hls::steam《》流的类型,而不是opencv中在外部memory中存储的matrix矩阵类型。因此,在hls实现opencv的设计中,需要将输入和输出hls可综合的视频设计接口,修改为video stream接口,也就是采用hls提供的video接口可综合函数,实现axi4 video stream到vivadohls中hls::mat《》类型的转换。
2 使用vivadohls实现opencv到rtl代码转换的流程
2.1 opencv设计中的权衡
opencv图像处理是基于存储器帧缓存而构建的,它总是假设视频frame数据存放在外部ddr 存储器中,因此,opencv对于访问局部图像性能较差,因为处理器的小容量高速缓存性能不足以完成这个任务。而且出于性能考虑,基于opencv设计的架构比较复杂,功耗更高。在对分辨率或帧速率要求低,或者在更大的图像中对需要的特征或区域进行处理是,opencv似乎足以满足很多应用的要求,但对于高分辨率高帧率实时处理的场景下,opencv很难满足高性能和低功耗的需求。
基于视频流的架构能提供高性能和低功耗,链条化的图像处理函数能减少外部存储器访问,针对视频优化的行缓存和窗口缓存比处理器高速缓存更简单,更易于用fpga部件,使用vivadohls中的数据流优化来实现。
vivadohls对opencv的支持,不是指可以将opencv的函数库直接综合成rtl代码,而是需要将代码转换为可综合的代码,这些可综合的视频库称为hls视频库,由vivadohls提供。
opencv函数不能直接通过hls进行综合,因为opencv函数一般都包含动态的内存分配、浮点以及假设图像在外部存储器中存放或者修改。
vivadohls视频库用于替换很多基本的 opencv函数,它与opencv具有相似的接口和算法,主要针对在fpga架构中实现的图像处理函数,包含了专门面向fpga的优化,比如定点运算而非浮点运算(不必精确到比特位),片上的行缓存(line buffer)和窗口缓存(window buffer)。
2.2 vivadohls实现opencv设计流程介绍
使用vivadohls实现opencv的开发,主要的三个步骤如下:
在计算机上开发opencv应用,由于是开源的设计,采用c++的编译器对其进行编译,仿真和debug,最后产生可执行文件。这些设计无需修改即可在 arm内核上运行opencv应用。
使用i/o函数抽取fpga实现的部分,并且使用可综合的vivadohls video库函数代码代替opencv函数的调用。
运行hls生成rtl代码,在vivadohls工程中启动co-sim,重用opencv的测试激励验证产生的rtl代码。在ise或者vivado开发环境中做rtl的集成和soc/fpga实现。
2.2.1 vivadohls视频库函数
hls视频库是包含在hls命名空间内的c++代码。#include “hls_video.h”
与opencv等具有相似的接口和等效的行为,例如:
opencv库:cvscale(src, dst, scale, shift);
hls视频库:hls:cale《。。。》(src, dst, scale, shift);
一些构造函数具有类似的或替代性的模板参数,例如:
opencv库:cv::mat mat(rows, cols, cv_8uc3);
hls视频库:hls::mat mat(rows, cols);
rows和cols指定处理的最大图像尺寸
表2.2.1 vivadohls视频处理函数库
2.2.2 vivadhls实现opencv设计的局限性
首先,必须用hls视频库函数代替opencv调用。
其次,不支持opencv通过指针访问帧缓存,可以在hls中使用vdma和 axi stream adpater函数代替。
再者,不支持opencv的随机访问。hls对于读取超过一次的数据必须进行复制,更多的例子可以参见见hls:uplicate()函数。
最后,不支持opencvs的in-place更新,比如 cvrectangle (img, point1, point2)。
下面表格2.2.2列举了opencv中随机访问一帧图像处理对应hls视频库的实现方法。
opencvhls视频库
读操作pix = cv_mat.at(i,j)
pix = cvget2d(cv_img,i,j)hls_img 》》 pix
写操作cv_mat.at(i,j) = pix
cvset2d(cv_img,i,j,pix)hls_img 《《 pix
表 2.2.2 opencv和hls中对一帧图像像素访问对应方法
2.3 用hls实现opencv应用的实例(快速角点滤波器image_filter)
我们通过快速角点的例子,说明通常用vivadohls实现opencv的流程。首先,开发基于opencv的快速角点算法设计,并使用基于opencv的测试激励仿真验证这个算法。接着,建立基于视频数据流链的opencv处理算法,改写前面直觉的opencv的通常设计,这样的改写是为了与hls视频库处理机制相同,方便后面步骤的函数替换。最后,将改写的opencv设计中的函数,替换为hls提供的相应功能的视频函数,并用vivadohls综合,最后在xilinx开发环境下实现。当然,这些可综合代码也可在处理器或arm上运行。
2.3.1 设计基于opencv的视频滤波器设计和测试激励
在这个例子中,首先设计开发完全调用opencv库函数的快速角点滤波器设计opencv_image_filter.cpp和这个滤波器的测试激励opencv_image_filter_tb.cpp,测试激励用于仿真验证opencv_image_filter算法功能。算法和测试激励设计代码如下:
void opencv_image_filter(iplimage* src, iplimage* dst)
{
iplimage* gray = cvcreateimage( cvgetsize(src), 8, 1 );
std::vector keypoints;
cv::mat gray_mat(gray,0);
cvcvtcolor( src, gray, cv_bgr2gray );
cv::fast( gray_mat, keypoints, 20, true);
cvcopy( src,dst);
for (int i=0;i
{
cvrectangle(dst, cvpoint(keypoints[i].pt.x-1,keypoints[i].pt.y-1),
cvpoint(keypoints[i].pt.x+1,keypoints[i].pt.y+1), cvscalar(255,0,0),cv_filled);
}
cvreleaseimage( &gray );
}
例子2.3.1.1 通常的opencv视频处理代码opencv_image_filter.cpp
int main (int argc, char** argv) {
iplimage* src=cvloadimage(input_image);
iplimage* dst = cvcreateimage(cvgetsize(src), src-》depth, src-》nchannels);
opencv_image_filter(src, dst);
cvsaveimage(output_image_golden, dst);
cvreleaseimage(&src);
cvreleaseimage(&dst);
return 0;
}
例子2.3.1.2 opencv视频处理测试激励代码opencv_image_filter_tb.cpp
上面的例子是直接调用opencv在处理器上软件应用实现的例子,可以看到在算法设计中直接调用opencv库函数,测试激励读入图像,经过滤波器处理输出的图像保存分析。可以看到,算法的处理基于ipiimage类型,输入和输出图像都使用此类型。
2.3.2 使用io函数和vivado hls视频库替换opencv函数库
需要特别说明的是,xilinx通常使用的视频处理模块都是基于axi4 streaming协议进行不同模式见像素数据的交互,也就是我们所说的axi4 video接口协议格式。为了和xilinx视频库接口协议统一,vivadohls提供了视频接口函数库,用于从opencv程序中抽取需要进行rtl综合转换的顶层函数,并把这些可综合的代码和opencv不可综合转换的代码进行隔离。然后,对需要综合转换为rtl代码的opencv函数,用xilinx vivadohls提供相应功能的可综合video函数进行替换。最后在c/c++编译环境下仿真验证opencv代码和替换video函数后功能的一致,并在vivadohls开发环境中做代码综合和产生rtl代码的co-sim混合仿真验证。
vivadohls可综合的视频接口函数:
hls::axivideo2mat 转换axi4 video stream到hls::mat表示格式
hls::mat2axivideo 转换hls::mat数据格式到axi4 video stream
首先,我们对2.3.1中opencv的设计进行改写,改写的代码还是完全基于opencv的函数,目的是为了对视频的处理机制基于视频流的方式,与vivadohls视频库提供函数的处理机制一致。下面是opencv设计的另一种写法:
void opencv_image_filter(iplimage* src, iplimage* dst)
{
iplimage* gray = cvcreateimage( cvgetsize(src), 8, 1 );
iplimage* mask = cvcreateimage( cvgetsize(src), 8, 1 );
iplimage* dmask = cvcreateimage( cvgetsize(src), 8, 1 );
std::vector keypoints;
cv::mat gray_mat(gray,0);
cvcvtcolor(src, gray, cv_bgr2gray );
cv::fast(gray_mat, keypoints, 20, true);
genmask(mask, keypoints);
cvdilate(mask,dmask);
cvcopy(src,dst);
printmask(dst,dmask,cvscalar(255,0,0));
cvreleaseimage( &mask );
cvreleaseimage( &dmask );
cvreleaseimage( &gray );
}
例子2.3.2.1另一种opencv设计应用opencv_image_filter.cpp
其次,使用vivado hls视频库替代标准opencv函数,并使用可综合的视频接口函数,采用video stream的方式交互视频数据。用于fpga的硬件可综合模块由vivadohls视频库函数与接口组成,我们用hls命名空间中的相似函数代替opencv函数,增加接口函数构建axi4 stream类型的接口。
void image_filter(axi_stream& input, axi_stream& output, int rows, int cols)
{
//create axi streaming interfaces for the core
#pragma hls resource variable=input core=axis metadata=“-bus_bundle input_stream”
#pragma hls resource variable=output core=axis metadata=“-bus_bundle output_stream”
#pragma hls resource core=axi_slave variable=rows metadata=“-bus_bundle control_bus”
#pragma hls resource core=axi_slave variable=cols metadata=“-bus_bundle control_bus”
#pragma hls resource core=axi_slave variable=return metadata=“-bus_bundle control_bus”
#pragma hls interface ap_stable port=rows
#pragma hls interface ap_stable port=cols
hls::mat _src(rows,cols);
hls::mat _dst(rows,cols);
#pragma hls dataflow
hls::axivideo2mat(input, _src);
hls::mat src0(rows,cols);
hls::mat src1(rows,cols);
#pragma hls stream depth=20000 variable=src1.data_stream
hls::mat mask(rows,cols);
hls::mat dmask(rows,cols);
hls:calar《3,unsigned char》 color(255,0,0);
hls:uplicate(_src,src0,src1);
hls::mat gray(rows,cols);
hls::cvtcolor(src0,gray);
hls::fastx(gray,mask,20,true);
hls:ilate(mask,dmask);
hls:aintmask(src1,dmask,_dst,color);
hls::mat2axivideo(_dst, output);
}
例子2.3.2.2 采用vivadohls视频库替换后可综合的设计opencv_image_filter.cpp
最后,在vivadohls开发环境下综合例子2.3.2.2的设计,产生rtl代码并重用opencv的测试激励验证rtl代码功能。
3 vhls实现opencv设计流程总结
通过上面章节介绍以及在vivadohls工具中实现opencv设计的例子可以看出,opencv函数可实现计算机视觉算法的快速原型设计,并使用vivadohls工具转换为rtl代码在fpga或者zynq soc上实现高分辨率高帧率的实时视频处理。计算机视觉应用与生俱来的异构特性,使其需要软硬件相结合的实现方案。vivado hls视频库能加快opencv函数向fpga可编程架构的映射。
如何解决Python爬虫中文乱码问题?Python爬虫中文乱码的解决方法
高通合作中移通信研究所、中移物联网推路边单元 加速 LTE-V2X 在中国的商业推广
中国移动开启了2018-2019年通信工程设计第三批补充采购项目
安捷伦CDMA2000测试解决方案
关于确保电涌事件不会超过ESD保护电路的额定功率
Vivado设计流程分析 Vivado HLS实现OpenCV的开发流程
韩法院禁止LGD前员工向中国公司跳槽
无线传感器网络在车位控制中有哪些应用
新版双轨奖金制度直销软件自动结算管理系统
基于嵌入式Linux与QT的汽车虚拟仪表设计[图]
为什么晶圆是圆形而不是矩形
小米新机工信部入网:或为Max 3,八核心主频2.0GHz处理器
标准孔板流量计的原理及特点
氧化锆氧气传感器在氧气还原系统中的作用
任正非:华为在6G技术上也是领先的
目前全球正普遍使用的罗氏(Roche)检测设备
采用固定频率峰值电流模式拓扑的降压型DC/DC控制器
生物识别和人脸识别的区别
首款国产全数字PET进入临床试验收官阶段,将帮助更多患者解决难题
新能源储能电容应用之燃料电池汽车辅助储能系统