Jak zaprojektować Wieloplatformowy pakiet SDK do obsługi wizji komputerowej niezależny od języka: praktyczny samouczek

(Cyrus Behroozi) (21 października 2020 r.)

Niedawno miałem okazję zaprezentować się na spotkaniu Venice Computer Vision . Jeśli nie jesteś zaznajomiony, jest to wydarzenie sponsorowane przez Trueface , podczas którego programiści i entuzjaści wizji komputerowej mogą zaprezentować najnowsze badania, aplikacje i praktyczne ćwiczenia samouczki.

W tym artykule omówię moją prezentację samouczkową dotyczącą projektowania niezależnego od języka zestawu SDK (SDK) do wdrażania na wielu platformach i maksymalnej rozszerzalności. Jeśli chcesz obejrzeć nagranie na żywo prezentacji, możesz to zrobić tutaj . Stworzyłem także cały projekt jako otwarty , więc możesz użyć go jako szablonu do następnego projektu komputerowego.

cyrusbehr / sdk_design

Jak zaprojektować zestaw SDK niezależny od języka do wdrażania międzyplatformowego i maksymalnej rozszerzalności. Wizja komputerowa w Wenecji…

github.com

Dlaczego ten samouczek jest ważny

Z mojego doświadczenia wynika, że ​​nigdy nie znalazłem wszechstronnego przewodnika, podsumowuje wszystkie stosowne kroki potrzebne do utworzenia niezależnego od języka, wieloplatformowego zestawu SDK. Musiałem przeszukać odmienną dokumentację, aby znaleźć tylko właściwe fragmenty informacji, nauczyć się każdego komponentu z osobna, a następnie samemu poskładać to wszystko razem. To było frustrujące. Zajęło to dużo czasu. A teraz, drogi czytelniku, skorzystaj z całej mojej pracy. Wcześniej dowiesz się, jak zbudować niezależny od języka, wieloplatformowy pakiet SDK. Są tam wszystkie niezbędne rzeczy. Żaden z puchu, zapisz kilka memów. Baw się dobrze.

W tym samouczku możesz się nauczyć, jak:

  • Zbudować podstawową bibliotekę widzenia komputerowego w C ++
  • Kompilować i krzyżować skompiluj bibliotekę dla AMD64, ARM64 i ARM32
  • Spakuj bibliotekę i wszystkie zależności jako pojedynczą bibliotekę statyczną
  • Zautomatyzuj testy jednostkowe
  • Skonfiguruj ciągłe potok integracji (CI)
  • Napisz powiązania Pythona dla naszej biblioteki
  • Generuj dokumentację bezpośrednio z naszego API

Na potrzeby tego demo utworzy pakiet SDK do wykrywania twarzy i punktów orientacyjnych przy użyciu narzędzia do wykrywania twarzy typu open source o nazwie MTCNN.

Przykład ramek ograniczających twarz i punktów orientacyjnych twarzy

Nasza funkcja API pobierze ścieżkę obrazu, a następnie zwróci współrzędne obwiedni twarzy i punktów orientacyjnych twarzy. Zdolność wykrywania twarzy jest bardzo przydatna w wizji komputerowej, ponieważ jest pierwszym krokiem w wielu procesach, w tym rozpoznawaniu twarzy, przewidywaniu wieku i automatycznym rozmywaniu twarzy.

Uwaga: samouczka, będę pracować na Ubuntu 18.04.

Po co używać C ++ w naszej bibliotece?

Uruchamianie wydajnego kodu C ++ może wyglądać następująco

Większość naszej biblioteki będzie napisana w C ++, języku skompilowanym i statycznie typowanym. Nie jest tajemnicą, że C ++ to bardzo szybki język programowania; jest wystarczająco niski, aby zapewnić nam pożądaną prędkość, i ma minimalne dodatkowe obciążenie w czasie wykonywania.

W aplikacjach do przetwarzania obrazu generalnie manipulujemy wieloma obrazami, wykonujemy operacje macierzowe, uruchamiamy wnioskowanie maszynowe, z których wszystko ogromna ilość obliczeń. Dlatego szybkość wykonania jest krytyczna. Jest to szczególnie ważne w aplikacjach czasu rzeczywistego, w których musisz zmniejszyć opóźnienie, aby osiągnąć żądaną liczbę klatek na sekundę – często mamy tylko milisekundy, aby uruchomić cały nasz kod.

Kolejną zaletą C ++ jest to, że jeśli skompilować dla określonej architektury i połączyć wszystkie zależności statycznie, a następnie możemy uruchomić go na tym sprzęcie bez konieczności stosowania dodatkowych interpreterów lub bibliotek. Wierz lub nie, ale możemy nawet działać na wbudowanym urządzeniu bez systemu operacyjnego!

Struktura katalogów

W naszym projekcie będziemy używać następującej struktury katalogów.

3rdparty będzie zawierał biblioteki zależności innych firm wymagane przez nasz projekt.

dist będzie zawierał pliki, które będą dystrybuowane do użytkowników końcowych SDK. W naszym przypadku będzie to sama biblioteka i powiązany plik nagłówkowy.

docker będzie zawierał plik dockera, który zostanie użyty do wygenerowania obrazu dockera dla kompilacji CI.

docs będzie zawierał skrypty kompilacji wymagane do generowania dokumentacji bezpośrednio z naszego pliku nagłówkowego.

include będzie zawierał wszelkie pliki dołączane dla publicznego interfejsu API.

models będzie zawierał pliki modelu głębokiego uczenia z wykrywaniem twarzy.

python będzie zawierał kod wymagany do wygenerowania powiązań Pythona.

src będzie zawierał wszystkie pliki CPP, które zostaną skompilowane, a także wszelkie pliki nagłówkowe, które nie będą dystrybuowane z SDK (wewnętrzne pliki nagłówkowe).

test będą zawierać nasze testy jednostkowe.

tools będzie zawierać nasze pliki łańcucha narzędzi CMake wymagane do kompilacji krzyżowej.

Instalowanie bibliotek zależności

W przypadku tego projektu firma zewnętrzna Wymagane biblioteki zależności to ncnn , lekka biblioteka wnioskowań systemów uczących się, OpenCV , biblioteka do rozszerzania obrazów, Catch2 , biblioteka do testów jednostkowych i wreszcie pybind11 , biblioteka używany do generowania powiązań Pythona. Pierwsze dwie biblioteki będą musiały zostać skompilowane jako samodzielne biblioteki, podczas gdy dwie ostatnie są tylko nagłówkami i dlatego wymagamy tylko źródła.

Jednym ze sposobów dodania tych bibliotek do naszych projektów jest użycie podmodułów git . Chociaż to podejście działa, osobiście jestem fanem używania skryptów powłoki, które pobierają kod źródłowy, a następnie budują dla żądanych platform: w naszym przypadku AMD64, ARM32 i ARM64.

Oto przykład tego, co z tych skryptów kompilacji wygląda następująco:

Skrypt jest dość prosty. Rozpoczyna się od ściągnięcia żądanego kodu źródłowego wydania z repozytorium git. Następnie CMake jest używany do przygotowania kompilacji, a następnie make jest wywoływany, aby sterować kompilatorem w celu zbudowania kodu źródłowego.

Zauważysz, że główna różnica między kompilacją AMD64 a kompilacją ARM jest taka, że kompilacje ARM przekazują dodatkowy parametr CMake o nazwie CMAKE_TOOLCHAIN_FILE. Ten argument służy do określania CMake, że architektura docelowa kompilacji (ARM32 lub ARM64) różni się od architektury hosta (AMD64 / x86_64). Dlatego CMake jest poinstruowany, aby użyć kompilatora krzyżowego określonego w wybranym pliku toolchain do zbudowania biblioteki (więcej o plikach toolchain w dalszej części tego samouczka). Aby ten skrypt powłoki działał, będziesz musiał mieć zainstalowane odpowiednie kompilatory krzyżowe na komputerze Ubuntu. Można je łatwo zainstalować za pomocą apt-get, a instrukcje, jak to zrobić, znajdują się tutaj .

Nasz interfejs API biblioteki

Nasz interfejs API biblioteki wygląda następująco:

Ponieważ jestem bardzo kreatywny, postanowiłem nazwać mój SDK MySDK. W naszym API mamy wyliczenie o nazwie ErrorCode, mamy strukturę o nazwie Point, a na koniec mamy jedną publiczną funkcję składową o nazwie getFaceBoxAndLandmarks. W zakresie tego samouczka nie będę wchodził w szczegóły implementacji SDK. Istota polega na tym, że wczytujemy obraz do pamięci za pomocą OpenCV, a następnie wykonujemy wnioskowanie z uczenia maszynowego za pomocą ncnn z modelami open source, aby wykryć ramkę ograniczającą twarz i punkty orientacyjne. Jeśli chcesz zagłębić się w implementację, możesz to zrobić tutaj .

