ZYNQ学习之路——在SDx中使用xfOpenCV图像加速处理

本文转载自:亦梦云烟的博客

简介
Xilinx的reVISION栈包含了一系列开发平台、算法和应用的开发资源,它支持流行的神经网络包括AlexNet, GoogleLeNet, VGG, SSD和FCN等,并且该视觉库提供了用于创建和实现CNN神经网络层的库,机器学习的元素被实现为一系列硬件加速的函数库,在应用开发层,Xilinx提供了标准的框架和库包括Caffe和OpenCV, reVISION栈同时也提供了第三方平台的开发平台,包括很多的传感器。

xfOpenCV是使用Xilinx SoC和FPGA优化硬件加速的OpenCV函数库,这些函数全部是用C/C++代码编写,使用高级综合工具(HLS)综合到FPGA中运行。本例程使用SDx2018.2和Zturn board平台进行设计。

OpenCV和xfOpenCV最主要的区别就是传统的OpenCV是在CPU(x86,ARM...)中运行,而xfOpenCV是在Xilinx SoC和FPGA中运行,使用SOC优化的代码可以比嵌入式GPU快40倍,比嵌入式CPU快100倍以上,鉴于所有的代码都是用C/C++编写,很容易移植为自己的机器视觉函数。

本文以Zturnboard为例介绍如何在SDSoC中使用OpenCV,本文在Ubuntu 16 64位,SDSoC2018.2(安装在ubuntu中)中测试通过。

一、概述
下图展示一个单传感器设计的reVISION框图:

