STM32F7上的OpenCV-发现

我是Embox操作系统的开发人员之一,在本文中,我将讨论如何设法在STM32746G板上运行OpenCV。


如果您进入搜索引擎,例如“ STM32板上的OpenCV”,则会发现很多人对在STM32板上或其他微控制器上使用此库感兴趣。
根据名称判断,有几段视频应显示所需的内容,但通常(在我看到的所有视频中)在STM32板上,只有从摄像机接收到图像并将结果显示在屏幕上,并且图像处理是在STM32板上完成的在常规计算机上,或者在功能更强大的板上(例如Raspberry Pi)。


为什么很难?


搜索查询之所以受欢迎,是因为OpenCV是最流行的计算机视觉库,这意味着更多的开发人员对此很熟悉,并且能够在微控制器上为台式机运行代码的能力大大简化了开发过程。 但是,为什么仍然没有流行的现成食谱来解决这个问题?


在小型板上使用OpenCV的问题与两个功能有关:


  • 如果即使仅使用最少的模块集来编译库,由于代码量很大(几兆字节的指令),它也根本无法放入相同STM32F7Discovery的闪存中(即使不考虑操作系统)
  • 该库本身是用C ++编写的,这意味着
    • 需要积极的运行时支持(例外等)
    • 对LibC / Posix的支持很少,通常在嵌入式系统的OS中可以找到它-您需要标准的pluss库和STL模板的标准库(向量等)。

移植到Embox


像往常一样,在将任何程序移植到操作系统之前,最好以开发人员期望的形式组装它。 在我们的例子中,这没有问题-可以在github上找到源代码,该库是在GNU / Linux下使用通常的cmake构建的。


好消息是-开箱即用的OpenCV可以组装为静态库,从而使移植更加容易。 我们使用标准配置收集该库,并查看它们占用了多少空间。 每个模块都组装在一个单独的库中。