Chciałbym, abyś jednak zwrócił uwagę na wzór projektu, którego używamy. Używamy techniki zwanej Wskaźnik do implementacji lub w skrócie pImpl , która zasadniczo usuwa szczegóły implementacji klasy, umieszczając je w oddzielnej klasie. W powyższym kodzie osiąga się to poprzez zadeklarowanie klasy Impl, a następnie umieszczenie unique_ptr w tej klasie jako prywatnej zmiennej składowej. Robiąc to, nie tylko ukrywamy implementację przed wścibskimi oczami użytkownika końcowego (co może być dość ważne w komercyjnym SDK), ale także zmniejszamy liczbę nagłówków, od których zależy nasz nagłówek API (a tym samym zapobiegamy Nagłówek API z #include nagłówków biblioteki zależności).

Uwaga o plikach modelu

Powiedziałem, że nie będziemy omawiać szczegóły realizacji, ale jest coś, o czym myślę, że warto o tym wspomnieć. Domyślnie używany przez nas detektor twarzy typu open source o nazwie MTCNN ładuje pliki modelu uczenia maszynowego w czasie wykonywania. Nie jest to idealne rozwiązanie, ponieważ oznacza to, że będziemy musieli przekazać modele użytkownikowi końcowemu. Ten problem jest jeszcze bardziej znaczący w przypadku modeli komercyjnych, w których nie chcesz, aby użytkownicy mieli swobodny dostęp do tych plików modeli (pomyśl o niezliczonych godzinach poświęconych na szkolenie tych modeli). Jednym z rozwiązań jest zaszyfrowanie plików tych modeli, co zdecydowanie radzę zrobić.Jednak nadal oznacza to, że musimy wysłać pliki modelu wraz z SDK. Ostatecznie chcemy zmniejszyć liczbę plików, które wysyłamy do użytkownika, aby ułatwić mu korzystanie z naszego oprogramowania (mniej plików to mniej miejsc, w których mogą wystąpić błędy). Dlatego możemy użyć metody pokazanej poniżej, aby przekonwertować pliki modelu na pliki nagłówkowe i faktycznie osadzić je w samym SDK.

Polecenie xdd bash służy do generowania zrzutów szesnastkowych i może być używane do generowania pliku nagłówkowego z pliku binarnego. Możemy zatem dołączyć pliki modeli do naszego kodu, jak zwykłe pliki nagłówkowe i załadować je bezpośrednio z pamięci. Ograniczeniem tego podejścia jest to, że nie jest to praktyczne w przypadku bardzo dużych plików modeli, ponieważ zużywa zbyt dużo pamięci podczas kompilacji. Zamiast tego możesz użyć narzędzia takiego jak ld, aby przekonwertować te duże pliki modeli bezpośrednio na pliki obiektowe.

CMake and Compiling our Library

Możemy teraz używać CMake do generowania plików kompilacji dla naszego projektu. Jeśli nie jesteś zaznajomiony, CMake jest generatorem systemu budowania używanym do zarządzania procesem kompilacji. Poniżej zobaczysz, jak wygląda część katalogu głównego CMakeLists.txt (plik CMake).

Zasadniczo tworzymy bibliotekę statyczną o nazwie my_sdk_static z dwoma plikami źródłowymi zawierającymi naszą implementację, my_sdk.cpp i mtcnn.cpp. Powodem, dla którego tworzymy bibliotekę statyczną, jest to, że z mojego doświadczenia wynika, że ​​jest ona łatwiejsza do rozpowszechniania wśród użytkowników i jest bardziej przyjazna dla urządzeń wbudowanych. Jak wspomniałem powyżej, jeśli plik wykonywalny jest połączony z biblioteką statyczną, można go uruchomić na urządzeniu wbudowanym, które nie ma nawet systemu operacyjnego. To po prostu nie jest możliwe w przypadku biblioteki dynamicznej. Dodatkowo przy bibliotekach dynamicznych musimy się martwić wersjami zależności. Możemy nawet potrzebować pliku manifestu związanego z naszą biblioteką. Biblioteki połączone statycznie mają również nieco lepszy profil wydajności niż ich dynamiczne odpowiedniki.

Następną rzeczą, którą robimy w naszym skrypcie CMake, jest wskazanie CMake, gdzie znaleźć niezbędne pliki nagłówkowe, których wymagają nasze pliki źródłowe. Coś do zapamiętania: chociaż nasza biblioteka skompiluje się w tym momencie, kiedy spróbujemy połączyć się z naszą biblioteką (na przykład z plikiem wykonywalnym), otrzymamy absolutną tonę niezdefiniowanych odniesień do błędów symboli. Dzieje się tak, ponieważ nie połączyliśmy żadnej z naszych bibliotek zależności. Więc jeśli chcielibyśmy z powodzeniem połączyć plik wykonywalny z libmy_sdk_static.a, musielibyśmy wyśledzić i połączyć również wszystkie biblioteki zależności (moduły OpenCV, ncnn itp.). W przeciwieństwie do bibliotek dynamicznych, biblioteki statyczne nie mogą rozwiązywać własnych zależności. W zasadzie są one po prostu zbiorem plików obiektowych spakowanych do archiwum.

W dalszej części tego samouczka pokażę, jak możemy spakować wszystkie biblioteki zależności w naszej bibliotece statycznej, aby użytkownik nie musiał martwić się o tworzenie linków do którejkolwiek z bibliotek zależności.

Krzyżowa kompilacja naszej biblioteki i plików Toolchain

Przetwarzanie na brzegu sieci jest tak… kanciaste

Wiele aplikacji komputerowych jest wdrażanych na krawędzi. To zazwyczaj polega na uruchomieniu kodu na urządzeniach wbudowanych o niskim poborze mocy, które zwykle mają procesory ARM. Ponieważ C ++ jest językiem skompilowanym, musimy skompilować nasz kod dla architektury procesora, na którym aplikacja będzie uruchamiana (każda architektura używa innych instrukcji asemblera).

Zanim zagłębimy się w to, zajmijmy się również różnica między ARM32 i ARM64, zwana także AArch32 i AArch64. AArch64 odnosi się do 64-bitowego rozszerzenia architektury ARM i jest zależne zarówno od procesora, jak i systemu operacyjnego. Na przykład, mimo że Raspberry Pi 4 ma 64-bitowy procesor ARM, domyślny system operacyjny Raspbian jest 32-bitowy. Dlatego takie urządzenie wymaga skompilowanego pliku binarnego AArch32. Gdybyśmy mieli uruchomić 64-bitowy system operacyjny, taki jak Gentoo na tym urządzeniu Pi, potrzebowalibyśmy skompilowanego pliku binarnego AArch64. Innym przykładem popularnego urządzenia wbudowanego jest NVIDIA Jetson, który ma wbudowany procesor graficzny i działa AArch64.

Aby skompilować krzyżowo, musimy określić w CMake, że nie kompilujemy dla architektury maszynę, na której obecnie budujemy. Dlatego musimy określić kompilator krzyżowy, którego powinien używać CMake. W przypadku AArch64 używamy kompilatora aarch64-linux-gnu-g++, aw przypadku AArch32 używamy kompilatora arm-linux-gnuebhif-g++ (hf oznacza hard float ).

Poniżej znajduje się przykład pliku toolchain. Jak widać, zamierzamy użyć kompilatora krzyżowego AArch64.

W naszym katalogu głównym CMakeLists.txt, możemy dodaj następujący kod na początku pliku.

Zasadniczo dodajemy opcje CMake, które można włączyć z wiersza poleceń w celu kompilacji krzyżowej. Włączenie opcji BUILD_ARM32 lub BUILD_ARM64 spowoduje wybranie odpowiedniego pliku łańcucha narzędzi i skonfigurowanie kompilacji pod kątem kompilacji krzyżowej.

Pakowanie naszego SDK z bibliotekami zależności

Jak wspomniano wcześniej, jeśli programista chce połączyć się z naszą biblioteką w tym momencie, będzie musiał również połączyć się ze wszystkimi bibliotekami zależności, aby rozwiązać wszystkie symbole z biblioteki zależności. Mimo że nasza aplikacja jest dość prosta, mamy już osiem bibliotek zależności! Pierwsza to ncnn, następnie mamy trzy biblioteki modułów OpenCV, a następnie mamy cztery biblioteki narzędziowe, które zostały zbudowane za pomocą OpenCV (libjpeg, libpng, zlib, libtiff). Moglibyśmy wymagać od użytkownika samodzielnego zbudowania bibliotek zależności lub nawet wysłania ich razem z naszą biblioteką, ale ostatecznie wymaga to więcej pracy od użytkownika i chodzi o obniżenie bariery użytkowania. Idealną sytuacją jest to, że możemy wysłać użytkownikowi pojedynczą bibliotekę, która zawiera naszą bibliotekę wraz ze wszystkimi bibliotekami zależności innych firm niż standardowe biblioteki systemowe. Okazuje się, że możemy to osiągnąć za pomocą magii CMake.

Najpierw dodajemy niestandardowy cel do nasz CMakeLists.txt, a następnie wykonaj tak zwany skrypt MRI. Ten skrypt MRI jest przekazywany do polecenia ar -M bash, które w zasadzie łączy wszystkie biblioteki statyczne w jedno archiwum. Fajne w tej metodzie jest to, że z wdziękiem radzi sobie z nakładającymi się nazwami członków z oryginalnych archiwów, więc nie musimy się martwić o tam konflikty. Zbudowanie tego niestandardowego celu zaowocuje libmy_sdk.a, który będzie zawierał nasz pakiet SDK wraz ze wszystkimi archiwami zależności.

Poczekaj chwilę: podsumujmy, co robimy już zrobiliśmy.

Weź oddech. Weź coś do przekąszenia. Zadzwoń do swojej mamy.

W tym momencie mamy bibliotekę statyczną o nazwie libmy_sdk.a, która zawiera nasz SDK i wszystkie biblioteki zależności, które spakowaliśmy w jednym archiwum. Mamy również możliwość kompilacji i cross-kompilacji (przy użyciu argumentów wiersza poleceń) dla wszystkich naszych platform docelowych.

Testy jednostkowe

Kiedy uruchamiasz testy jednostkowe po raz pierwszy

Prawdopodobnie nie muszę wyjaśnij, dlaczego testy jednostkowe są ważne, ale w zasadzie są one kluczową częścią projektu SDK, która pozwala deweloperowi upewnić się, że SDK działa z wcięciem. Dodatkowo, jeśli wprowadzane są jakiekolwiek istotne zmiany, pomaga to wyśledzić je i szybciej wypuścić poprawki.

W tym konkretnym przypadku utworzenie pliku wykonywalnego testu jednostkowego daje nam również możliwość połączenia się z połączona biblioteka, którą właśnie stworzyliśmy, aby zapewnić, że możemy poprawnie linkować zgodnie z przeznaczeniem (i nie otrzymujemy żadnego z tych nieprzyjemnych, niezdefiniowanych błędów odniesienia do symbolu).

Używamy Catch2 jako naszego frameworka do testów jednostkowych . Składnia jest przedstawiona poniżej:

Jak działa Catch2, mamy to makro o nazwie TEST_CASE i inne makro o nazwie SECTION. Dla każdego SECTION TEST_CASE jest wykonywane od początku. W naszym przykładzie mySdk zostanie najpierw zainicjowany, a następnie uruchomiona zostanie pierwsza sekcja o nazwie „Obraz inny niż twarz”. Następnie mySdk zostanie zdekonstruowany przed rekonstrukcją, a następnie uruchomiona zostanie druga sekcja o nazwie „Twarze na obrazie”. Jest to świetne, ponieważ zapewnia, że ​​mamy nowy obiekt MySDK do wykonania dla każdej sekcji. Następnie możemy użyć makr, takich jak REQUIRE, aby tworzyć nasze asercje.

Możemy użyć CMake do zbudowania pliku wykonywalnego do testów jednostkowych o nazwie run_tests. Jak widać w wywołaniu target_link_libraries w wierszu 3 poniżej, jedyną biblioteką, z którą musimy się połączyć, jest nasza libmy_sdk.a i żadna inna biblioteki zależności.

Dokumentacja

Gdyby tylko użytkownicy przeczytali tę cholerną dokumentację.

Użyjemy doxygen do generowania dokumentacji bezpośrednio z naszego pliku nagłówkowego. Możemy iść dalej i udokumentować wszystkie nasze metody i typy danych w naszym publicznym nagłówku, używając składni pokazanej we fragmencie kodu poniżej.Pamiętaj, aby określić wszystkie parametry wejściowe i wyjściowe dla dowolnych funkcji.

W celu faktycznego wygenerowania dokumentacji , potrzebujemy czegoś, co nazywa się doxyfile, który jest w zasadzie schematem instruowania doxygen, jak wygenerować dokumentację. Możemy wygenerować ogólny plik doxyfile, uruchamiając doxygen -g w naszym terminalu, zakładając, że w systemie jest zainstalowany doxygen. Następnie możemy edytować plik doxyfile. Musimy przynajmniej określić katalog wyjściowy, a także pliki wejściowe.

W naszym przypadku, chcemy tylko wygenerować dokumentację z naszego pliku nagłówkowego API, dlatego określiliśmy katalog include. Wreszcie, używasz CMake do faktycznego tworzenia dokumentacji, co można zrobić w ten sposób .

Python Bindings

Czy masz już dość oglądania częściowo trafnych gifów? Tak, ja też nie.

Bądźmy szczerzy. C ++ nie jest najłatwiejszym ani najbardziej przyjaznym językiem do programowania. Dlatego chcemy rozszerzyć naszą bibliotekę o obsługę powiązań językowych, aby była łatwiejsza w użyciu dla programistów. Pokażę to za pomocą Pythona, ponieważ jest to popularny język prototypowania wizji komputerowej, ale inne powiązania językowe są równie łatwe do napisania. Aby to osiągnąć, używamy pybind11:

Zaczynamy od PYBIND11_MODULE makro, które tworzy funkcję, która zostanie wywołana, gdy instrukcja importu zostanie wydana z poziomu Pythona. Zatem w powyższym przykładzie nazwa modułu Pythona to mysdk. Następnie jesteśmy w stanie zdefiniować nasze klasy i ich składowe przy użyciu składni pybind11.

Warto zwrócić uwagę: w C ++ dość często przekazuje się zmienne za pomocą zmiennego odwołania, które umożliwia zarówno odczyt, jak i zapis. Dokładnie to zrobiliśmy z naszą funkcją składową API z parametrami faceDetected i fbAndLandmarks. W Pythonie wszystkie argumenty są przekazywane przez odwołanie. Jednak niektóre podstawowe typy Pythona są niezmienne, w tym bool. Przypadkowo, nasz parametr faceDetected jest wartością logiczną, która jest przekazywana przez zmienne odniesienie. Dlatego musimy zastosować obejście pokazane w powyższym kodzie w liniach od 31 do 34, w których definiujemy wartość bool w naszej funkcji opakowującej Pythona, a następnie przekazujemy ją do naszej funkcji C ++ przed zwróceniem zmiennej jako części krotki.

Po zbudowaniu biblioteki powiązań Pythona możemy z łatwością ją wykorzystać, korzystając z poniższego kodu:

Continuous Integration

W naszym potoku ciągłej integracji będziemy używać narzędzia o nazwie CircleCI, które bardzo mi się podoba, ponieważ integruje się bezpośrednio z Github. Nowa kompilacja będzie uruchamiana automatycznie za każdym razem, gdy naciśniesz zatwierdzenie. Aby rozpocząć, przejdź do witryny CircleCI i połącz ją z kontem Github, a następnie wybierz projekt, który chcesz dodać. Po dodaniu musisz utworzyć katalog .circleci w katalogu głównym swojego projektu i utworzyć plik o nazwie config.yml w tym katalogu.

Dla każdego, kto nie jest zaznajomiony, YAML jest językiem serializacji powszechnie używanym w plikach konfiguracyjnych. Możemy go użyć, aby poinstruować, jakie operacje mają wykonać CircleCI. W poniższym fragmencie YAML możesz zobaczyć, jak najpierw budujemy jedną z bibliotek zależności, następnie budujemy sam SDK, a na końcu tworzymy i uruchamiamy testy jednostkowe.

Jeśli jesteśmy inteligentni (i zakładam, że tak jest, jeśli dotarłeś tak daleko), możemy użyć buforowania, aby znacznie skrócić czas kompilacji. Na przykład w powyższym YAML buforujemy kompilację OpenCV przy użyciu skrótu skryptu kompilacji jako klucza pamięci podręcznej. W ten sposób biblioteka OpenCV zostanie odbudowana tylko wtedy, gdy skrypt kompilacji zostanie zmodyfikowany – w przeciwnym razie zostanie użyta kompilacja z pamięci podręcznej. Inną rzeczą, na którą należy zwrócić uwagę, jest to, że uruchamiamy kompilację wewnątrz wybranego przez nas obrazu dockera. Wybrałem niestandardowy obraz dockera ( tutaj to plik Dockerfile), w którym zainstalowałem wszystkie zależności systemowe.

Fin.

I gotowe. Jak każdy dobrze zaprojektowany produkt, chcemy obsługiwać najbardziej pożądane platformy i ułatwiać korzystanie z niego jak największej liczbie programistów. Korzystając z powyższego samouczka, stworzyliśmy SDK, który jest dostępny w kilku językach i można go wdrażać na wielu platformach. Nie musiałeś nawet samodzielnie czytać dokumentacji pybind11. Mam nadzieję, że ten samouczek okazał się przydatny i zabawny. Miłego budowania.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *