OpenCV στο STM32F7-Discovery

OpenCV στο STM32F7-Discovery Είμαι ένας από τους προγραμματιστές του λειτουργικού συστήματος Embox, και σε αυτό το άρθρο θα μιλήσω για το πώς κατάφερα να τρέξω το OpenCV στην πλακέτα STM32746G.

Εάν πληκτρολογήσετε κάτι σαν "OpenCV σε πλακέτα STM32" σε μια μηχανή αναζήτησης, μπορείτε να βρείτε αρκετά άτομα που ενδιαφέρονται να χρησιμοποιήσουν αυτήν τη βιβλιοθήκη σε πλακέτες STM32 ή άλλους μικροελεγκτές.
Υπάρχουν πολλά βίντεο που, αν κρίνουμε από το όνομα, πρέπει να δείχνουν τι χρειάζεται, αλλά συνήθως (σε όλα τα βίντεο που είδα) στον πίνακα STM32, λαμβανόταν μόνο η εικόνα από την κάμερα και το αποτέλεσμα εμφανιζόταν στην οθόνη, και η ίδια η επεξεργασία εικόνας έγινε είτε σε κανονικό υπολογιστή, είτε σε πιο ισχυρούς πίνακες (για παράδειγμα, Raspberry Pi).

Γιατί είναι δύσκολο;

Η δημοτικότητα των ερωτημάτων αναζήτησης εξηγείται από το γεγονός ότι το OpenCV είναι η πιο δημοφιλής βιβλιοθήκη υπολογιστικής όρασης, πράγμα που σημαίνει ότι περισσότεροι προγραμματιστές είναι εξοικειωμένοι με αυτήν και η δυνατότητα εκτέλεσης κώδικα έτοιμου για επιτραπέζιο υπολογιστή σε μικροελεγκτή απλοποιεί σημαντικά τη διαδικασία ανάπτυξης. Αλλά γιατί δεν υπάρχουν ακόμα δημοφιλείς έτοιμες συνταγές για την επίλυση αυτού του προβλήματος;

Το πρόβλημα της χρήσης OpenCV σε μικρά σάλια σχετίζεται με δύο χαρακτηριστικά:

  • Εάν μεταγλωττίσετε τη βιβλιοθήκη ακόμη και με ένα ελάχιστο σύνολο μονάδων, απλά δεν θα χωρέσει στη μνήμη flash του ίδιου STM32F7Discovery (ακόμη και χωρίς να λάβετε υπόψη το λειτουργικό σύστημα) λόγω ενός πολύ μεγάλου κωδικού (αρκετά megabyte οδηγιών)
  • Η ίδια η βιβλιοθήκη είναι γραμμένη σε C++, που σημαίνει
    • Χρειάζεστε υποστήριξη για θετικό χρόνο εκτέλεσης (εξαιρέσεις, κ.λπ.)
    • Μικρή υποστήριξη για το LibC/Posix, το οποίο βρίσκεται συνήθως στο λειτουργικό σύστημα για ενσωματωμένα συστήματα - χρειάζεστε μια τυπική βιβλιοθήκη συν και μια τυπική βιβλιοθήκη προτύπων 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 ως ενότητα (περνώντας όλες τις ίδιες επιλογές για το "ελάχιστο" build με τη μορφή στατικών βιβλιοθηκών), μετά από αυτό προσθέτω μια απλή εφαρμογή που μοιάζει με αυτό:

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 включены в сборку и т.п.>

Το επόμενο βήμα είναι να εκτελέσετε κάποιο παράδειγμα, κατά προτίμηση ένα από τα τυπικά που προσφέρουν οι ίδιοι οι προγραμματιστές. στον ιστότοπό σας. διάλεξα ανιχνευτής συνόρων.

Το παράδειγμα έπρεπε να ξαναγραφτεί ελαφρώς για να εμφανιστεί η εικόνα με το αποτέλεσμα απευθείας στο buffer του καρέ. Έπρεπε να το κάνω αυτό, γιατί. λειτουργία imshow() μπορεί να σχεδιάζει εικόνες μέσω των διασυνδέσεων QT, GTK και Windows, οι οποίες, φυσικά, σίγουρα δεν θα είναι στη διαμόρφωση για το STM32. Στην πραγματικότητα, το QT μπορεί επίσης να τρέξει στο STM32F7Discovery, αλλά αυτό θα συζητηθεί σε άλλο άρθρο 🙂

