OpenCV على STM32F7-Discovery

OpenCV على STM32F7-Discovery أنا أحد مطوري نظام التشغيل امبوكس، وسأتحدث في هذا المقال عن كيف تمكنت من تشغيل OpenCV على لوحة STM32746G.

إذا كتبت شيئًا مثل "OpenCV on STM32 board" في محرك بحث ، فيمكنك العثور على عدد غير قليل من الأشخاص المهتمين باستخدام هذه المكتبة على لوحات STM32 أو وحدات تحكم دقيقة أخرى.
هناك العديد من مقاطع الفيديو التي ، بناءً على الاسم ، يجب أن توضح ما هو مطلوب ، ولكن عادةً (في جميع مقاطع الفيديو التي رأيتها) على لوحة STM32 ، تم استلام الصورة فقط من الكاميرا وتم عرض النتيجة على الشاشة ، وتمت معالجة الصور نفسها إما على جهاز كمبيوتر عادي أو على لوحات أكثر قوة (على سبيل المثال ، Raspberry Pi).

لماذا هو صعب؟

تفسر شعبية استعلامات البحث من خلال حقيقة أن OpenCV هي مكتبة رؤية الكمبيوتر الأكثر شيوعًا ، مما يعني أن المزيد من المطورين على دراية بها ، والقدرة على تشغيل تعليمات برمجية جاهزة لسطح المكتب على متحكم دقيق يبسط إلى حد كبير عملية التطوير. ولكن لماذا لا توجد حتى الآن وصفات شعبية جاهزة لحل هذه المشكلة؟

ترتبط مشكلة استخدام OpenCV على الشالات الصغيرة بميزتين:

  • إذا قمت بتجميع المكتبة حتى مع الحد الأدنى من مجموعة الوحدات النمطية ، فلن تتناسب ببساطة مع ذاكرة الفلاش الخاصة بنفس STM32F7Discovery (حتى بدون مراعاة نظام التشغيل) بسبب رمز كبير جدًا (عدة ميغا بايت من التعليمات)
  • المكتبة نفسها مكتوبة بلغة C ++ ، مما يعني
    • بحاجة إلى دعم لوقت التشغيل الإيجابي (استثناءات ، وما إلى ذلك)
    • القليل من الدعم لـ LibC / Posix ، والذي يوجد عادةً في نظام التشغيل للأنظمة المضمنة - أنت بحاجة إلى مكتبة قياسية بالإضافة إلى مكتبة قوالب STL قياسية (متجه ، وما إلى ذلك)

النقل إلى Embox

كالعادة ، قبل نقل أي برنامج إلى نظام التشغيل ، من الجيد محاولة بنائه بالشكل الذي قصده المطورون به. في حالتنا ، لا توجد مشاكل في هذا - يمكن العثور على الكود المصدري على githabe، المكتبة مبنية على نظام جنو / لينكس بالطريقة المعتادة.

والخبر السار هو أنه يمكن بناء 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 و. البيانات مساحة كبيرة ، لكن الرمز أكثر من 70 ميغا بايت. من الواضح أنه إذا كان هذا مرتبطًا بشكل ثابت بتطبيق معين ، فسيصبح الرمز أقل.