图1-1 reVISION架构图

  • 视频数据输入(蓝色部分)
  • 使用硬件急速的内存到内存的机器视觉库(红色部分)
  • 视频数据输出(绿色部分)
  • 1.1 平台
    Xilinx提供了ZCU102和ZCU104的单传感器reVISION平台,它支持以下视频接口。

    1.1.1 源

  • USB2/3接口摄像头,高达1080p60或双目1080p30.
  • HDMI 输入达4K60
  • MIPI CSI通过FMC扩展板卡连接,高达4K60
  • 1.1.2 显示

  • HDMI输出4K60
  • DP输出4K30
  • 1.2 设计样例
    Xilinx提供了一些用于参考的设计样例,它们通常是从一个视频文件中读取每一帧进行处理,使用标准的OpenCV调用(如cv::imread()),然后使用xfopencv函数处理这一帧,最后输出为一个文件(如cv::imwrite())。例程说明了五种不同的xfopencv硬件加速视觉的OpenCV函数。

  • 双边滤波器
  • Harris滤波器
  • 密集光流
  • 双目视觉(深度检测)
  • 透视变换
  • 二、配置系统环境
    在本系列教程的基础上修改SDSoC platform。

    2.1 配置文件系统
    配置文件系统包含OpenCV的动态库

    $ cd
    $ petalinux-config -c rootfs
    //进入菜单选择
    PetaLinux Package Groups --->
    packagegroup-petalinux-opencv --->
    [*] packagegroup-petalinux-opencv

    图2-1 配置rootfs包含opencv

    NOTE:在首次链接OpenCV时需要联网,否则会报错!此时需要保证ubuntu能访问网络。

    然后编译工程并打包

    $ petalinux-build -c rootfs
    $ petalinux-package --boot --format BIN --fsbl ./images/linux/zynq_fsbl.elf --fpga ./images/linux/system.bit --u-boot

    将新生成的image.ub(大小会增加很多,大约50多MB)替换原来的文件新文件中增加了opencv的动态库文件,放在平台设计文件夹中即可。

    图2-2 编译petalinux工程后的images/linux文件夹内容

    用此镜像启动系统,查看/usr/lib目录,可以发现增加了opencv的动态库。

    root@ZturnTemplate:~# ls /usr/lib
    dri libgphoto2.so.6.0.0 libopencv_saliency.so.3.3.0
    gconv libgphoto2_port libopencv_shape.so.3.3
    gdk-pixbuf-2.0 libgphoto2_port.so.12 libopencv_shape.so.3.3.0
    gio libgphoto2_port.so.12.0.0 libopencv_stereo.so.3.3
    girepository-1.0 libgstapp-1.0.so.0 libopencv_stereo.so.3.3.0
    gstreamer-1.0 libgstapp-1.0.so.0.1202.0 libopencv_stitching.so.3.3
    libGL.so.1 libgstaudio-1.0.so.0 libopencv_stitching.so.3.3.0
    libGL.so.1.2.0 libgstaudio-1.0.so.0.1202.0 libopencv_structured_light.so.3.3
    libICE.so.6 libgstbase-1.0.so.0 libopencv_structured_light.so.3.3.0
    libICE.so.6.3.0 libgstbase-1.0.so.0.1202.0 libopencv_superres.so.3.3
    libSM.so.6 libgstcheck-1.0.so.0 libopencv_superres.so.3.3.0
    libSM.so.6.0.1 libgstcheck-1.0.so.0.1202.0 libopencv_surface_matching.so.3.3
    libX11-xcb.so.1 libgstcontroller-1.0.so.0 libopencv_surface_matching.so.3.3.0
    libX11-xcb.so.1.0.0 libgstcontroller-1.0.so.0.1202.0 libopencv_tracking.so.3.3
    libX11.so.6 libgstnet-1.0.so.0 libopencv_tracking.so.3.3.0
    libX11.so.6.3.0 libgstnet-1.0.so.0.1202.0 libopencv_video.so.3.3
    libXau.so.6 libgstpbutils-1.0.so.0 libopencv_video.so.3.3.0
    libXau.so.6.0.0 libgstpbutils-1.0.so.0.1202.0 libopencv_videoio.so.3.3
    libXcomposite.so.1 libgstreamer-1.0.so.0 libopencv_videoio.so.3.3.0
    libXcomposite.so.1.0.0 libgstreamer-1.0.so.0.1202.0 libopencv_videostab.so.3.3
    libXcursor.so.1 libgstriff-1.0.so.0 libopencv_videostab.so.3.3.0
    libXcursor.so.1.0.2 libgstriff-1.0.so.0.1202.0 libopencv_xfeatures2d.so.3.3
    libXdamage.so.1 libgsttag-1.0.so.0 libopencv_xfeatures2d.so.3.3.0
    libXdamage.so.1.1.0 libgsttag-1.0.so.0.1202.0 libopencv_ximgproc.so.3.3
    libXdmcp.so.6 libgstvideo-1.0.so.0 libopencv_ximgproc.so.3.3.0
    libXdmcp.so.6.0.0 libgstvideo-1.0.so.0.1202.0 libopencv_xobjdetect.so.3.3
    libXext.so.6 libgthread-2.0.so.0 libopencv_xobjdetect.so.3.3.0
    libXext.so.6.4.0 libgthread-2.0.so.0.5200.3 libopencv_xphoto.so.3.3
    libXfixes.so.3 libgtk-3.so.0 libopencv_xphoto.so.3.3.0
    libXfixes.so.3.1.0 libgtk-3.so.0.2200.17 liborc-0.4.so.0
    libXft.so.2 libharfbuzz.so.0 liborc-0.4.so.0.27.0
    libXft.so.2.3.2 libharfbuzz.so.0.10400.8 libpango-1.0.so.0
    libXi.so.6 libhistory.so.7 libpango-1.0.so.0.4000.6
    libXi.so.6.1.0 libhistory.so.7.0 libpangocairo-1.0.so.0
    libXrandr.so.2 libjpeg.so.62 libpangocairo-1.0.so.0.4000.6
    libXrandr.so.2.2.0 libjpeg.so.62.2.0 libpangoft2-1.0.so.0
    libXrender.so.1 libkmod.so.2 libpangoft2-1.0.so.0.4000.6
    libXrender.so.1.3.0 libkmod.so.2.3.2 libpangoxft-1.0.so.0
    libXtst.so.6 libltdl.so.7 libpangoxft-1.0.so.0.4000.6
    libXtst.so.6.1.0 libltdl.so.7.3.1 libpci.so.3
    libXxf86vm.so.1 liblzma.so.5 libpci.so.3.5.5
    libXxf86vm.so.1.0.0 liblzma.so.5.2.3 libpcre.so.1
    libatk-1.0.so.0 libmediactl.so.0 libpcre.so.1.2.9
    libatk-1.0.so.0.22409.1 libmediactl.so.0.0.0 libpixman-1.so.0
    libatk-bridge-2.0.so.0 libopencv_aruco.so.3.3 libpixman-1.so.0.34.0
    libatk-bridge-2.0.so.0.0.0 libopencv_aruco.so.3.3.0 libpng16.so.16
    libatspi.so.0 libopencv_bgsegm.so.3.3 libpng16.so.16.31.0
    libatspi.so.0.0.1 libopencv_bgsegm.so.3.3.0 libpython3.5m.so.1.0
    libbz2.so.1 libopencv_bioinspired.so.3.3 libreadline.so.7
    libbz2.so.1.0.6 libopencv_bioinspired.so.3.3.0 libreadline.so.7.0
    libcairo-gobject.so.2 libopencv_calib3d.so.3.3 libsocketcan.so.2
    libcairo-gobject.so.2.11400.10 libopencv_calib3d.so.3.3.0 libsocketcan.so.2.2.1
    libcairo.so.2 libopencv_ccalib.so.3.3 libssl.so.1.0.2
    libcairo.so.2.11400.10 libopencv_ccalib.so.3.3.0 libstdc++.so.6
    libcrypto.so.1.0.2 libopencv_core.so.3.3 libstdc++.so.6.0.24
    libdbus-1.so.3 libopencv_core.so.3.3.0 libtbb.so.2
    libdbus-1.so.3.14.12 libopencv_dpm.so.3.3 libtbbmalloc.so.2
    libdrm.so.2 libopencv_dpm.so.3.3.0 libtbbmalloc_proxy.so.2
    libdrm.so.2.4.0 libopencv_face.so.3.3 libtiff.so.5
    libdvbv5.so.0 libopencv_face.so.3.3.0 libtiff.so.5.2.6
    libdvbv5.so.0.0.0 libopencv_features2d.so.3.3 libv4l
    libelf-0.170.so libopencv_features2d.so.3.3.0 libv4l1.so.0
    libelf.so.1 libopencv_flann.so.3.3 libv4l1.so.0.0.0
    libepoxy.so.0 libopencv_flann.so.3.3.0 libv4l2.so.0
    libepoxy.so.0.0.0 libopencv_fuzzy.so.3.3 libv4l2.so.0.0.0
    libexif.so.12 libopencv_fuzzy.so.3.3.0 libv4l2rds.so.0
    libexif.so.12.3.3 libopencv_highgui.so.3.3 libv4l2rds.so.0.0.0
    libexpat.so.1 libopencv_highgui.so.3.3.0 libv4l2subdev.so.0
    libexpat.so.1.6.5 libopencv_img_hash.so.3.3 libv4l2subdev.so.0.0.0
    libffi.so.6 libopencv_img_hash.so.3.3.0 libv4lconvert.so.0
    libffi.so.6.0.4 libopencv_imgcodecs.so.3.3 libv4lconvert.so.0.0.0
    libfontconfig.so.1 libopencv_imgcodecs.so.3.3.0 libwebp.so.7
    libfontconfig.so.1.9.4 libopencv_imgproc.so.3.3 libwebp.so.7.0.0
    libfreetype.so.6 libopencv_imgproc.so.3.3.0 libwebpdemux.so.2
    libfreetype.so.6.14.0 libopencv_line_descriptor.so.3.3 libwebpdemux.so.2.0.2
    libgailutil-3.so.0 libopencv_line_descriptor.so.3.3.0 libwebpmux.so.3
    libgailutil-3.so.0.0.0 libopencv_ml.so.3.3 libwebpmux.so.3.0.0
    libgdk-3.so.0 libopencv_ml.so.3.3.0 libxcb-dri2.so.0
    libgdk-3.so.0.2200.17 libopencv_objdetect.so.3.3 libxcb-dri2.so.0.0.0
    libgdk_pixbuf-2.0.so.0 libopencv_objdetect.so.3.3.0 libxcb-glx.so.0
    libgdk_pixbuf-2.0.so.0.3608.0 libopencv_optflow.so.3.3 libxcb-glx.so.0.0.0
    libgio-2.0.so.0 libopencv_optflow.so.3.3.0 libxcb-render.so.0
    libgio-2.0.so.0.5200.3 libopencv_phase_unwrapping.so.3.3 libxcb-render.so.0.0.0
    libglapi.so.0 libopencv_phase_unwrapping.so.3.3.0 libxcb-shm.so.0
    libglapi.so.0.0.0 libopencv_photo.so.3.3 libxcb-shm.so.0.0.0
    libglib-2.0.so.0 libopencv_photo.so.3.3.0 libxcb.so.1
    libglib-2.0.so.0.5200.3 libopencv_plot.so.3.3 libxcb.so.1.1.0
    libgmodule-2.0.so.0 libopencv_plot.so.3.3.0 libxml2.so.2
    libgmodule-2.0.so.0.5200.3 libopencv_reg.so.3.3 libxml2.so.2.9.4
    libgobject-2.0.so.0 libopencv_reg.so.3.3.0 opkg
    libgobject-2.0.so.0.5200.3 libopencv_rgbd.so.3.3 python3.5
    libgphoto2 libopencv_rgbd.so.3.3.0 ssl
    libgphoto2.so.6 libopencv_saliency.so.3.3 systemd
    root@ZturnTemplate:~#

    2.2 配置xfOpenCV库
    将编译rootfs时生成的rootfs.tar.gz文件复制到自己的一个文件夹中(/home/software/SDSoC/rootfs),解压后得到Zynq的文件系统中的文件。

    图2-3 linux文件系统解压后的路径

    将该路径添加到Linux系统的环境变量中:

    sudo gedit ~/.bashrc

    在最后一行添加:

    export SYSROOT_arm64=/home/biac/software/SDSoC/rootfs

    2.3 配置xfOpenCV库
    在SDx IDE中,点击Xilinx -> SDx Libraries,点击下载Xilinx xfOpenCV Library。

    图2-4 在SDx IDE中下载xfOpenCV

    NOTE:下载的库可以被添加到任何工程中。

    三、测试opencv库
    新建一个SDSoC应用工程,命名为opencv_lab1。

    添加main.cpp文件,编辑内容如下:

    #include
    #include

    // colordetect
    // Description:
    // Will detect the colors from the thresholds provided
    // Inputs:
    // - in_img
    // - nLowThresh
    // - nHighThresh
    // Output:
    // - out_img
    void colordetect(cv::Mat &_src,
    cv::Mat &_dst,
    unsigned char nLowThresh[3][3],
    unsigned char nHighThresh[3][3]) {

    // Temporary matrices for processing
    cv::Mat mask1, mask2, mask3, _imgrange, _imghsv;

    // Convert the input to the HSV colorspace. Using BGR here since it is the default of OpenCV.
    // Using RGB yields different results, requiring a change of the threshold ranges
    cv::cvtColor(_src, _imghsv, cv::COLOR_BGR2HSV);

    // Get the color of Yellow from the HSV image and store it as a mask
    cv::inRange(_imghsv, cv::Scalar(nLowThresh[0][0], nLowThresh[0][1], nLowThresh[0][2]), cv::Scalar(nHighThresh[0][0], nHighThresh[0][1], nHighThresh[0][2]), mask1);

    // Get the color of Green from the HSV image and store it as a mask
    cv::inRange(_imghsv, cv::Scalar(nLowThresh[1][0], nLowThresh[1][1], nLowThresh[1][2]), cv::Scalar(nHighThresh[1][0], nHighThresh[1][1], nHighThresh[1][2]), mask2);

    // Get the color of Red from the HSV image and store it as a mask
    cv::inRange(_imghsv, cv::Scalar(nLowThresh[2][0], nLowThresh[2][1], nLowThresh[2][2]), cv::Scalar(nHighThresh[2][0], nHighThresh[2][1], nHighThresh[2][2]), mask3);

    // Bitwise OR the masks together (adding them) to the range
    _imgrange = mask1 | mask2 | mask3;

    cv::Mat element = cv::getStructuringElement( 0,cv::Size(3, 3), cv::Point(-1, -1));

    cv::erode(_imgrange, _dst, element, cv::Point(-1, -1), 1, cv::BORDER_CONSTANT);

    cv::dilate(_dst, _dst, element, cv::Point(-1, -1), 1, cv::BORDER_CONSTANT);

    cv::dilate(_dst, _dst, element, cv::Point(-1, -1), 1, cv::BORDER_CONSTANT);

    cv::erode(_dst, _dst, element, cv::Point(-1, -1), 1, cv::BORDER_CONSTANT);
    }

    int main(int argc, char **argv)
    {
    printf("Test for SDSoC platform\n");
    //Create the input/output cv::Mat objects
    cv::Mat in_img, out_img;
    cv::Mat imghsv, imgrange, imgerode, imgdilate1, imgdilate2;

    // Define the low and high thresholds
    // Want to grab 3 colors (Blue, Green, Orange) for teh input image
    unsigned char nLowThresh[3][3] = { { 110, 150, 20 }, // Lower boundary for Blue
    { 38, 0, 20 }, // Lower boundary for Green
    { 10, 150, 20 } }; // Lower boundary for Orange
    unsigned char nHighThresh[3][3] = { { 130, 255, 255 }, // Upper boundary for Blue
    { 75, 125, 255 }, // Upper boundary for Green
    { 25, 255, 255 } }; // Upper boundary for Orange
    // Read an image
    in_img = cv::imread("rock_landscape.jpg", 1);
    if (!in_img.data) {
    return -1;
    }

    // Create the output image to match the input image (CV_8U)
    int height = in_img.rows;
    int width = in_img.cols;
    out_img.create(height, width, CV_8U);

    // Run the input and thresholds into the colordect function
    colordetect(in_img, out_img, nLowThresh, nHighThresh);

    // Write out the input image and the output image
    cv::imwrite("output.png", out_img);
    cv::imwrite("input.png", in_img);

    return 0;
    }

    四、配置编译环境
    下载完库文件之后需要将库路径添加到应用工程中。

    点击Xilinx -> SDx Libraries,选中Xilinx xfOpenCV Library,点击Add to project下拉框,选择自己的应用工程即可。

    图4-1 将SDx库添加到工程中

    此时,所有的opencv头文件和库都将被复制到工程目录的libs目录下(工程目录中的libs文件夹中),所有需要设置的选项也相应的自动进行设置好了。

    图4-2 添加xfOpencv库后的目录

    在编译选项中,opencv_前缀的链接库都自动添加到工程的设置中。

    图4-3 自动添加opencv链接库

    添加路径:右键点击工程,选择Properties -> C/C++ General -> Paths and Symbols -> Includes,在GNU C和GNU C++中都添加xfopencv的include路径。如图1-9所示。

    图4-4 添加xfopencv的库路径

    添加链接:右键点击工程,选择Properties -> C/C++ Build -> Tool Settings -> SDSCC Compiler -> Inferred Options -> Software Platform

    Software platform Inferred Flags中输入:

    -I${SYSROOT_arm64}/usr/include -hls-target 1

    Properties -> C/C++ Build -> Tool Settings -> SDS++ Compiler -> Inferred Options -> Software Platform中执行同样的操作。

    Properties -> C/C++ Build -> Tool Settings -> SDS++ Linker -> Miscellaneous -> Linker Flags中添加:

    --sysroot=${SYSROOT_arm64} -Wl,-rpath-link=${SYSROOT_arm64}/lib,-rpath-link=${SYSROOT_arm64}/usr/lib

    编译工程。

    可能出现错误,根据错误进行修改

    错误提示:
    arm-linux-gnueabihf-g++.exe: error: unrecognized command line option '-mstrict-align'; did you mean '-Wstrict-aliasing'?

    解决方法:在Properties -> C/C++ Build -> Settings -> SDSCC Compiler和 SDS++ Compiler中的Miscellaneous,删除other flags中的编译选项,只留-c -fmessage-length=0 -MT"$@" -hls-target 1。

    编译成功后, 将sd_card目录中的所有文件复制到SD卡中,并将例程中的图片复制到SD卡中。

    执行程序:
    $ mount /dev/mmcblk0p1 /mnt/
    $ cd /mnt/
    $ ./opencv_lab1.elf
    $ ls
    System Volume Information
    UPDATE
    image.ub
    input.png
    opencv_lab1.elf
    output.png
    rock_landscape.jpg
    sds_trace_data.dat

    执行成功后在SD卡中生成了两个图片文件: input.png和output.png。

    图4-5 output.jpg

    五、硬件加速opencv
    以xilinx在xfopencv中提供的样例做测试,复制xfopencv/examples/histogram目录中的C文件到自己的SDx工程中,安装前文所讲配置编译环境。

    图5-1 histogram工程文件结构

    添加Hardware Functions:

    展开libs/include/imgproc/xf_histogram.hpp/xf

    右键选择calcHist函数,选择Toggle HW/SW,将dilate函数作为硬件实现的函数,默认时钟为100MHz。

    图5-2 添加硬件加速函数

    编译完成后,拷贝sd_card中所有文件到SD开根目录,执行程序:

    mount /dev/mmcblk0p1 /mnt/
    cd /mnt/
    ./opencv_lab1.elf im0.jpg
    Start sw accel function
    elapsed time 9366176
    Start hw accel function
    elapsed time 13883070

    可以看到硬件加速的函数自行时间比软件还要长,后面讲分析原因。

    参考资料
    [1] ug1233-xilinx-opencv-user-guide.pdf

    [2] https://github.com/Xilinx/Revision-Getting-Started-Guide/tree/2018.2-tut...

    [3] https://github.com/Xilinx/SDSoC-Tutorials/tree/master/opencv-to-xfopencv...

    [4] https://blog.csdn.net/lulugay/article/details/83305194

    最新文章

    最新文章