OpenCV auf STM32F7-Discovery

Ich bin einer der Entwickler des Embox- Betriebssystems und werde in diesem Artikel darüber sprechen, wie ich es geschafft habe, OpenCV auf der STM32746G-Karte auszuführen.


Wenn Sie in eine Suchmaschine wie "OpenCV auf STM32-Karte" fahren, finden Sie einige, die daran interessiert sind, diese Bibliothek auf STM32-Karten oder anderen Mikrocontrollern zu verwenden.
Es gibt mehrere Videos, die nach dem Namen zeigen sollten, was benötigt wird, aber normalerweise (in allen Videos, die ich gesehen habe) auf der STM32-Platine wurde nur das Bild von der Kamera empfangen und das Ergebnis auf dem Bildschirm angezeigt, und die Bildverarbeitung wurde entweder am ausgeführt auf einem normalen Computer oder auf leistungsstärkeren Boards (z. B. Raspberry Pi).


Warum ist es schwierig?


Die Beliebtheit von Suchanfragen erklärt sich aus der Tatsache, dass OpenCV die beliebteste Computer-Vision-Bibliothek ist, was bedeutet, dass mehr Entwickler damit vertraut sind und die Möglichkeit, Code für den Desktop auf dem Mikrocontroller auszuführen, den Entwicklungsprozess erheblich vereinfacht. Aber warum gibt es immer noch keine beliebten vorgefertigten Rezepte zur Lösung dieses Problems?


Das Problem der Verwendung von OpenCV auf kleinen Boards hängt mit zwei Funktionen zusammen:


  • Wenn Sie die Bibliothek auch mit einem minimalen Satz von Modulen kompilieren, passt sie aufgrund des sehr großen Codes (mehrere Megabyte an Anweisungen) einfach nicht in den Flash-Speicher desselben STM32F7Discovery (auch ohne Berücksichtigung des Betriebssystems).
  • Die Bibliothek selbst ist in C ++ geschrieben, was bedeutet
    • Benötigen Sie Unterstützung für eine positive Laufzeit (Ausnahmen usw.)
    • Es gibt wenig Unterstützung für LibC / Posix, das normalerweise im Betriebssystem für eingebettete Systeme zu finden ist. Sie benötigen eine Standardbibliothek mit Pluspunkten und eine Standardbibliothek mit STL-Vorlagen (Vektor usw.).

Portierung auf Embox


Wie üblich sollten Sie vor dem Portieren von Programmen auf das Betriebssystem versuchen, diese in der von den Entwicklern beabsichtigten Form zusammenzustellen. In unserem Fall gibt es keine Probleme damit - die Quellen befinden sich auf dem Github , die Bibliothek wird unter GNU / Linux mit dem üblichen cmake erstellt.


Aus den guten Nachrichten: OpenCV kann sofort als statische Bibliothek zusammengestellt werden, was die Portierung erleichtert. Wir sammeln die Bibliothek mit der Standardkonfiguration und sehen, wie viel Speicherplatz sie benötigen. Jedes Modul wird in einer separaten Bibliothek zusammengestellt.


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

Wie Sie in der letzten Zeile sehen können, nehmen .bss und .data nicht viel Platz ein, aber der Code ist mehr als 70 MiB groß. Es ist klar, dass der Code kleiner wird, wenn dies statisch mit einer bestimmten Anwendung verknüpft ist.


Versuchen wir, so viele Module wie möglich wegzuwerfen, damit ein minimales Beispiel erstellt wird (das beispielsweise nur die Version von OpenCV anzeigt). Schauen Sie sich also cmake .. -LA und deaktivieren Sie alles, was in den Optionen deaktiviert ist.


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

Dies ist einerseits nur ein Bibliotheksmodul, andererseits ist es ohne Optimierung durch den Compiler hinsichtlich der Codegröße ( -Os ). ~ 3 MiB Code sind immer noch ziemlich viel, aber es gibt bereits Hoffnung auf Erfolg.


Im Emulator ausführen


Das Debuggen auf dem Emulator ist viel einfacher. Stellen Sie daher zunächst sicher, dass die Bibliothek auf qemu ausgeführt wird. Als emulierte Plattform habe ich Integrator / CP gewählt, weil Erstens ist es auch ARM, und zweitens unterstützt Embox die Grafikausgabe für diese Plattform.