دعنا نحاول التخلص من أكبر عدد ممكن من الوحدات بحيث يتم تجميع مثال صغير (والذي ، على سبيل المثال ، سيخرج ببساطة إصدار 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 включены в сборку и т.п.>

الخطوة التالية هي تشغيل بعض الأمثلة ، ويفضل أن تكون واحدة من النماذج القياسية التي يقدمها المطورون أنفسهم. على موقعك. اخترت كاشف الحدود الحكيم.

يجب إعادة كتابة المثال قليلاً لعرض الصورة مع النتيجة مباشرة في المخزن المؤقت للإطار. كان علي أن أفعل هذا ، لأن. وظيفة imshow() يمكنه رسم الصور من خلال واجهات QT و GTK و Windows ، والتي ، بالطبع ، لن تكون بالتأكيد في التكوين لـ STM32. في الواقع ، يمكن أيضًا تشغيل QT على STM32F7Discovery ، ولكن سيتم مناقشة ذلك في مقال آخر 🙂

بعد توضيح قصير يتم فيه تخزين نتيجة كاشف الحافة ، نحصل على صورة.

OpenCV على STM32F7-Discovery

الصورة الأصلية

OpenCV على STM32F7-Discovery

نتيجة

يعمل على STM32F7Discovery

يوجد في 32F746GDISCOVERY العديد من أقسام ذاكرة الأجهزة التي يمكننا استخدامها بطريقة أو بأخرى

  1. 320 كيلو بايت رام
  2. 1 ميجا بايت فلاش للصورة
  3. 8 ميغا بايت SDRAM
  4. فلاش 16 ميجا بايت QSPI NAND
  5. منفذ كارت الذاكرة الصغيرة

يمكن استخدام بطاقة SD لتخزين الصور ، ولكن في سياق تشغيل مثال بسيط ، فإن هذا ليس مفيدًا جدًا.
تبلغ دقة الشاشة 480 × 272 ، مما يعني أن ذاكرة التخزين المؤقت للإطار ستكون 522،240 بايت على عمق 32 بت ، أي. هذا أكبر من حجم ذاكرة الوصول العشوائي ، لذا فإن ذاكرة التخزين المؤقت (frameebuffer) والكومة (التي ستكون مطلوبة ، بما في ذلك OpenCV ، لتخزين البيانات للصور والهياكل المساعدة) ستكون موجودة في SDRAM ، وكل شيء آخر (ذاكرة للأكوام واحتياجات النظام الأخرى ) إلى ذاكرة الوصول العشوائي.

إذا أخذنا الحد الأدنى من التكوين لـ STM32F7Discovery (تخلص من الشبكة بالكامل ، وجميع الأوامر ، وجعل الأكوام صغيرة قدر الإمكان ، وما إلى ذلك) وأضفنا OpenCV مع أمثلة هناك ، فستكون الذاكرة المطلوبة كما يلي:

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

بالنسبة لأولئك الذين ليسوا على دراية بالأقسام التي تذهب إلى أين ، سأشرح: in .text и .rodata التعليمات والثوابت (تقريبًا ، بيانات للقراءة فقط) تكمن في .data البيانات قابلة للتغيير ، .bss هناك متغيرات "nulled" والتي ، مع ذلك ، تحتاج إلى مكان (هذا القسم سوف "ينتقل" إلى RAM).

الخبر السار هو ذلك .data/.bss يجب أن تتناسب ، ولكن مع .text المشكلة هي أنه لا يوجد سوى 1 ميغا بايت من الذاكرة للصورة. يمكن التخلص منها .text الصورة من المثال وقراءتها ، على سبيل المثال ، من بطاقة SD إلى الذاكرة عند بدء التشغيل ، ولكن تزن الفواكه. png حوالي 330 كيلوبايت ، لذلك لن يحل هذا المشكلة: معظم .text يتكون من كود OpenCV.

بشكل عام ، لم يتبق سوى شيء واحد - تحميل جزء من الكود على فلاش QSPI (له وضع تشغيل خاص لتعيين الذاكرة إلى ناقل النظام ، بحيث يمكن للمعالج الوصول إلى هذه البيانات مباشرة). في هذه الحالة ، تظهر مشكلة: أولاً ، لا تتوفر ذاكرة محرك فلاش QSPI فور إعادة تشغيل الجهاز (تحتاج إلى تهيئة الوضع المعين للذاكرة بشكل منفصل) ، وثانيًا ، لا يمكنك "وميض" هذه الذاكرة باستخدام محمل مألوف.

نتيجة لذلك ، تقرر ربط كل الكود في QSPI ، ووميضه بمحمل مكتوب ذاتيًا يتلقى الملف الثنائي المطلوب عبر TFTP.

نتيجة

ظهرت فكرة نقل هذه المكتبة إلى Embox منذ حوالي عام ، ولكن تم تأجيلها مرارًا وتكرارًا لأسباب مختلفة. أحدها هو دعم libstdc ++ ومكتبة القوالب القياسية. تعد مشكلة دعم C ++ في Embox خارج نطاق هذه المقالة ، لذلك سأقول هنا فقط أننا تمكنا من تحقيق هذا الدعم بالقدر المناسب لهذه المكتبة للعمل 🙂

في النهاية ، تم التغلب على هذه المشكلات (على الأقل بما يكفي لكي يعمل مثال OpenCV) ، ونفذ المثال. تستغرق اللوحة 40 ثانية طويلة للبحث عن الحدود باستخدام مرشح Canny. هذا ، بالطبع ، طويل جدًا (هناك اعتبارات حول كيفية تحسين هذا الأمر ، سيكون من الممكن كتابة مقال منفصل حول هذا الأمر في حالة النجاح).

OpenCV على STM32F7-Discovery

ومع ذلك ، كان الهدف الوسيط هو إنشاء نموذج أولي يُظهر الإمكانية الأساسية لتشغيل OpenCV على STM32 ، على التوالي ، تم تحقيق هذا الهدف ، رائع!

TL ؛ د: تعليمات خطوة بخطوة

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: مقتطف من أقسام 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 على محرك أقراص فلاش 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 ثانية 🙂

إذا حدث خطأ ما - اكتب مشكلة في مستودعنا، أو إلى القائمة البريدية [البريد الإلكتروني محمي]، أو في تعليق هنا.

المصدر: www.habr.com

إضافة تعليق