Im Moment entwickle ich eine Anwendung mit der OpenCV-API (C++
). Diese Anwendung verarbeitet Videos.
Am PC geht alles sehr schnell. Und heute habe ich beschlossen, diese Anwendung auf Android zu portieren (um die Kamera als Videoeingang zu verwenden). Glücklicherweise gibt es OpenCV für Android, also habe ich gerade meinen nativen Code zur Android-Beispielanwendung hinzugefügt. Bis auf die Leistung funktioniert alles. Ich habe meine Anwendung einem Benchmarking unterzogen und festgestellt, dass die Anwendung mit 4-5 fps arbeitet, was eigentlich nicht akzeptabel ist (mein Gerät hat einen Singlecore-1-GHz-Prozessor) – ich möchte, dass es mit etwa 10 fps funktioniert.
Macht es Sinn meine Bewerbung komplett neu zu schreiben C
? Ich weiß, dass mit solchen Dingen wie std::vector
ist viel bequemer für Entwickler, aber ich kümmere mich nicht darum.
Es scheint, dass OpenCV's C
Schnittstelle hat dieselben Funktionen/Methoden wie C++
Schnittstelle.
Ich habe diese Frage gegoogelt, aber nichts gefunden.
Danke für jeden Rat.
Ich habe ziemlich viel mit Android und Optimierungen gearbeitet (ich habe eine Videoverarbeitungs-App geschrieben, die einen Frame in 4 ms verarbeitet), also hoffe ich, dass ich Ihnen einige relevante Antworten geben kann.
Es gibt keinen großen Unterschied zwischen der C- und der C++-Schnittstelle in OpenCV. Ein Teil des Codes ist in C geschrieben und hat einen C++-Wrapper, und ein Teil umgekehrt. Alle signifikanten Unterschiede zwischen den beiden (wie von Shervin Emami gemessen) sind entweder Rückschritte, Fehlerbehebungen oder Qualitätsverbesserungen. Sie sollten bei der neuesten OpenCV-Version bleiben.
Warum nicht umschreiben?
Sie verbringen viel Zeit, die Sie viel besser nutzen könnten. Die C-Schnittstelle ist umständlich und die Wahrscheinlichkeit, Fehler oder Speicherlecks einzuführen, ist hoch. Das sollte man meiner Meinung nach vermeiden.
Beratung zur Optimierung
A. Aktivieren Sie Optimierungen.
Sowohl Compiler-Optimierungen als auch das Fehlen von Debug-Assertionen können einen großen Unterschied in Ihrer Laufzeit machen.
B. Profilieren Sie Ihre App.
Tun Sie es zuerst auf Ihrem Computer, da es viel einfacher ist. Verwenden Sie Visual Studio Profiler, um die langsamen Teile zu identifizieren. Optimieren Sie sie. Optimieren Sie niemals, weil Sie denken, dass es langsam ist, sondern weil Sie es messen. Beginnen Sie mit der langsamsten Funktion, optimieren Sie sie so weit wie möglich, und nehmen Sie dann die zweite langsamer. Messen Sie Ihre Änderungen, um sicherzustellen, dass sie tatsächlich schneller sind.
C. Fokus auf Algorithmen.
Ein schnellerer Algorithmus kann die Leistung um Größenordnungen (100x) verbessern. Ein C++-Trick bringt Ihnen vielleicht einen 2-fachen Leistungsschub.
Klassische Techniken:
-
Passen Sie die Größe Ihrer Videoframes an, um sie kleiner zu machen. Oft können Sie die Informationen aus einem 200 x 300 Pixel großen Bild extrahieren, anstatt aus einem 1024 x 768 Pixel. Die Fläche des ersten ist 10 mal kleiner.
-
Verwenden Sie einfachere Operationen statt komplizierter. Verwenden Sie Ganzzahlen anstelle von Floats. Und niemals verwenden double
in einer Matrix oder a for
Schleife, die tausende Male ausgeführt wird.
-
Rechne so wenig wie möglich. Können Sie ein Objekt nur in einem bestimmten Bereich des Bildes verfolgen, anstatt alles für alle Frames zu verarbeiten? Können Sie eine grobe/ungefähre Erkennung auf einem sehr kleinen Bild vornehmen und es dann auf einen ROI im Vollbild verfeinern?
D. Verwenden Sie C, wo es darauf ankommt
In Schleifen kann es sinnvoll sein, statt C++ den C-Stil zu verwenden. Ein Zeiger auf eine Datenmatrix oder ein Float-Array ist viel schneller als mat.at oder std::vector<>. Oft ist der Engpass eine verschachtelte Schleife. Konzentrieren Sie sich darauf. Es macht keinen Sinn, vector<> überall zu ersetzen und Ihren Code zu spaghettifizieren.
E. Vermeiden Sie versteckte Kosten
Einige OpenCV-Funktionen konvertieren Daten in Double, verarbeiten sie und konvertieren sie dann zurück in das Eingabeformat. Hüten Sie sich vor ihnen, sie beeinträchtigen die Leistung auf Mobilgeräten. Beispiele: Warping, Skalierung, Typumwandlungen. Außerdem ist bekannt, dass Farbraumkonvertierungen träge sind. Bevorzugen Sie direkt aus dem nativen YUV erhaltene Graustufen.
F. Vektorisierung verwenden
ARM-Prozessoren implementieren die Vektorisierung mit einer Technologie namens NEON. Lernen Sie, es zu benutzen. Es ist mächtig!
Ein kleines Beispiel:
float* a, *b, *c;
// init a and b to 1000001 elements
for(int i=0;i<1000001;i++)
c[i] = a[i]*b[i];
kann wie folgt umgeschrieben werden. Es ist ausführlicher, aber viel schneller.
float* a, *b, *c;
// init a and b to 1000001 elements
float32x4_t _a, _b, _c;
int i;
for(i=0;i<1000001;i+=4)
{
a_ = vld1q_f32( &a[i] ); // load 4 floats from a in a NEON register
b_ = vld1q_f32( &b[i] );
c_ = vmulq_f32(a_, b_); // perform 4 float multiplies in parrallel
vst1q_f32( &c[i], c_); // store the four results in c
}
// the vector size is not always multiple of 4 or 8 or 16.
// Process the remaining elements
for(;i<1000001;i++)
c[i] = a[i]*b[i];
Puristen sagen, dass Sie in Assembler schreiben müssen, aber für einen normalen Programmierer ist das ein bisschen abschreckend. Ich hatte gute Ergebnisse mit gcc-Intrinsikwie im obigen Beispiel.
Eine weitere Möglichkeit für einen schnellen Start besteht darin, handcodierten SSE-optimierten Code in OpenCV in NEON zu konvertieren. SSE ist das NEON-Äquivalent in Intel-Prozessoren, und viele OpenCV-Funktionen verwenden es, wie z hier. Dies ist der Bildfiltercode für uchar-Matrizen (das reguläre Bildformat). Sie sollten Anweisungen nicht blind einzeln umwandeln, sondern sich zunächst ein Beispiel nehmen.
Mehr über NEON erfahren Sie in dieses Blog und die folgenden Beiträge.
G. Achten Sie auf die Bildaufnahme
Auf einem Mobilgerät kann es überraschend langsam sein. Die Optimierung ist geräte- und betriebssystemspezifisch.
Bevor Sie eine solche Entscheidung treffen, sollten Sie Ihren Code profilieren, um die Hotspots in Ihrem Code zu finden. Ohne diese Informationen sind alle Änderungen, die Sie zur Beschleunigung vornehmen, reine Vermutungen. Haben Sie dies versucht Android NDK-Profiler?
Es gibt einige Leistungstests, die Shervin Imami auf seiner Website durchgeführt hat. Sie können es überprüfen, um einige Ideen zu bekommen.
http://www.shervinemami.info/timingTests.html
Ich hoffe es hilft.
(Und außerdem wäre es schön, wenn Sie Ihre eigenen Erkenntnisse irgendwo teilen, wenn Sie eine Möglichkeit zur Leistungssteigerung erhalten.)
Ich denke, die Frage muss formuliert werden: Ist C schneller als C++? und die Antwort ist NEIN. Beide sind in die native Maschinensprache kompiliert und C++ ist so schnell wie C. Was die STL (insbesondere ISO-Standard) betrifft, so sind sie auch so konzipiert und darauf geachtet, dass sie so schnell wie Zeiger sind + sie bieten Flexibilität. Der einzige Grund, C zu verwenden, ist, dass Ihre Plattform C++ nicht unterstützt. Konvertieren Sie meiner bescheidenen Meinung nach nicht alles in C, da Sie wahrscheinlich fast die gleiche Leistung erhalten. und versuchen Sie stattdessen, Ihren Code zu verbessern oder andere Funktionalitäten von opencv zu verwenden, um das zu tun, was Sie wollen.
Nicht überzeugt? Nun, dann schreiben Sie eine einfache Funktion, einmal in C und einmal in C++, und führen Sie sie in einer Schleife von 100 Millionen Mal aus und messen Sie die Zeit selbst. Vielleicht hilft dir das, die richtige Entscheidung zu treffen
Ich habe noch nie C oder C++ in Android verwendet. Aber in einem PC können Sie C++ dazu bringen, so schnell wie C-Code zu laufen (manchmal sogar schneller). Der größte Teil von C++ wurde speziell entwickelt, um mehr Funktionen zu ermöglichen, jedoch nicht auf Kosten der Geschwindigkeit (Templates werden zur Kompilierzeit aufgelöst). Die meisten Compiler sind ziemlich gut darin, Ihren Code zu optimieren, und Ihre std::vector-Aufrufe werden inline ausgeführt, und der Code ist fast derselbe wie bei der Verwendung eines nativen C-Arrays.
Ich würde vorschlagen, dass Sie nach einer anderen Möglichkeit suchen, Ihre Leistung zu verbessern. Vielleicht gibt es einige Multimedia-Hardwareerweiterungen in Android, auf die Sie zugreifen und den Code optimieren können.
Bei mehreren Tests ist mir folgendes aufgefallen:
-
Die C-Schnittstelle (IplImage) ist um ein Vielfaches schneller, wenn direkt auf die Pixel zugegriffen wird, anstatt die Mat.at(x,y)-Methode zu verwenden. Als ich meine C++-Anwendung in C konvertierte, hatte ich eine 3-fache Leistungssteigerung in meiner Blob-Erkennungsroutine
-
Die C++-Schnittstelle stürzt in bestimmten Routinen ab, wenn sie von externen Anwendungen (z. B. LabView) aufgerufen wird, während sie beim Aufruf derselben Routinen in C funktioniert. Beispiel hierfür sind FindContours und cvFindContours
-
C ist weitaus kompatibler mit eingebetteten Geräten. Allerdings habe ich in diesem Bereich noch nichts gemacht.
Ich hatte ähnliche Probleme auf IOS-Geräten und die Diskussion Maximale Geschwindigkeit von IOS/iPad/iPhone enthält einige Hinweise, die auch auf andere mobile Plattformen anwendbar sind.
Ich bin insbesondere mit Android nicht vertraut, aber die Verwendung der C-Schnittstelle von OpenCV bringt Ihnen keinen signifikanten Leistungsschub, da immer noch derselbe Basiscode verwendet wird.
– Mohammed
7. Juli 2012 um 15:47 Uhr
Ich habe meiner Antwort weitere Informationen hinzugefügt. Ich hoffe, Sie finden sie nützlich.
– Sam
7. Juli 2012 um 19:45 Uhr