> size lib/*so --totals text data bss dec hex filename 1945822 15431 960 1962213 1df0e5 lib/libopencv_calib3d.so 17081885 170312 25640 17277837 107a38d lib/libopencv_core.so 10928229 137640 20192 11086061 a928ed lib/libopencv_dnn.so 842311 25680 1968 869959 d4647 lib/libopencv_features2d.so 423660 8552 184 432396 6990c lib/libopencv_flann.so 8034733 54872 1416 8091021 7b758d lib/libopencv_gapi.so 90741 3452 304 94497 17121 lib/libopencv_highgui.so 6338414 53152 968 6392534 618ad6 lib/libopencv_imgcodecs.so 21323564 155912 652056 22131532 151b34c lib/libopencv_imgproc.so 724323 12176 376 736875 b3e6b lib/libopencv_ml.so 429036 6864 464 436364 6a88c lib/libopencv_objdetect.so 6866973 50176 1064 6918213 699045 lib/libopencv_photo.so 698531 13640 160 712331 ade8b lib/libopencv_stitching.so 466295 6688 168 473151 7383f lib/libopencv_video.so 315858 6972 11576 334406 51a46 lib/libopencv_videoio.so 76510375 721519 717496 77949390 4a569ce (TOTALS) 

从最后一行可以看到,.bss和.data不会占用太多空间,但是代码超过70 MiB。 显然,如果将它与特定应用程序静态链接,则代码将变得更小。


让我们尝试抛出尽可能多的模块,以便组装一个最小的示例(例如,仅显示OpenCV的版本),因此请注意cmake .. -LA并禁用选项中禁用的所有内容。


  -DBUILD_opencv_java_bindings_generator=OFF \ -DBUILD_opencv_stitching=OFF \ -DWITH_PROTOBUF=OFF \ -DWITH_PTHREADS_PF=OFF \ -DWITH_QUIRC=OFF \ -DWITH_TIFF=OFF \ -DWITH_V4L=OFF \ -DWITH_VTK=OFF \ -DWITH_WEBP=OFF \ <...> 

 > size lib/libopencv_core.a --totals text data bss dec hex filename 3317069 36425 17987 3371481 3371d9 (TOTALS) 

一方面,这只是一个库模块,另一方面,编译器没有根据代码大小( -Os )对其进行优化。 〜3 MiB的代码仍然很多,但是已经给成功带来了希望。


在模拟器中运行


在模拟器上进行调试要容易得多,因此首先请确保该库在qemu上运行。 作为仿真平台,我选择了Integrator / CP,因为 首先,它也是ARM,其次,Embox支持该平台的图形输出。


Embox具有一种构建外部库的机制,使用它,我们将OpenCV作为模块添加(将“最小”构建的所有相同选项作为静态库传递),然后添加最简单的应用程序,如下所示:


 version.cpp: #include <stdio.h> #include <opencv2/core/utility.hpp> int main() { printf("OpenCV: %s", cv::getBuildInformation().c_str()); return 0; } 

我们组装系统,运行它-我们得到了预期的结论。


 root@embox:/#opencv_version OpenCV: General configuration for OpenCV 4.0.1 ===================================== Version control: bd6927bdf-dirty Platform: Timestamp: 2019-06-21T10:02:18Z Host: Linux 5.1.7-arch1-1-ARCH x86_64 Target: Generic arm-unknown-none CMake: 3.14.5 CMake generator: Unix Makefiles CMake build tool: /usr/bin/make Configuration: Debug CPU/HW features: Baseline: requested: DETECT disabled: VFPV3 NEON C/C++: Built as dynamic libs?: NO <      --    ,   OpenCV     ..> 

下一步是运行一些示例,最好是开发人员自己在其网站上提供的所有标准。 我选择了Canny的边界探测器


为了将图像和结果直接显示在帧缓冲区中,必须对示例进行一些重写。 我必须这样做,因为 imshow()函数能够通过QT,GTK和Windows界面绘制图像,当然,这些界面绝对不会出现在STM32配置中。 实际上,QT也可以在STM32F7Discovery上运行,但这将在另一篇文章中讨论:)


在简短说明以何种格式存储边界检测器的结果后,我们得到了图像。



原始图片



结果


在STM32F7Discovery上运行


无论如何我们都可以使用32F746GDISCOVERY上的几个硬件分区


  1. 320KiB内存
  2. 1 MiB闪光灯拍摄图像
  3. 8MiB SDRAM
  4. 16MiB QSPI NAND闪存驱动器
  5. MicroSD卡插槽

SD卡可用于存储图像,但是在运行一个最小示例的情况下,这不是很有用。
显示屏的分辨率为480x272,这意味着帧缓冲区的内存为522,240字节,深度为32位,即 这超出了RAM的大小,因此我们将帧缓冲区和一堆(OpenCV需要使用一堆存储图像和辅助结构的数据)放在SDRAM中,其他所有内容(用于堆栈和其他系统需求的内存)都将放入RAM 。


如果我们为STM32F7Discovery进行最小配置(将整个网络,所有命令都扔掉,使堆栈尽可能小等),并在其中添加带有示例的OpenCV,并带有所需的内存,则将是:


  text data bss dec hex filename 2876890 459208 312736 3648834 37ad42 build/base/bin/embox 

对于那些不太了解要折叠的部分的人,我将说明:指令和常量(大致为只读数据)位于.text.rodata ,数据是可变的.data ,而“零”位于.bss但是,需要放置一个变量(此部分将“转到” RAM)。


好消息是.data / .bss应该适合,但是使用.text麻烦是图像只有1MiB的内存。 您可以从.text示例中.text图片,并在启动时从SD卡中读取.text图片,例如,将其从SD卡读取到内存中,但是fruit.png的重量约为330KiB,因此这不能解决问题:大多数.text由OpenCV代码组成。


总的来说,只剩下一件事了-将部分代码加载到QSPI闪存驱动器上(它具有一种特殊的操作模式,用于将内存映射到系统总线,以便处理器可以直接访问此数据)。 在这种情况下,会出现问题:首先,QSPI闪存驱动器的内存在设备重启后立即不可用(您需要单独初始化内存映射模式),其次,您无法使用常规的引导加载程序来刷新此内存。


结果,决定链接QSPI中的所有代码,并用引导加载程序对其进行刷新,该引导加载程序将通过TFTP接收必要的二进制文件。


结果


将该库移植到Embox的想法大约是在一年前提出的,但是由于种种原因,它一再被推迟。 其中之一是对libstdc ++和standart模板库的支持。 在Embox中支持C ++的问题不在本文讨论范围之内,因此在这里我只想说说我们已经设法以适当的数量实现了对这种库的支持:)


最后,克服了这些问题(至少足以使OpenCV示例正常工作),并且该示例开始了。 40秒钟使电路板通过Canny过滤器搜索边界。 当然,这太长了(考虑如何优化此问题,如果成功,有可能会撰写另一篇文章)。




尽管如此,中间的目标是创建一个原型,该原型将展示在STM32上分别运行OpenCV的基本可能性,这一目标实现了,加油!

tl; dr:分步说明


0:下载Embox的资源,例如:


  git clone https://github.com/embox/embox && cd ./embox 

1:让我们开始构建一个引导加载程序,它将“刷新” QSPI闪存驱动器。


  make confload-arm/stm32f7cube 

现在,您需要配置网络,因为 我们将通过TFTP上传图像。 为了设置板卡和主机的IP地址,您需要修改conf / rootfs / network文件。


配置示例:


 iface eth0 inet static address 192.168.2.2 netmask 255.255.255.0 gateway 192.168.2.1 hwaddress aa:bb:cc:dd:ee:02 

gateway是将从中加载映像的主机地址, address是板的地址。


之后,收集引导加载程序:


  make 

2:板上正常进行引导加载程序加载(对不起,双关语)-这里没有具体说明,您需要像对STM32F7Discovery的任何其他应用程序那样执行此操作。 如果您不知道该怎么做,可以在此处阅读。
3:使用OpenCV的配置编译映像。


  make confload-platform/opencv/stm32f7discovery make 

4:在qspi.bin中从需要写入QSPI的ELF节中提取


  arm-none-eabi-objcopy -O binary build/base/bin/embox build/base/bin/qspi.bin \ --only-section=.text --only-section=.rodata \ --only-section='.ARM.ex*' \ --only-section=.data 

conf目录包含执行此操作的脚本,因此您可以运行它


  ./conf/qspi_objcopy.sh #   -- build/base/bin/qspi.bin 

5:使用tftp将qspi.bin.bin加载到QSPI闪存驱动器上。 为此,请在主机上将qspi.bin复制到tftp服务器的根文件夹(通常是/ srv / tftp /或/ var / lib / tftpboot /;对应服务器的软件包在大多数流行的发行版中,通常称为tftpd或tftp-hpa,有时您需要使systemctl start tftpd.service才能启动)。


  #   tftpd sudo cp build/base/bin/qspi.bin /srv/tftp #   tftp-hpa sudo cp build/base/bin/qspi.bin /var/lib/tftpboot 

在Embox上(即在引导加载程序中),您需要运行以下命令(我们假设服务器的地址为192.168.2.1):


  embox> qspi_loader qspi.bin 192.168.2.1 

6:使用goto命令,您需要“跳转”到QSPI存储器中。 具体位置取决于图像的链接方式,您可以看到此地址为mem 0x90000000 (起始地址适合第二个32位图像字); 您还需要使用-s标志设置堆栈,堆栈地址为0x90000000,例如:


  embox>mem 0x90000000 0x90000000: 0x20023200 0x9000c27f 0x9000c275 0x9000c275 ↑ ↑        embox>goto -i 0x9000c27f -s 0x20023200 #  -i         <      ,    OpenCV > 

7:跑


  embox> edges 20 

并享受40秒的边界搜索:)


如果出现问题,请在我们的存储库或邮件列表embox-devel@googlegroups.com或此处的注释中写问题。

Source: https://habr.com/ru/post/zh-CN457724/


All Articles