Μετά από μια σύντομη διευκρίνιση σε ποια μορφή αποθηκεύεται το αποτέλεσμα του ανιχνευτή ακμών, παίρνουμε μια εικόνα.

OpenCV στο STM32F7-Discovery

πρωτότυπη εικόνα

OpenCV στο STM32F7-Discovery

Αποτέλεσμα

Λειτουργεί σε STM32F7Discovery

Στο 32F746GDISCOVERY υπάρχουν αρκετές ενότητες μνήμης υλικού που μπορούμε να χρησιμοποιήσουμε με τον ένα ή τον άλλο τρόπο

  1. 320 KiB RAM
  2. Φλας 1 MiB για εικόνα
  3. 8 MiB SDRAM
  4. Flash 16 MiB QSPI NAND
  5. υποδοχή κάρτας microSD

Μια κάρτα SD μπορεί να χρησιμοποιηθεί για την αποθήκευση εικόνων, αλλά στο πλαίσιο της εκτέλεσης ενός ελάχιστου παραδείγματος, αυτό δεν είναι πολύ χρήσιμο.
Η οθόνη έχει ανάλυση 480×272, που σημαίνει ότι η μνήμη framebuffer θα είναι 522 byte σε βάθος 240 bit, δηλ. αυτό είναι περισσότερο από το μέγεθος της μνήμης RAM, επομένως το framebuffer και ο σωρός (που θα απαιτηθούν, συμπεριλαμβανομένου του 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 το πρόβλημα είναι ότι υπάρχει μόνο 1 MiB μνήμης για την εικόνα. Μπορεί να πεταχτεί έξω .text την εικόνα από το παράδειγμα και διαβάστε την, για παράδειγμα, από μια κάρτα SD στη μνήμη κατά την εκκίνηση, αλλά το fruits.png ζυγίζει περίπου 330 KiB, επομένως αυτό δεν θα λύσει το πρόβλημα: τα περισσότερα .text αποτελείται από τον κωδικό OpenCV.

Σε γενικές γραμμές, απομένει μόνο ένα πράγμα - η φόρτωση ενός μέρους του κώδικα σε ένα φλας QSPI (έχει έναν ειδικό τρόπο λειτουργίας για την αντιστοίχιση της μνήμης στο δίαυλο συστήματος, έτσι ώστε ο επεξεργαστής να έχει άμεση πρόσβαση σε αυτά τα δεδομένα). Σε αυτήν την περίπτωση, προκύπτει ένα πρόβλημα: πρώτον, η μνήμη μιας μονάδας flash QSPI δεν είναι διαθέσιμη αμέσως μετά την επανεκκίνηση της συσκευής (πρέπει να αρχικοποιήσετε ξεχωριστά τη λειτουργία αντιστοίχισης μνήμης) και, δεύτερον, δεν μπορείτε να "φλασάρετε" αυτήν τη μνήμη με ένα γνωστό bootloader.

Ως αποτέλεσμα, αποφασίστηκε να συνδεθεί όλος ο κώδικας στο QSPI και να τον αναβοσβήνουν με έναν αυτογραφικό φορτωτή που θα λάβει το απαιτούμενο δυαδικό αρχείο μέσω TFTP.

Αποτέλεσμα

Η ιδέα μεταφοράς αυτής της βιβλιοθήκης στο Embox εμφανίστηκε πριν από περίπου ένα χρόνο, αλλά ξανά και ξανά αναβλήθηκε για διάφορους λόγους. Ένα από αυτά είναι η υποστήριξη για το libstdc++ και την τυπική βιβλιοθήκη προτύπων. Το πρόβλημα της υποστήριξης C++ στο Embox ξεφεύγει από το πεδίο αυτού του άρθρου, επομένως εδώ θα πω μόνο ότι καταφέραμε να επιτύχουμε αυτήν την υποστήριξη στο σωστό ποσό για να λειτουργήσει αυτή η βιβλιοθήκη 🙂

Στο τέλος, αυτά τα προβλήματα ξεπεράστηκαν (τουλάχιστον αρκετά για να λειτουργήσει το παράδειγμα OpenCV) και το παράδειγμα έτρεξε. Χρειάζονται 40 μεγάλα δευτερόλεπτα για να αναζητήσει ο πίνακας όρια χρησιμοποιώντας το φίλτρο Canny. Αυτό, φυσικά, είναι πολύ μεγάλο (υπάρχουν σκέψεις σχετικά με τον τρόπο βελτιστοποίησης αυτού του θέματος, θα είναι δυνατό να γραφτεί ένα ξεχωριστό άρθρο σχετικά με αυτό σε περίπτωση επιτυχίας).

OpenCV στο STM32F7-Discovery

Ωστόσο, ο ενδιάμεσος στόχος ήταν να δημιουργηθεί ένα πρωτότυπο που θα δείχνει τη θεμελιώδη δυνατότητα εκτέλεσης του OpenCV στο STM32, αντίστοιχα, αυτός ο στόχος επετεύχθη, οκ!

tl;dr: οδηγίες βήμα προς βήμα

0: Λήψη πηγών Embox, όπως αυτό:

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

1: Ας ξεκινήσουμε συναρμολογώντας ένα bootloader που θα «αναβοσβήνει» μια μονάδα flash QSPI.

    make confload-arm/stm32f7cube

Τώρα πρέπει να διαμορφώσετε το δίκτυο, γιατί. Θα ανεβάσουμε την εικόνα μέσω TFTP. Για να ορίσετε τις διευθύνσεις IP πλακέτας και κεντρικού υπολογιστή, πρέπει να επεξεργαστείτε το conf/rootfs/δίκτυο.

Παράδειγμα διαμόρφωσης:

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 - διεύθυνση του διοικητικού συμβουλίου.

Μετά από αυτό, συλλέγουμε τον bootloader:

    make

2: Η συνήθης φόρτωση του bootloader (συγγνώμη για το λογοπαίγνιο) στον πίνακα - δεν υπάρχει τίποτα συγκεκριμένο εδώ, πρέπει να το κάνετε όπως για οποιαδήποτε άλλη εφαρμογή για το STM32F7Discovery. Εάν δεν ξέρετε πώς να το κάνετε, μπορείτε να διαβάσετε σχετικά εδώ.
3: Σύνταξη εικόνας με διαμόρφωση για το OpenCV.

    make confload-platform/opencv/stm32f7discovery
    make

4: Απόσπασμα από τμήματα ELF για εγγραφή στο QSPI στο 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

Υπάρχει ένα σενάριο στον κατάλογο conf που το κάνει αυτό, ώστε να μπορείτε να το εκτελέσετε

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

5: Χρησιμοποιώντας tftp, πραγματοποιήστε λήψη του qspi.bin.bin σε μια μονάδα flash 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 (δηλαδή στο bootloader), πρέπει να εκτελέσετε την ακόλουθη εντολή (υποθέτουμε ότι ο διακομιστής έχει τη διεύθυνση 192.168.2.1):

    embox> qspi_loader qspi.bin 192.168.2.1

6: Με εντολή goto πρέπει να "πηδήσετε" στη μνήμη QSPI. Η συγκεκριμένη τοποθεσία θα διαφέρει ανάλογα με τον τρόπο σύνδεσης της εικόνας, μπορείτε να δείτε αυτήν τη διεύθυνση με την εντολή mem 0x90000000 (η διεύθυνση έναρξης ταιριάζει στη δεύτερη λέξη 32-bit της εικόνας). θα χρειαστεί επίσης να επισημάνετε τη στοίβα -s, η διεύθυνση στοίβας είναι στο 0x90000000, για παράδειγμα:

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

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

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

7: Εκκίνηση

    embox> edges 20

και απολαύστε την αναζήτηση συνόρων 40 δευτερολέπτων 🙂

Εάν κάτι πάει στραβά - γράψτε ένα ζήτημα το αποθετήριο μας, ή στη λίστα αλληλογραφίας [προστασία μέσω email], ή σε ένα σχόλιο εδώ.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο