OpenCV ist eine Bibliothek mit Computer-Vision-Algorithmen, Bildverarbeitung und numerischen Allzweckalgorithmen mit Open Source, die vielen C ++ - Entwicklern bekannt ist. Neben C ++ wird OpenCV auch für Python, Java, Ruby, Matlab, Lua und andere Sprachen entwickelt. Da es unter diesen Sprachen kein Haupt-C # gibt, habe ich beschlossen, auf die OpenCvSharp-Wrapper-Bibliothek unter C # zu achten und dieses Projekt zu überprüfen. Was daraus wurde, finden Sie in diesem Artikel.
Einleitung
Bevor ich zu PVS-Studio kam, war ich auf Ausstellungen mit Robotik beschäftigt. Zu meinen Aufgaben gehörten die grundlegendste Reparatur (wenn ein schwerwiegender Ausfall auftrat, wurde der Roboter an eine andere Person übergeben) sowie die Entwicklung einer Vielzahl von Software und Dienstprogrammen.
Müde und vor kurzem in einer neuen Stadt angekommen, habe ich zusammen mit einem frisch ausgepackten KIKI-Roboter.Apropos Entwicklung. Das war ziemlich lustig. Jedes Mal, wenn jemandem aus dem Team eine Idee einfiel, was die Gäste der Ausstellungen sonst noch überraschen würde, haben wir dieses Thema zur allgemeinen Diskussion gebracht und, wenn die Idee gut war, die Umsetzung aufgenommen. Einmal kam uns die Idee, eine Kreatur zu erschaffen, die auf ein menschliches Gesicht mit einer Begrüßungsrede reagiert.
Nachdem ich im Internet nach einer Bibliothek für meine Bedürfnisse gesucht hatte, stieß ich auf die OpenCV-Website, Bibliotheken mit Computer-Vision-Algorithmen. Ich war bald enttäuscht - OpenCV wurde in C ++ implementiert. Mein Wissen über die Vorteile, die ich vom College bekam, war eindeutig nicht genug. Daher stieß ich durch kurzes Googeln auf OpenCvSharp - Wrapper dieser Bibliothek unter C #, meiner Hauptsprache. Seitdem sind sechs Monate vergangen, das Programm wurde bereits lange geschrieben und verwendet, und ich habe mich entschlossen, unter die Haube von OpenCvSharp zu gehen und den Quellcode mit dem statischen Analysegerät PVS-Studio zu überprüfen.
Geprüftes Projekt
OpenCvSharp ist ein Wrapper über OpenCV zur Verwendung der Bibliothek in C # -Projekten. OpenCV-Bibliothek haben
wir übrigens
auch überprüft . Zu den Vorteilen von OpenCvSharp gehören eine große Sammlung von Codebeispielen, plattformübergreifende Funktionen (die auf jeder von Mono unterstützten Plattform ausgeführt werden können) und eine einfache Installation.
Der Wrapper ist ein kleines Projekt und enthält ungefähr 112.200 Zeilen C # -Code. Davon sind 1,2% Kommentare, die übrigens irgendwie verdächtig klein sind. Aber für ein so kleines Projekt gibt es viele Fehler. In dem Artikel habe ich mehr als 20 Fehler geschrieben, aber es gab andere, die nicht so interessant oder offensichtlich waren.
PVS-Studio Code Analyzer
PVS-Studio ist ein Tool zum Erkennen von Fehlern und potenziellen Schwachstellen im Quellcode von Programmen, die in C, C ++, C # und Java geschrieben wurden. Läuft unter Windows, Linux und MacOS. Zusätzlich zu unerreichbarem Code, Fehlern und Tippfehlern kann PVS-Studio potenzielle Sicherheitslücken erkennen, wie oben erwähnt. Daher kann es als Tool für statische Anwendungssicherheitstests (Static Application Security Testing, SAST) betrachtet werden.
Codeteile, die bei der Prüfung eines Analysatorberichts aufgefallen sind
Die
WriteableBitmapConverter- Methode macht sofort mit vier gleichen PVS-Studio-Warnungen auf sich aufmerksam:
- V3005 Die Variable 'optimumChannels [PixelFormats.Indexed1]' wird sich selbst zugewiesen. WriteableBitmapConverter.cs 22
- V3005 Die Variable 'optimumChannels [PixelFormats.Indexed8]' wird sich selbst zugewiesen. WriteableBitmapConverter.cs 23
- V3005 Die Variable 'optimumTypes [PixelFormats.Indexed1]' wird sich selbst zugewiesen. WriteableBitmapConverter.cs 50
- V3005 Die Variable 'optimumTypes [PixelFormats.Indexed8]' wird sich selbst zugewiesen. WriteableBitmapConverter.cs 51
static WriteableBitmapConverter() { optimumChannels = new Dictionary <PixelFormat, int>(); optimumChannels[PixelFormats.Indexed1] =
Die
PixelFormats- Klasse
wird im
System.Windows.Media-Namespace definiert und ist eine Sammlung verschiedener Pixelformate. Der Analysator macht darauf aufmerksam, dass die
WriteableBitmapConverter- Methode die Werte den
Elementen optimumChannels [PixelFormats.Indexed1] und
optimumChannels [PixelFormats.Indexed8] neu zuweist , was praktisch keinen Sinn
ergibt . Es ist unklar, ob dies ein einfacher Tippfehler ist oder etwas anderes gemeint war. Übrigens zeigt dieser Codeabschnitt deutlich die Vorteile statischer Analysegeräte. Wenn Sie vor Ihren Augen eine Reihe von Zeilen des gleichen Typs sehen, beginnen Ihre Augen zu „verschwimmen“ und Ihre Aufmerksamkeit lässt nach - es ist nicht verwunderlich, wenn sich auch nach der Codeüberprüfung ein Tippfehler in das Programm einschleicht. Und der statische Analysator hat keine Probleme mit der Aufmerksamkeit und muss sich nicht ausruhen, daher ist es für ihn einfacher, solche Fehler zu finden.
Spüren Sie die Kraft und Kraft der statischen Analyse.PVS-Studio Warnung :
V3021 Es gibt zwei if-Anweisungen mit identischen bedingten Ausdrücken. Die erste 'if'-Anweisung enthält die Methodenrückgabe. Dies bedeutet, dass die zweite 'if'-Anweisung sinnlos ist. InputArray.cs 394
private static MatType EstimateType(Type t) { .... if (t == typeof(Vec2b)) return MatType.CV_8UC2; if (t == typeof(Vec3b)) return MatType.CV_8UC3; if (t == typeof(Vec4b)) return MatType.CV_8UC4; if (t == typeof(Vec6b)) return MatType.CV_8UC(6); if (t == typeof(Vec2s))
Dieser Fehler ist ein bisschen wie der vorherige. Der Programmierer hat eine Situation erstellt, in der derselbe Zustand zweimal überprüft wird. In diesem Fall macht dies keinen Sinn - dann wird der Zweig der "duplizierten"
if-Anweisung nicht ausgeführt, da:
- Wenn der erste bedingte Ausdruck wahr ist, wird die Methode beendet.
- Wenn die erste Bedingung falsch ist, ist auch die zweite falsch, da sich die zu testende Variable - t - nicht zwischen bedingten Ausdrücken ändert.
Der Entwickler sollte diesen Code noch einmal überprüfen. Es ist wahrscheinlich, dass anstelle der zweiten Variablen
Vec2s eine andere sein sollte.
PVS-Studio Warnung :
V3010 Der Rückgabewert der Funktion 'ToString' muss verwendet werden. ImgProcTest.cs 80
public static RectanglesIntersectTypes RotatedRectangleIntersection(RotatedRect rect1, RotatedRect rect2, out Point2f[] intersectingRegion) { using (var intersectingRegionVec = new VectorOfPoint2f()) { int ret = NativeMethods .imgproc_rotatedRectangleIntersection_vector( rect1, rect2, intersectingRegionVec.CvPtr); intersectingRegion = intersectingRegionVec.ToArray(); return (RectanglesIntersectTypes) ret; } } public void RotatedRectangleIntersectionVector() { var rr1 = new RotatedRect(new Point2f(100, 100), new Size2f(100, 100), 45); var rr2 = new RotatedRect(new Point2f(130, 100), new Size2f(100, 100), 0); Cv2.RotatedRectangleIntersection(rr1, rr2, out var intersectingRegion); .... intersectingRegion.ToString(); }
Die
RotatedRectangleIntersection- Methode gibt ein Array von
Point2f- Elementen über den Parameter
intersectingRegion zurück . Nachdem das Programm die
intersectingRegion mit Werten
gefüllt hat , wird die
ToString () -Methode für dieses Array aufgerufen
. Bei den Elementen des Arrays treten keine Änderungen auf, und in der letzten Zeile werden keine nützlichen Arbeiten ausgeführt. Daher besteht Grund zu der Annahme, dass der Entwickler einfach vergessen hat, sie zu entfernen.
PVS-Studio-Warnungen :
- V3021 Es gibt zwei 'if'-Anweisungen mit identischen bedingten Ausdrücken. Die erste 'if'-Anweisung enthält die Methodenrückgabe. Dies bedeutet, dass die zweite 'if'-Anweisung sinnlos ist. Cv2_calib3d.cs 1370
- V3022 Der Ausdruck 'objectPoints == null' ist immer falsch. Cv2_calib3d.cs 1372
public static double CalibrateCamera(....) { if (objectPoints == null) throw new ArgumentNullException(nameof(objectPoints)); if (objectPoints == null) throw new ArgumentNullException(nameof(objectPoints)); .... }
In diesem Fall wurde ein Codefragment dupliziert, wodurch zwei Warnungen angezeigt wurden. Die erste Warnung zeigt an, dass beide
if-Anweisungen denselben bedingten Ausdruck haben. Wenn dieser Ausdruck wahr ist, verlässt die Methode den
then- Zweig der ersten
if-Anweisung . Aus diesem Grund ist die zweite Bedingung immer falsch, wie durch die folgende Warnung angezeigt. Anscheinend wurde der Text kopiert, aber vergessen zu korrigieren.
Nettes Kopieren-Einfügen.Andere ähnliche Warnungen des Analysators:
- V3021 Es gibt zwei 'if'-Anweisungen mit identischen bedingten Ausdrücken. Die erste 'if'-Anweisung enthält die Methodenrückgabe. Dies bedeutet, dass die zweite 'if'-Anweisung sinnlos ist. Cv2_calib3d.cs 1444
- V3022 Der Ausdruck 'objectPoints == null' ist immer falsch. Cv2_calib3d.cs 1446
PVS-Studio- Warnung :
V3022 Ausdruck 'label == MarkerValue' ist immer falsch. Labeller.cs 135
internal static class Labeller { .... private const int MarkerValue = -1; public static int Perform(Mat img, CvBlobs blobs) { .... int label = 0; int lastLabel = 0; CvBlob lastBlob = null; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { if (imgIn[x + y * step] == 0) continue; bool labeled = labels[y, x] != 0; if (....) { labeled = true;
In diesem Codeabschnitt wird eine
Beschriftungsvariable von Null erstellt. Wenn eine bestimmte Bedingung erfüllt ist, kann eine zu dieser Variablen hinzugefügt werden. In diesem Fall ändert sich im Code der Wert der Variablenbezeichnung nicht nach unten. In der mit einem Pfeil markierten Zeile wird diese Variable mit einer Konstanten von -1 verglichen, was praktisch keinen Sinn ergibt.
PVS-Studio Warnung :
V3038 Das Argument wurde mehrmals an die Methode übergeben. Es ist möglich, dass stattdessen ein anderes Argument übergeben wird. Cv2_photo.cs 124
public static void FastNlMeansDenoisingMulti(....) { .... NativeMethods.photo_fastNlMeansDenoisingMulti( srcImgPtrs, srcImgPtrs.Length, dst.CvPtr, imgToDenoiseIndex, templateWindowSize, h, templateWindowSize, searchWindowSize); .... }
Um zu verstehen, was der Analysator bedeutet, schauen wir uns die Parameter der
photo_fastNlMeansDenoisingMulti- Methode an:
public static extern void photo_fastNlMeansDenoisingMulti( IntPtr[] srcImgs, int srcImgsLength, IntPtr dst, int imgToDenoiseIndex, int temporalWindowSize, float h, int templateWindowSize, int searchWindowSize)
Vereinfachen Sie noch mehr, um es sehr deutlich zu machen. Vergleichen Sie diese Zeilen:
NativeMethods.photo_fastNlMeansDenoisingMulti( .... templateWindowSize, .... templateWindowSize, ....); public static extern void photo_fastNlMeansDenoisingMulti( .... int temporalWindowSize, .... int templateWindowSize, ....)
Der Analysator macht darauf aufmerksam, dass der Entwickler die Variable
templateWindowSize zweimal verwendet hat, obwohl
temporalWindowSize höchstwahrscheinlich anstelle der ersten Erwähnung dieser Variablen verwendet werden sollte. Es ist auch verdächtig, dass der Wert von
temporalWindowSize in der
photo_fastNlMeansDenoisingMulti- Methode überhaupt nicht verwendet wird. Vielleicht wurde dies absichtlich gemacht, aber anstelle der Entwickler lohnt es sich, diesen Code genauer zu betrachten. Ist dort ein Fehler eingeschlichen?
Ähnliche Warnungen des Analysators:
- V3038 Das Argument wurde mehrmals an method übergeben. Es ist möglich, dass stattdessen ein anderes Argument übergeben wird. Cv2_photo.cs 149
- V3038 Das Argument wurde mehrmals an method übergeben. Es ist möglich, dass stattdessen ein anderes Argument übergeben wird. Cv2_photo.cs 180
- V3038 Das Argument wurde mehrmals an method übergeben. Es ist möglich, dass stattdessen ein anderes Argument übergeben wird. Cv2_photo.cs 205
Der folgende Fehler ähnelt dem vorherigen.
Warnung PVS-Studio :
V3066 Möglicherweise falsche Reihenfolge der an die Methode 'calib3d_Rodrigues_MatToVec' übergebenen Argumente: 'matrixM.CvPtr' und 'vectorM.CvPtr'. Cv2_calib3d.cs 86
public static void Rodrigues(double[,] matrix, out double[] vector, out double[,] jacobian) { .... using (var jacobianM = new Mat<double>()) { NativeMethods.calib3d_Rodrigues_MatToVec (matrixM.CvPtr, vectorM.CvPtr, jacobianM.CvPtr); .... } }
Schauen wir uns die Parameter
calib3d_Rodrigues_MatToVec an public static extern void calib3d_Rodrigues_MatToVec( IntPtr vector, IntPtr matrix, IntPtr jacobian)
Möglicherweise wurden beim Aufrufen der Methode
calib3d_Rodrigues_MatToVec die Argumente
matrixM.CvPtr und
vectorM.CvPtr verwechselt . Entwickler sollten sich diesen Code genauer ansehen. Es besteht die Möglichkeit, dass sich ein Fehler eingeschlichen hat, der die korrekten Berechnungen beeinträchtigt.
PVS-Studio Warnung :
V3063 Ein Teil des bedingten Ausdrucks ist immer falsch, wenn er ausgewertet wird: data == null. Mat.cs 3539
private void CheckArgumentsForConvert(....) { .... if (data == null) throw new ArgumentNullException(nameof(data)); MatType t = Type(); if (data == null || (data.Length * dataDimension)
Der Analysator zeigt an, dass die zweiten Prüfdaten
== null niemals
wahr sein werden , weil Wenn in der ersten Bedingung die
Daten null sind , wird eine Ausnahme ausgelöst und die Programmausführung erreicht die zweite Prüfung nicht.
Ich verstehe, dass Sie bereits müde sind, aber nur noch sehr wenig übrig ist.PVS-Studio Warnung :
V3127 Es wurden zwei ähnliche Codefragmente gefunden. Möglicherweise ist dies ein Tippfehler, und anstelle von 'src2' Cv2_imgproc.cs 1547 sollte die Variable 'window' verwendet werden
public static Point2d PhaseCorrelateRes(....) { if (src1 == null) throw new ArgumentNullException(nameof(src1)); if (src2 == null) throw new ArgumentNullException(nameof(src2)); if (window == null) throw new ArgumentNullException(nameof(src2));
Und dann fand der Analysator einen Tippfehler. In diesem Abschnitt des Codes wird der Wert der Variablen auf
Null überprüft. Wenn diese Bedingung erfüllt ist, wird für jede der Variablen eine Ausnahme ausgelöst. Die
Fenstervariable ist jedoch nicht so einfach. Wenn diese Variable
null ist , wird auch eine Ausnahme für sie generiert, aber der Text dieser Ausnahme wird falsch geschrieben. Das Variablenfenster selbst wird im Text dieser Ausnahme
nicht angezeigt, stattdessen wird dort
src2 angezeigt. Anscheinend sollte die letzte Bedingung so sein:
if (window == null) throw new ArgumentNullException(nameof(window));
PVS-Studio Warnung :
V3142 Nicht erreichbarer Code erkannt. Möglicherweise liegt ein Fehler vor. MatOfT.cs 873
Betrachten wir zur Abwechslung den Fall, in dem der Analysator tatsächlich absolut richtig ist, wenn ein nicht erreichbarer Code gemeldet wird, aber kein Fehler vorliegt. Dies ist der Fall, wenn gesagt werden kann, dass der Analysator gleichzeitig eine Warnung generiert, die sowohl richtig als auch falsch ist.
public new Mat<TElem> SubMat(params Range[] ranges) { Mat result = base.SubMat(ranges); return Wrap(result); }
Der Analysator behauptet, dass die
return-Anweisung hier nicht erreichbar ist. Schauen Sie sich dazu den Hauptteil der
SubMat- Methode an.
public Mat SubMat(params Range[] ranges) { throw new NotImplementedException(); }
Wie Sie sehen, wurde die Funktion noch nicht hinzugefügt und löst immer eine Ausnahme aus. Und der Analysator hat Recht, wenn er einen nicht erreichbaren Code meldet. Dies kann jedoch nicht als echter Fehler bezeichnet werden.
Die folgenden drei vom Analysator gefundenen Fehler sind vom gleichen Typ, aber sie sind so cool, dass ich nicht anders konnte, als sie alle aufzuschreiben.
PVS-Studio- Warnung :
V3022- Ausdruck 'String.IsNullOrEmpty ("winName")' ist immer falsch. Cv2_highgui.cs 46
public static void DestroyWindow(string winName) { if (String.IsNullOrEmpty("winName")) .... }
PVS-Studio- Warnung :
V3022 Der Ausdruck 'string.IsNullOrEmpty ("fileName")' ist immer falsch. FrameSource.cs 37
public static FrameSource CreateFrameSource_Video(string fileName) { if (string.IsNullOrEmpty("fileName")) .... }
PVS-Studio- Warnung :
V3022 Der Ausdruck 'string.IsNullOrEmpty ("fileName")' ist immer falsch. FrameSource.cs 53
public static FrameSource CreateFrameSource_Video_CUDA(string fileName) { if (string.IsNullOrEmpty("fileName")) .... }
Manchmal gibt es hinter der Warnung des
V3022- Analysators (der Ausdruck ist immer wahr / falsch) wirklich seltsame oder lustige Dinge. In allen drei Fällen wird die gleiche Situation beobachtet. Der Methodencode hat einen Parameter vom Typ
string , dessen Wert überprüft werden muss. Es wird jedoch nicht der Wert der Variablen überprüft, sondern das Zeichenfolgenliteral mit seinem Namen, d. H. der Name wird vergeblich zitiert.
Anscheinend hat der Entwickler einmal versiegelt und diesen Fehler mithilfe von Copy-Paste per Code weitergegeben.
Fazit
OpenCvSharp-Entwickler haben wichtige und großartige Arbeit geleistet. Und ich als Benutzer dieser Bibliothek bin ihnen sehr dankbar. Vielen Dank.
Da ich jedoch im PVS-Studio-Team bin und mir den Code anschaue, muss ich zugeben, dass das Problem seiner Qualität nicht ausreichend gelöst wurde. In diesem Projekt werden statische Codeanalysatoren höchstwahrscheinlich nicht regelmäßig verwendet. Und viele Fehler werden durch teurere Methoden korrigiert (z. B. Tests gemäß Benutzerbewertungen). Und einige bleiben im Allgemeinen noch lange im Code, und wir finden sie einfach. Diese Idee wird in einer kurzen
Anmerkung zum Thema der Philosophie der Verwendung der Methodik der statischen Analyse ausführlicher vorgestellt.
Da das Projekt offen ist und sich auf GitHub befindet, haben die Entwickler die Möglichkeit, die
kostenlose Lizenzierungsoption PVS-Studio zu nutzen und die Analyse regelmäßig anzuwenden.
Vielen Dank für Ihre Aufmerksamkeit.
Laden Sie Ihre Projekte mit der Testversion von PVS-Studio
herunter und testen Sie sie.

Wenn Sie diesen Artikel einem englischsprachigen Publikum zugänglich machen möchten, verwenden Sie bitte den Link zur Übersetzung: Ekaterina Nikiforova.
Überprüfen des OpenCvSharp Wrapper auf OpenCV mit PVS-Studio .