Embox hat einen Mechanismus zum Erstellen externer Bibliotheken. Mit diesem Mechanismus fügen wir OpenCV als Modul hinzu (wobei alle Optionen für den "minimalen" Build als statische Bibliotheken übergeben werden). Danach füge ich die einfachste Anwendung hinzu, die so aussieht:


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

Wir bauen das System zusammen, führen es aus - wir erhalten die erwartete Schlussfolgerung.


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

Der nächste Schritt besteht darin, ein Beispiel auszuführen, am besten einen Standard derjenigen, die die Entwickler selbst auf ihrer Website anbieten. Ich habe mich für Cannys Grenzdetektor entschieden.


Das Beispiel musste etwas umgeschrieben werden, um das Bild mit dem Ergebnis direkt im Bildspeicher anzuzeigen. Ich musste das tun, weil Die Funktion imshow() kann Bilder über die Schnittstellen QT, GTK und Windows zeichnen, die natürlich nicht in der Konfiguration für STM32 enthalten sind. Tatsächlich kann QT auch auf STM32F7Discovery ausgeführt werden, dies wird jedoch in einem anderen Artikel erläutert :)


Nach einer kurzen Klarstellung, in welchem ​​Format das Ergebnis des Randdetektors gespeichert ist, erhalten wir ein Bild.



Originalbild



Ergebnis


Läuft auf STM32F7Discovery


Auf dem 32F746GDISCOVERY gibt es mehrere Hardware-Partitionen, die wir sowieso verwenden können


  1. 320 KB RAM
  2. 1MiB Blitz für Bild
  3. 8 MB SDRAM
  4. 16 MB QSPI NAND-Flash-Laufwerk
  5. MicroSD-Kartensteckplatz

Eine SD-Karte kann zum Speichern von Bildern verwendet werden. Im Zusammenhang mit der Ausführung eines minimalen Beispiels ist dies jedoch nicht sehr nützlich.
Die Anzeige hat eine Auflösung von 480 x 272, was bedeutet, dass der Speicher für den Bildpuffer 522.240 Bytes in einer Tiefe von 32 Bit beträgt, d. H. Dies ist mehr als die Größe des Arbeitsspeichers, daher werden wir den Framebuffer und eine Reihe (die für OpenCV zum Speichern von Daten für Bilder und Hilfsstrukturen benötigt werden) im SDRAM ablegen. Alles andere (Speicher für Stapel und andere Systemanforderungen) wird in den Arbeitsspeicher verschoben .


Wenn wir die minimale Konfiguration für STM32F7Discovery verwenden (das gesamte Netzwerk, alle Befehle wegwerfen, die Stapel so klein wie möglich machen usw.) und OpenCV mit Beispielen dort mit dem erforderlichen Speicher hinzufügen, ist Folgendes:


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

Für diejenigen, die nicht .text .rodata , welche Abschnitte gefaltet werden, erkläre ich: Anweisungen und Konstanten (ungefähr schreibgeschützte Daten) sind in .text und .rodata , Daten sind in .data veränderbar und "Null" ist in .bss Variablen, die jedoch einen Platz benötigen (dieser Abschnitt "geht" in den Arbeitsspeicher).


Die gute Nachricht ist, dass .data / .bss passen sollte, aber mit .text darin, dass nur 1 MB Speicher für das Bild vorhanden sind. Sie können .text Bild aus dem Beispiel aus dem .text und es beispielsweise beim Start von der SD-Karte in den Speicher lesen. Obst.png wiegt jedoch etwa 330 KB, sodass das Problem nicht gelöst wird: Der größte Teil des .text besteht aus OpenCV-Code.


Im Großen und Ganzen bleibt nur noch eines übrig: Laden eines Teils des Codes auf ein QSPI-Flash-Laufwerk (es verfügt über einen speziellen Betriebsmodus zum Zuordnen von Speicher zum Systembus, sodass der Prozessor direkt auf diese Daten zugreifen kann). In diesem Fall tritt ein Problem auf: Erstens ist der Speicher eines QSPI-Flash-Laufwerks nicht unmittelbar nach dem Neustart des Geräts verfügbar (Sie müssen den Speicherzuordnungsmodus separat initialisieren), und zweitens können Sie diesen Speicher nicht mit dem üblichen Bootloader flashen.


Aus diesem Grund wurde beschlossen, den gesamten Code in QSPI zu verknüpfen und mit einem Bootloader zu flashen, der die erforderliche Binärdatei über TFTP empfängt.


Ergebnis


Die Idee, diese Bibliothek auf Embox zu portieren, kam vor etwa einem Jahr, wurde jedoch aus verschiedenen Gründen immer wieder verschoben. Eine davon ist die Unterstützung für libstdc ++ und die Standardvorlagenbibliothek. Das Problem der Unterstützung von C ++ in Embox geht über den Rahmen dieses Artikels hinaus. Daher möchte ich hier nur sagen, dass wir es geschafft haben, diese Unterstützung in der richtigen Menge zu erreichen, damit diese Bibliothek funktioniert :)


Am Ende wurden diese Probleme überwunden (zumindest genug, damit das OpenCV-Beispiel funktioniert), und das Beispiel wurde gestartet. Nach 40 langen Sekunden sucht das Board mit dem Canny-Filter nach Grenzen. Dies ist natürlich zu lang (es gibt Überlegungen zur Optimierung dieser Angelegenheit, bei Erfolg kann ein separater Artikel darüber geschrieben werden).




Trotzdem war das Zwischenziel, einen Prototyp zu erstellen, der die grundlegende Möglichkeit zeigt, OpenCV auf STM32 auszuführen. Dieses Ziel wurde erreicht, Prost!

tl; dr: Schritt für Schritt Anleitung


0: Laden Sie die Embox-Quellen herunter, zum Beispiel wie folgt:


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

1: Beginnen wir mit dem Erstellen eines Bootloaders, der das QSPI-Flash-Laufwerk „flasht“.


  make confload-arm/stm32f7cube 

Jetzt müssen Sie das Netzwerk konfigurieren, weil Wir werden das Bild über TFTP hochladen. Um die IP-Adressen der Karte und des Hosts festzulegen, müssen Sie die Datei conf / rootfs / network ändern.


Konfigurationsbeispiel:


 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 ist die Hostadresse, von der das Image geladen wird, address ist die Adresse der Karte.


Sammeln Sie danach den Bootloader:


  make 

2: Normales Laden des Bootloaders (Entschuldigung für das Wortspiel) auf dem Board - hier gibt es nichts Spezielles. Sie müssen dies wie bei jeder anderen Anwendung für STM32F7Discovery tun. Wenn Sie nicht wissen, wie das geht, können Sie hier darüber lesen.
3: Kompilieren des Images mit der Konfiguration für OpenCV.


  make confload-platform/opencv/stm32f7discovery make 

4: Extrahieren aus ELF-Abschnitten, die in QSPI geschrieben werden müssen, in 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 

Das Verzeichnis conf enthält ein Skript, das dies ausführt, damit Sie es ausführen können


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

5: Laden Sie mit tftp qspi.bin.bin auf ein QSPI-Flash-Laufwerk. Kopieren Sie dazu auf dem Host qspi.bin in den Stammordner des TFTP-Servers (normalerweise sind es / srv / tftp / oder / var / lib / tftpboot /; Pakete für den entsprechenden Server sind in den gängigsten Distributionen enthalten, die normalerweise als tftpd oder tftp-hpa bezeichnet werden. manchmal muss systemctl start tftpd.service zu starten).


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

In Embox (d. H. Im Bootloader) müssen Sie den folgenden Befehl ausführen (wir gehen davon aus, dass der Server die Adresse 192.168.2.1 hat):


  embox> qspi_loader qspi.bin 192.168.2.1 

6: Mit dem Befehl goto müssen Sie in den QSPI-Speicher "springen". Der spezifische Speicherort hängt davon ab, wie das Bild verknüpft ist. Sie können diese Adresse mit mem 0x90000000 (die Startadresse passt in das zweite 32-Bit- mem 0x90000000 ). Sie müssen den Stapel auch mit dem Flag -s setzen. Die Stapeladresse lautet 0x90000000, zum Beispiel:


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

7: Laufen


  embox> edges 20 

und genieße eine 40 Sekunden lange Grenzsuche :)


Wenn etwas schief geht, schreiben Sie ein Problem in unser Repository oder in die Mailingliste embox-devel@googlegroups.com oder in die Kommentare hier.

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


All Articles