OpenCV en STM32F7-Discovery

Soy uno de los desarrolladores del sistema operativo Embox , y en este artículo hablaré sobre cómo logré ejecutar OpenCV en la placa STM32746G.


Si conduce a un motor de búsqueda algo así como "OpenCV en la placa STM32", puede encontrar bastantes interesados ​​en usar esta biblioteca en placas STM32 u otros microcontroladores.
Hay varios videos que, a juzgar por el nombre, deben demostrar lo que se necesita, pero generalmente (en todos los videos que vi) en la placa STM32, solo la imagen se recibió de la cámara y el resultado se mostró en la pantalla, y el procesamiento de la imagen en sí se realizó en una computadora normal o en placas más potentes (por ejemplo, Raspberry Pi).


¿Por qué es dificil?


La popularidad de las consultas de búsqueda se explica por el hecho de que OpenCV es la biblioteca de visión por computadora más popular, lo que significa que más desarrolladores están familiarizados con ella, y la capacidad de ejecutar código listo para el escritorio en el microcontrolador simplifica enormemente el proceso de desarrollo. Pero, ¿por qué todavía no hay recetas populares preparadas para resolver este problema?


El problema de usar OpenCV en placas pequeñas está asociado con dos características:


  • Si compila la biblioteca incluso con un conjunto mínimo de módulos, simplemente no cabe en la memoria flash del mismo STM32F7Discovery (incluso sin tener en cuenta el sistema operativo) debido al código muy grande (varios megabytes de instrucciones)
  • La biblioteca en sí está escrita en C ++, lo que significa
    • Necesita soporte para un tiempo de ejecución positivo (excepciones, etc.)
    • Hay poco soporte para LibC / Posix, que generalmente se encuentra en el sistema operativo para sistemas integrados: necesita una biblioteca estándar de ventajas y una biblioteca estándar de plantillas STL (vector, etc.)

Portar a Embox


Como de costumbre, antes de portar cualquier programa al sistema operativo, es una buena idea intentar ensamblarlo en la forma en que los desarrolladores lo pretenden. En nuestro caso, no hay problemas con esto: las fuentes se pueden encontrar en el github , la biblioteca está construida bajo GNU / Linux con el cmake habitual.


De las buenas noticias: OpenCV listo para usar se puede ensamblar como una biblioteca estática, lo que facilita la transferencia. Recopilamos la biblioteca con la configuración estándar y vemos cuánto espacio ocupan. Cada módulo se ensambla en una biblioteca separada.


> 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) 

Como puede ver en la última línea, .bss y .data no ocupan mucho espacio, pero el código tiene más de 70 MiB. Está claro que si esto está vinculado estáticamente con una aplicación específica, el código se hará más pequeño.


Intentemos lanzar tantos módulos como sea posible para que se ensamble un ejemplo mínimo (que, por ejemplo, solo muestra la versión de OpenCV), así que mire cmake .. -LA y desactive todo lo que esté deshabilitado en las opciones.


  -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) 

Por un lado, este es solo un módulo de biblioteca, por otro lado, no está optimizado por el compilador en términos de tamaño de código ( -Os ). ~ 3 MiB de código todavía es bastante, pero ya da esperanzas de éxito.


Ejecutar en emulador


La depuración en el emulador es mucho más fácil, así que primero asegúrese de que la biblioteca se ejecute en qemu. Como plataforma emulada, elegí Integrator / CP, porque en primer lugar, también es ARM y, en segundo lugar, Embox admite la salida de gráficos para esta plataforma.


Embox tiene un mecanismo para construir bibliotecas externas, usándolo agregamos OpenCV como módulo (pasando todas las mismas opciones para la compilación "mínima" que las bibliotecas estáticas), luego agrego la aplicación más simple que se ve así:


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

Ensamblamos el sistema, lo ejecutamos, obtenemos la conclusión esperada.


 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     ..> 

El siguiente paso es ejecutar algún ejemplo, lo mejor de todo es un estándar de los que los propios desarrolladores ofrecen en su sitio web . Elegí el detector de bordes de Canny .


El ejemplo tuvo que reescribirse un poco para mostrar la imagen con el resultado directamente en el búfer de cuadros. Tuve que hacer esto porque La función imshow() es capaz de dibujar imágenes a través de las interfaces QT, GTK y Windows, que, por supuesto, definitivamente no estarán en la configuración STM32. De hecho, QT también se puede ejecutar en STM32F7Discovery, pero esto se discutirá en otro artículo :)


Después de una breve aclaración en qué formato se almacena el resultado del detector de bordes, obtenemos una imagen.



Imagen original



Resultado


Ejecutando en STM32F7Discovery


Hay varias particiones de hardware en el 32F746GDISCOVERY que podemos usar de todos modos


  1. 320 KB de RAM
  2. Flash de 1MiB para imagen
  3. SDRAM de 8MiB
  4. Unidad flash QSPI NAND de 16MiB
  5. Ranura para tarjeta microSD

Se puede usar una tarjeta SD para almacenar imágenes, pero en el contexto de ejecutar un ejemplo mínimo, esto no es muy útil.
La pantalla tiene una resolución de 480x272, lo que significa que la memoria para el framebuffer será de 522,240 bytes a una profundidad de 32 bits, es decir. esto es más que el tamaño de la RAM, por lo que colocaremos el framebuffer y un grupo (que será necesario para que OpenCV almacene datos para imágenes y estructuras auxiliares) en SDRAM, todo lo demás (memoria para pilas y otras necesidades del sistema) irá a RAM .


