OpenCV auf STM32F7-Discovery

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

Wenn Sie in einer Suchmaschine etwas wie „OpenCV auf STM32-Board“ eingeben, finden Sie etliche Leute, die daran interessiert sind, diese Bibliothek auf STM32-Boards oder anderen Mikrocontrollern zu verwenden.
Es gibt mehrere Videos, die, dem Namen nach zu urteilen, zeigen sollen, was benötigt wird, aber normalerweise (in allen Videos, die ich gesehen habe) wurde auf der STM32-Karte nur das Bild von der Kamera empfangen und das Ergebnis auf dem Bildschirm angezeigt. und die Bildverarbeitung selbst erfolgte entweder auf einem normalen Computer oder auf leistungsstärkeren Platinen (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, Desktop-fähigen Code auf einem Mikrocontroller auszuführen, den Entwicklungsprozess erheblich vereinfacht. Doch warum gibt es noch immer keine gängigen Fertigrezepte zur Lösung dieses Problems?

Das Problem bei der Verwendung von OpenCV auf kleinen Schals hängt mit zwei Merkmalen zusammen:

  • Wenn Sie die Bibliothek selbst mit einem minimalen Satz an Modulen kompilieren, passt sie aufgrund eines 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, d.h
    • Benötigen Sie Unterstützung für positive Laufzeit (Ausnahmen usw.)
    • Geringe Unterstützung für LibC/Posix, das normalerweise in Betriebssystemen für eingebettete Systeme zu finden ist – Sie benötigen eine Standard-Plus-Bibliothek und eine Standard-STL-Vorlagenbibliothek (Vektor usw.)

Portierung nach Embox

Bevor Sie ein Programm auf das Betriebssystem portieren, sollten Sie wie üblich versuchen, es in der von den Entwicklern vorgesehenen Form zu erstellen. In unserem Fall gibt es damit keine Probleme – den Quellcode finden Sie unter githabe, die Bibliothek wird unter GNU/Linux mit dem üblichen cmake erstellt.

Die gute Nachricht ist, dass OpenCV sofort als statische Bibliothek erstellt werden kann, was die Portierung erleichtert. Wir sammeln eine Bibliothek mit einer Standardkonfiguration und sehen, wie viel Platz sie beanspruchen. Jedes Modul ist in einer separaten Bibliothek gesammelt.

> 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 der letzten Zeile entnehmen können, beanspruchen .bss und .data nicht viel Platz, aber der Code ist mehr als 70 MiB groß. Es ist klar, dass der Code weniger wird, wenn dies statisch an eine bestimmte Anwendung gebunden ist.

Versuchen wir, so viele Module wie möglich wegzuwerfen, damit ein minimales Beispiel entsteht (das beispielsweise einfach die OpenCV-Version ausgibt), also schauen wir cmake .. -LA und schalten Sie in den Optionen alles aus, was sich ausschaltet.

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

Einerseits ist dies nur ein Modul der Bibliothek, andererseits ist dies ohne Compiler-Optimierung für die Codegröße (-Os). ~3 MiB Code sind immer noch ziemlich viel, lassen aber bereits auf Erfolg hoffen.

Führen Sie den Emulator aus

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

Embox verfügt über einen Mechanismus zum Erstellen externer Bibliotheken. Mit diesem fügen wir OpenCV als Modul hinzu (übergeben alle gleichen Optionen für den „minimalen“ Build in Form statischer Bibliotheken). Anschließend füge ich eine einfache 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 Ausgabe.

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, vorzugsweise eines der Standardbeispiele, die von den Entwicklern selbst angeboten werden. auf Ihrer Website. Ich entschied mich Grenzdetektor schlau.

Das Beispiel musste leicht umgeschrieben werden, um das Bild mit dem Ergebnis direkt im Framebuffer anzuzeigen. Ich musste das tun, weil. Funktion imshow() kann Bilder über die QT-, GTK- und Windows-Schnittstellen zeichnen, was natürlich definitiv nicht in der Konfiguration für STM32 enthalten sein wird. Tatsächlich kann QT auch auf STM32F7Discovery ausgeführt werden, dies wird jedoch in einem anderen Artikel besprochen 🙂

Nach einer kurzen Klärung, in welchem ​​Format das Ergebnis des Kantendetektors gespeichert wird, erhalten wir ein Bild.

OpenCV auf STM32F7-Discovery

Originalbild

OpenCV auf STM32F7-Discovery

Erlebe die Kraft effektiver Ergebnisse

Läuft auf STM32F7Discovery

Auf 32F746GDISCOVERY gibt es mehrere Hardware-Speicherabschnitte, die wir auf die eine oder andere Weise nutzen können

  1. 320 KB RAM
  2. 1 MB Flash für das Bild
  3. 8 MB SDRAM
  4. 16 MB QSPI NAND-Flash
  5. microSD-Kartensteckplatz

Zum Speichern von Bildern kann eine SD-Karte verwendet werden, im Kontext der Ausführung eines Minimalbeispiels ist dies jedoch nicht sehr nützlich.
Das Display hat eine Auflösung von 480×272, was bedeutet, dass der Framebuffer-Speicher 522 Byte bei einer Tiefe von 240 Bit beträgt, also Dies ist mehr als die Größe des RAM, daher befinden sich der Framebuffer und der Heap (der, auch für OpenCV, zum Speichern von Daten für Bilder und Hilfsstrukturen benötigt wird) im SDRAM, alles andere (Speicher für Stacks und andere Systemanforderungen). ) wird in den RAM verschoben.