Si tomamos la configuración mínima para STM32F7Discovery (descarte toda la red, todos los comandos, haga que las pilas sean lo más pequeñas posible, etc.) y agreguemos OpenCV con ejemplos allí, con la memoria requerida, lo siguiente será:


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

Para aquellos que no están muy familiarizados con las secciones que se están doblando, explicaré: las instrucciones y las constantes (aproximadamente, datos de solo lectura) están en .text y .rodata , los datos son mutables en .data y "cero" en .bss variables que, sin embargo, necesitan un lugar (esta sección "irá" a la RAM).


La buena noticia es que .data / .bss debería encajar, pero con .text problema es que solo hay 1MiB de memoria para la imagen. Puede .text imagen del ejemplo del .text y leerla, por ejemplo, de la tarjeta SD en la memoria al inicio, pero fruits.png pesa unos 330 KB, por lo que esto no resolverá el problema: la mayor parte del .text consiste en código OpenCV.


En general, solo queda una cosa: cargar parte del código en una unidad flash QSPI (tiene un modo operativo especial para asignar memoria al bus del sistema, de modo que el procesador pueda acceder a estos datos directamente). En este caso, surge un problema: en primer lugar, la memoria de la unidad flash QSPI no está disponible inmediatamente después de reiniciar el dispositivo (debe inicializar por separado el modo de asignación de memoria) y, en segundo lugar, no puede actualizar esta memoria con el gestor de arranque habitual.


Como resultado, se decidió vincular todo el código en QSPI y actualizarlo con un gestor de arranque, que recibirá el binario necesario a través de TFTP.


Resultado


La idea de portar esta biblioteca a Embox surgió hace un año, pero una y otra vez se retrasó debido a varias razones. Uno de ellos es el soporte para libstdc ++ y la biblioteca de plantillas estándar. El problema de admitir C ++ en Embox está más allá del alcance de este artículo, así que aquí solo diré que logramos lograr este soporte en la cantidad adecuada para que esta biblioteca funcione :)


Al final, se superaron estos problemas (al menos lo suficiente como para que funcione el ejemplo de OpenCV), y comenzó el ejemplo. 40 segundos largos toman el tablero para buscar límites por el filtro Canny. Esto, por supuesto, es demasiado largo (hay consideraciones sobre cómo optimizar este asunto, será posible escribir un artículo por separado si tiene éxito).




Sin embargo, el objetivo intermedio era crear un prototipo que mostrara la posibilidad fundamental de ejecutar OpenCV en STM32, respectivamente, este objetivo se logró, ¡salud!

tl; dr: instrucciones paso a paso


0: descargue las fuentes de Embox, por ejemplo así:


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

1: Comencemos construyendo un gestor de arranque que "flasheará" la unidad flash QSPI.


  make confload-arm/stm32f7cube 

Ahora necesita configurar la red, porque Subiremos la imagen a través de TFTP. Para configurar las direcciones IP de la placa y el host, debe modificar el archivo conf / rootfs / network.


Ejemplo de configuración:


 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 es la dirección del host desde donde se cargará la imagen, la address es la dirección de la placa.


Después de eso, recoge el gestor de arranque:


  make 

2: Carga normal del cargador de arranque (perdón por el juego de palabras) en la placa: no hay nada específico aquí, debe hacer esto como para cualquier otra aplicación para STM32F7Discovery. Si no sabe cómo hacer esto, puede leer sobre esto aquí .
3: Compilar la imagen con la configuración para OpenCV.


  make confload-platform/opencv/stm32f7discovery make 

4: Extracción de secciones ELF que deben escribirse en QSPI, en qspi.bin


  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 

El directorio conf contiene un script que hace esto, por lo que puede ejecutarlo


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

5: Utilizando tftp, cargue qspi.bin.bin en una unidad flash QSPI. Para hacer esto, en el host, copie qspi.bin a la carpeta raíz del servidor tftp (generalmente es / srv / tftp / o / var / lib / tftpboot /; los paquetes para el servidor correspondiente se encuentran en las distribuciones más populares, generalmente llamadas tftpd o tftp-hpa, a veces necesita hacer que systemctl start tftpd.service para iniciar).


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

En Embox (es decir, en el gestor de arranque), debe ejecutar el siguiente comando (suponemos que el servidor tiene la dirección 192.168.2.1):


  embox> qspi_loader qspi.bin 192.168.2.1 

6: Usando el comando goto , debe "saltar" a la memoria QSPI. La ubicación específica variará dependiendo de cómo esté vinculada la imagen, puede ver esta dirección con mem 0x90000000 (la dirección de inicio se ajusta a la segunda palabra de imagen de 32 bits); también necesita configurar la pila con el indicador -s , la dirección de la pila está en 0x90000000, por ejemplo:


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

7: correr


  embox> edges 20 

y disfruta de una búsqueda de borde de 40 segundos :)


Si algo sale mal, escriba el problema en nuestro repositorio , o en la lista de correo embox-devel@googlegroups.com, o en los comentarios aquí.

Source: https://habr.com/ru/post/457724/


All Articles