Wenn wir die Mindestkonfiguration für STM32F7Discovery nehmen (das gesamte Netzwerk, alle Befehle wegwerfen, Stacks so klein wie möglich machen usw.) und dort OpenCV mit Beispielen hinzufügen, ist der erforderliche Speicher wie folgt:

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

Für diejenigen, die nicht genau wissen, welche Abschnitte wohin gehören, erkläre ich: in .text и .rodata Anweisungen und Konstanten (grob gesagt schreibgeschützte Daten) liegen darin .data die Daten sind veränderlich, .bss Es gibt „nullierte“ Variablen, die dennoch einen Platz benötigen (dieser Abschnitt „geht“ in den RAM).

Die gute Nachricht ist, dass .data/.bss sollte passen, aber mit .text Das Problem ist, dass nur 1 MB Speicher für das Bild vorhanden ist. Kann weggeworfen werden .text Laden Sie das Bild aus dem Beispiel herunter und lesen Sie es beispielsweise beim Start von der SD-Karte in den Speicher, aber Fruits.png wiegt etwa 330 KiB, sodass dies das Problem nicht lösen wird: die meisten .text besteht aus dem OpenCV-Code.

Im Großen und Ganzen bleibt nur noch eines: einen Teil des Codes auf einen QSPI-Flash zu laden (er verfügt über einen speziellen Betriebsmodus zum Zuordnen von Speicher zum Systembus, sodass der Prozessor direkt auf diese Daten zugreifen kann). In diesem Fall entsteht ein Problem: Erstens ist der Speicher eines QSPI-Flash-Laufwerks nicht sofort nach dem Neustart des Geräts verfügbar (Sie müssen den Memory-Mapping-Modus separat initialisieren), und zweitens können Sie diesen Speicher nicht mit „flashen“. ein bekannter Bootloader.

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

Erlebe die Kraft effektiver Ergebnisse

Die Idee, diese Bibliothek auf Embox zu portieren, entstand vor etwa einem Jahr, wurde jedoch aus verschiedenen Gründen immer wieder verschoben. Eine davon ist die Unterstützung von libstdc++ und der Standardvorlagenbibliothek. Das Problem der C++-Unterstützung in Embox geht über den Rahmen dieses Artikels hinaus, daher möchte ich hier nur sagen, dass es uns gelungen ist, diese Unterstützung im richtigen Umfang zu erreichen, damit diese Bibliothek funktioniert 🙂

Am Ende wurden diese Probleme überwunden (zumindest so weit, dass das OpenCV-Beispiel funktionierte) und das Beispiel lief. Es dauert 40 lange Sekunden, bis das Board mithilfe des Canny-Filters nach Grenzen sucht. Das ist natürlich zu lang (es gibt Überlegungen, wie man diese Angelegenheit optimieren kann, im Erfolgsfall wird es möglich sein, einen separaten Artikel darüber zu schreiben).

OpenCV auf STM32F7-Discovery

Das Zwischenziel bestand jedoch darin, einen Prototyp zu erstellen, der die grundsätzliche Möglichkeit der Ausführung von OpenCV auf STM32 aufzeigt bzw. dieses Ziel wurde erreicht, Hurra!

tl;dr: Schritt-für-Schritt-Anleitung

0: Laden Sie Embox-Quellen wie folgt herunter:

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

1: Beginnen wir mit der Zusammenstellung eines Bootloaders, der ein QSPI-Flash-Laufwerk „flasht“.

    make confload-arm/stm32f7cube

Jetzt müssen Sie das Netzwerk konfigurieren, denn. Wir werden das Bild über TFTP hochladen. Um die Board- und Host-IP-Adressen festzulegen, müssen Sie conf/rootfs/network bearbeiten.

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 - Hostadresse, von der das Bild geladen wird, address - Adresse des Vorstandes.

Danach sammeln wir den Bootloader:

    make

2: Das übliche Laden des Bootloaders (sorry für das Wortspiel) auf die Platine – hier gibt es nichts Spezifisches, Sie müssen es wie bei jeder anderen Anwendung für STM32F7Discovery tun. Wenn Sie nicht wissen, wie es geht, können Sie es nachlesen hier.
3: Kompilieren eines Bildes mit einer Konfiguration für OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: Auszug aus ELF-Abschnitten, die in QSPI in qspi.bin geschrieben werden sollen

    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

Im conf-Verzeichnis gibt es ein Skript, das dies erledigt, sodass Sie es ausführen können

    ./conf/qspi_objcopy.sh # Нужный бинарник -- build/base/bin/qspi.bin

5: Laden Sie qspi.bin.bin über TFTP auf ein QSPI-Flash-Laufwerk herunter. Kopieren Sie dazu auf dem Host qspi.bin in den Stammordner des TFTP-Servers (normalerweise /srv/tftp/ oder /var/lib/tftpboot/; Pakete für den entsprechenden Server sind in den gängigsten Distributionen verfügbar, meist genannt tftpd oder tftp-hpa, manchmal muss man es tun systemctl start tftpd.service anfangen).

    # вариант для tftpd
    sudo cp build/base/bin/qspi.bin /srv/tftp
    # вариант для tftp-hpa
    sudo cp build/base/bin/qspi.bin /var/lib/tftpboot

Auf Embox (also im Bootloader) müssen Sie 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 Befehl goto Sie müssen in den QSPI-Speicher „springen“. Der genaue Speicherort hängt davon ab, wie das Bild verlinkt ist. Sie können diese Adresse mit dem Befehl anzeigen mem 0x90000000 (die Startadresse passt in das zweite 32-Bit-Wort des Bildes); Sie müssen auch den Stapel kennzeichnen -s, die Stack-Adresse liegt bei 0x90000000, Beispiel:

    embox>mem 0x90000000
    0x90000000:     0x20023200  0x9000c27f  0x9000c275  0x9000c275
                      ↑           ↑
              это адрес    это  адрес 
                стэка        первой
                           инструкции

    embox>goto -i 0x9000c27f -s 0x20023200 # Флаг -i нужен чтобы запретить прерывания во время инициализации системы

    < Начиная отсюда будет вывод не загрузчика, а образа с OpenCV >

7: Starten

    embox> edges 20

und genieße die 40-sekündige Grenzsuche 🙂

Wenn etwas schief geht, schreiben Sie ein Problem Unser Repository, oder an die Mailingliste [E-Mail geschützt] , oder in einem Kommentar hier.

Source: habr.com

Kommentar hinzufügen