OpenCV هي مكتبة تحتوي على خوارزميات رؤية الكمبيوتر ومعالجة الصور ولوغاريتمات رقمية للأغراض العامة مع مصدر مفتوح ومألوف للعديد من مطوري C ++. بالإضافة إلى C ++ ، يتم تطوير OpenCV أيضًا للبيثون ، جافا ، روبي ، ماتلاب ، لوا ، ولغات أخرى. نظرًا لعدم وجود الرئيسي لـ C ، بين هذه اللغات ، فقد قررت الانتباه إلى مكتبة مجمّع OpenCvSharp ضمن C # والتحقق من هذا المشروع. ما جاء من هذا يمكن العثور عليها في هذه المقالة.
مقدمة
في وقت سابق ، قبل انضمامي إلى PVS-Studio ، شاركت في الروبوتات في المعارض. تضمنت مهماتي الإصلاح الأساسي (في حالة حدوث انهيار كبير ، تم منح الروبوت لشخص آخر) ، بالإضافة إلى تطوير مجموعة واسعة من البرامج والأدوات المساعدة.
تعبت وصلت مؤخراً إلى مدينة جديدة ، وأنا مع روبوت KIKI غير المعبأ حديثًا.الحديث عن التنمية. كان هذا مضحك جدا. في كل مرة يتم فيها تقديم فكرة لشخص ما من الفريق ، ما الذي سيفاجئ ضيوف المعارض ، نطرح هذه المسألة للمناقشة العامة ، وإذا كانت الفكرة جيدة ، فقد أخذناها للتنفيذ. مرة واحدة ، جاءت الفكرة إلينا لعمل مخلوق يستجيب لوجه إنساني مع خطاب الترحيب.
بعد البحث في الإنترنت عن مكتبة لاحتياجاتي ، صادفت موقع OpenCV على الويب ، ومكتبات خوارزميات رؤية الكمبيوتر. شعرت بخيبة أمل قريبًا - تم تطبيق OpenCV في الإصدار C ++. من الواضح أن معرفتي بالمزايا التي حصلت عليها من الكلية لم تكن كافية. لذلك ، من خلال googling لفترة وجيزة ، صادفت OpenCvSharp - غلاف هذه المكتبة تحت C # ، لغتي الرئيسية. لقد انقضت ستة أشهر منذ ذلك الحين ، وقد تم بالفعل كتابة البرنامج واستخدامه لفترة طويلة ، وقررت الدخول تحت غطاء OpenCvSharp والتحقق من الكود المصدر الخاص به باستخدام محلل ثابت PVS-Studio.
مشروع مدقق
OpenCvSharp عبارة عن مجمّع على OpenCV لاستخدام المكتبة في مشاريع C #. مكتبة OpenCV ، بالمناسبة ،
فحصنا أيضًا . تشتمل ميزات OpenCvSharp على مجموعة كبيرة من نماذج الأكواد ، والنظام الأساسي (يمكن أن يعمل على أي نظام أساسي تدعمه Mono) وسهولة التثبيت.
الغلاف هو مشروع صغير ويحتوي على حوالي 112200 سطر من كود C #. 1.2٪ من هذه التعليقات ، والتي ، بالمناسبة ، صغيرة بشكل مثير للريبة. ولكن لمثل هذا المشروع الصغير هناك الكثير من الأخطاء. كتبت في المقال أكثر من 20 خطأ ، لكن كان هناك أخطاء أخرى لم تكن مثيرة للاهتمام أو واضحة.
PVS-Studio محلل الكود
PVS-Studio هي أداة للكشف عن الأخطاء ونقاط الضعف المحتملة في الكود المصدري للبرامج المكتوبة بلغات C و C ++ و C # و Java. يعمل على أنظمة التشغيل Windows و Linux و macOS. بالإضافة إلى الأكواد والأخطاء المطبعية غير القابلة للتحقيق ، يمكن لـ PVS-Studio اكتشاف نقاط الضعف المحتملة ، كما هو مذكور أعلاه. لذلك ، يمكن اعتباره وسيلة لاختبار أمان التطبيق الثابت (اختبار أمان التطبيق الثابت ، SAST).
أجزاء من التعليمات البرمجية التي لفتت الانتباه عند فحص تقرير محلل
تجذب طريقة
WriteableBitmapConverter الانتباه على الفور مع أربعة من نفس النوع من تحذيرات PVS-Studio:
- V3005 يتم تخصيص المتغير "optimumChannels [PixelFormats.Indexed1]" لنفسه. WriteableBitmapConverter.cs 22
- V3005 يتم تخصيص المتغير "optimumChannels [PixelFormats.Indexed8]" لنفسه. WriteableBitmapConverter.cs 23
- V3005 يتم تعيين متغير "optimumTypes [PixelFormats.Indexed1]" لنفسه. WriteableBitmapConverter.cs 50
- V3005 يتم تعيين متغير "optimumTypes [PixelFormats.Indexed8]" لنفسه. WriteableBitmapConverter.cs 51
static WriteableBitmapConverter() { optimumChannels = new Dictionary <PixelFormat, int>(); optimumChannels[PixelFormats.Indexed1] =
يتم تعريف فئة
PixelFormats في
مساحة اسم System.Windows.Media وهي عبارة عن مجموعة من تنسيقات البكسل المختلفة. يسترعي المحلل الانتباه إلى حقيقة أن أسلوب
WriteableBitmapConverter يعيد تعيين القيم إلى
القنوات المثلى [PixelFormats.Indexed1] وعناصر
optimumChannels [PixelFormats.Indexed8] ، مما لا معنى له من الناحية العملية. من غير الواضح ما إذا كان هذا خطأ مطبعي بسيط أم كان المقصود بشيء آخر. بالمناسبة ، يوضح هذا القسم من التعليمات البرمجية بوضوح فوائد أجهزة التحليل الثابتة. عندما ترى مجموعة من الخطوط من نفس النوع أمام عينيك ، تبدأ عينيك في "التمويه" ، ويتبدد انتباهك - ليس من المستغرب إذا ، حتى بعد مراجعة الكود ، تزحف الخطأ المطبعي في البرنامج. وليس للمحلل الساكن أي مشاكل في الانتباه ولا يحتاج إلى الراحة ، وبالتالي فإنه من الأسهل عليه العثور على مثل هذه الأخطاء.
تشعر بقوة وقوة التحليل الثابت.تحذير PVS-Studio :
V3021 هناك
بيانان "if" مع تعبيرات شرطية متطابقة. تحتوي العبارة "if" الأولى على طريقة إرجاع. هذا يعني أن العبارة 'if' الثانية لا معنى لها 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))
هذا الخطأ يشبه إلى حد ما الخطأ السابق. قدم المبرمج موقفًا يتم فيه فحص نفس الحالة مرتين. في هذه الحالة ، هذا غير منطقي -
إذًا لن يتم تنفيذ فرع "التكرار"
إذا لم يتم تنفيذ البيان ، حيث:
- إذا كان التعبير الشرطي الأول صحيحًا ، فستخرج الطريقة ؛
- إذا كان الشرط الأول خاطئًا ، فسيكون الثاني خاطئًا أيضًا ، لأن المتغير الذي يتم اختباره - t - لا يتغير بين التعبيرات الشرطية.
يجب على المطور التحقق من هذا الرمز. من المحتمل أنه بدلاً من المتغير الثاني يجب أن يكون
Vec2s آخر.
تحذير PVS-Studio :
V3010 يجب استخدام قيمة الإرجاع للدالة 'ToString'. 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(); }
إرجاع الأسلوب
RotatedRectangleIntersection صفيف عناصر
Point2f خلال المعلمة
intersectingRegion . بعد أن يملأ البرنامج
intersectingRegion بالقيم ، يتم استدعاء الأسلوب
ToString () على هذا الصفيف
. مع عناصر المصفوفة ، لا تحدث أي تغييرات من هذا ولا يتم القيام بأي عمل مفيد في السطر الأخير ، وبالتالي هناك سبب للشك في أن المطور قد نسي إزالته.
تحذيرات PVS-Studio :
- V3021 هناك بيانان "if" مع تعبيرات شرطية متطابقة. تحتوي العبارة "if" الأولى على طريقة إرجاع. هذا يعني أن العبارة "if" الثانية لا معنى لها Cv2_calib3d.cs 1370
- تعبير V3022 'objectPoints == null' غير صحيح دائمًا. Cv2_calib3d.cs 1372
public static double CalibrateCamera(....) { if (objectPoints == null) throw new ArgumentNullException(nameof(objectPoints)); if (objectPoints == null) throw new ArgumentNullException(nameof(objectPoints)); .... }
في هذه الحالة ، تم تكرار جزء من الشفرة ، بسبب ظهور تحذيرين. يشير التحذير الأول إلى أنه
إذا كانت البيانات لها نفس التعبير الشرطي. إذا كان هذا التعبير صحيحًا ،
فستخرج الطريقة من فرع
وقتها في أول
عبارة if . لهذا السبب ، سيكون الشرط الثاني خاطئًا دائمًا ، كما هو موضح في التحذير التالي. على ما يبدو ، تم نسخ النص ، ولكن نسيت تصحيحه.
لطيف نسخ لصق.تحذيرات محلل أخرى مماثلة:
- V3021 هناك بيانان "if" مع تعبيرات شرطية متطابقة. تحتوي العبارة "if" الأولى على طريقة إرجاع. هذا يعني أن العبارة "if" الثانية لا معنى لها Cv2_calib3d.cs 1444
- تعبير V3022 'objectPoints == null' غير صحيح دائمًا. Cv2_calib3d.cs 1446
تحذير PVS-Studio : تعبير
V3022 'label == MarkerValue' غير صحيح دائمًا. 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;
في هذا القسم من التعليمات البرمجية ، يتم إنشاء متغير
تسمية صفر. عند استيفاء شرط معين ، يمكن إضافة واحد إلى هذا المتغير. في هذه الحالة ، في التعليمات البرمجية ، لا تتغير قيمة
التسمية المتغيرة لأسفل. في السطر المحدد بسهم ، تتم مقارنة هذا المتغير بثبات يساوي -1 ، مما لا معنى له من الناحية العملية.
تحذير PVS-Studio :
V3038 تم تمرير الوسيطة إلى الطريقة عدة مرات. من الممكن أن يتم تمرير وسيطة أخرى بدلاً من ذلك. Cv2_photo.cs 124
public static void FastNlMeansDenoisingMulti(....) { .... NativeMethods.photo_fastNlMeansDenoisingMulti( srcImgPtrs, srcImgPtrs.Length, dst.CvPtr, imgToDenoiseIndex, templateWindowSize, h, templateWindowSize, searchWindowSize); .... }
لفهم ما يعنيه المحلل ، دعنا ننظر إلى معلمات طريقة
photo_fastNlMeansDenoisingMulti :
public static extern void photo_fastNlMeansDenoisingMulti( IntPtr[] srcImgs, int srcImgsLength, IntPtr dst, int imgToDenoiseIndex, int temporalWindowSize, float h, int templateWindowSize, int searchWindowSize)
تبسيط أكثر لجعلها واضحة للغاية. قارن هذه الخطوط:
NativeMethods.photo_fastNlMeansDenoisingMulti( .... templateWindowSize, .... templateWindowSize, ....); public static extern void photo_fastNlMeansDenoisingMulti( .... int temporalWindowSize, .... int templateWindowSize, ....)
يسترعي المحلل الانتباه إلى أن المطور استخدم المتغير
templateWindowSize مرتين ، على الرغم من أنه على الأرجح ، يجب أن يكون
temporalWindowSize في مكان أول ذكر لهذا المتغير. من المشكوك فيه أيضًا أن قيمة
temporalWindowSize في طريقة
photo_fastNlMeansDenoisingMulti لا تُستخدم على الإطلاق. ربما تم ذلك عن عمد ، ولكن بدلاً من المطورين ، يجدر بنا إلقاء نظرة فاحصة على هذا الرمز ، هل هناك خطأ في التسلل إلى هناك؟
تحذيرات محلل مماثلة:
- V3038 تم تمرير الوسيطة إلى الطريقة عدة مرات. من الممكن أن يتم تمرير وسيطة أخرى بدلاً من ذلك. Cv2_photo.cs 149
- V3038 تم تمرير الوسيطة إلى الطريقة عدة مرات. من الممكن أن يتم تمرير وسيطة أخرى بدلاً من ذلك. Cv2_photo.cs 180
- V3038 تم تمرير الوسيطة إلى الطريقة عدة مرات. من الممكن أن يتم تمرير وسيطة أخرى بدلاً من ذلك. Cv2_photo.cs 205
الخطأ التالي سيكون مشابهًا للخطأ السابق.
تحذير PVS-Studio :
V3066 تم تمرير الترتيب غير الصحيح المحتمل للوسيطات إلى طريقة 'calib3d_Rodrigues_MatToVec': 'matrixM.CvPtr' و '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); .... } }
دعونا نلقي نظرة على المعلمات
calib3d_Rodrigues_MatToVec public static extern void calib3d_Rodrigues_MatToVec( IntPtr vector, IntPtr matrix, IntPtr jacobian)
ربما عند استدعاء الأسلوب
calib3d_Rodrigues_MatToVec ، تم خلط الوسائط
matrixM.CvPtr و
vectorM.CvPtr . يجب على المطورين إلقاء نظرة فاحصة على هذا الرمز. هناك احتمال أن خطأ تسلل إلى أن يتداخل مع الحسابات الصحيحة.
تحذير PVS-Studio :
V3063 جزء من التعبير الشرطي دائمًا ما يكون خاطئًا إذا تم تقييمه: 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)
يشير المحلل إلى أن
بيانات الفحص الثانية
== فارغة لن تكون أبدًا
صحيحة ، لأن إذا كانت
بيانات الشرط الأول
خالية ، فسيتم طرح استثناء ، ولن يصل تنفيذ البرنامج إلى الفحص الثاني.
أنا أفهم أنك متعب بالفعل ، ولكن لم يتبق سوى القليل جدًا.PVS-Studio Warning :
V3127 تم العثور على شظايا رمز مماثلة. ربما ، هذا خطأ مطبعي ويجب استخدام متغير "window" بدلاً من "src2" Cv2_imgproc.cs 1547
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));
ثم عثر المحلل على خطأ مطبعي. في هذا القسم من الكود ، يتم التحقق من قيمة المتغيرات
لإلغاءها ، وإذا تم استيفاء هذا الشرط ، يتم طرح استثناء لكل من المتغيرات. ومع ذلك ، فإن متغير
النافذة ليست بهذه البساطة. إذا كان هذا المتغير
فارغًا ، فسيتم أيضًا إنشاء استثناء له ، ولكن يتم كتابة نص هذا الاستثناء بشكل غير صحيح. لا تظهر
نافذة المتغير نفسها في نص هذا الاستثناء ؛ بدلاً من ذلك ، تتم الإشارة إلى
src2 هناك . على ما يبدو ، يجب أن يكون الشرط الأخير مثل هذا:
if (window == null) throw new ArgumentNullException(nameof(window));
تحذير PVS-Studio :
تم اكتشاف رمز غير
قابل للوصول
V3142 . من الممكن وجود خطأ. MatOfT.cs 873
الآن ، من أجل التغيير ، دعونا نلقي نظرة على الحالة التي يكون فيها المحلل على حق تمامًا عند الإبلاغ عن رمز يتعذر الوصول إليه ، ولكن لا يوجد خطأ. هذا هو الحال عندما يمكن القول أن المحلل يولد تحذيرًا صحيحًا وكاذبًا في نفس الوقت.
public new Mat<TElem> SubMat(params Range[] ranges) { Mat result = base.SubMat(ranges); return Wrap(result); }
يدعي المحلل أن
بيان الإرجاع غير قابل للوصول هنا. للتحقق من ذلك ، انظر إلى
نص أسلوب
SubMat .
public Mat SubMat(params Range[] ranges) { throw new NotImplementedException(); }
كما ترون ، لم تتم إضافة الوظيفة بعد ورمت دائمًا استثناء. ويكون المحلل على حق عند الإبلاغ عن رمز يتعذر الوصول إليه. ولكن هذا لا يمكن أن يسمى خطأ حقيقيا.
الأخطاء الثلاثة التالية التي وجدها المحلل هي من نفس النوع ، لكنها باردة لدرجة أنني لم أتمكن من المساعدة في كتابتها كلها.
تحذير PVS-Studio : تعبير
V3022 'String.IsNullOrEmpty ("winName")' خطأ دائمًا. Cv2_highgui.cs 46
public static void DestroyWindow(string winName) { if (String.IsNullOrEmpty("winName")) .... }
تحذير PVS-Studio : تعبير
V3022 'string.IsNullOrEmpty ("fileName")' خطأ دائمًا. FrameSource.cs 37
public static FrameSource CreateFrameSource_Video(string fileName) { if (string.IsNullOrEmpty("fileName")) .... }
تحذير PVS-Studio : تعبير
V3022 'string.IsNullOrEmpty ("fileName")' خطأ دائمًا. FrameSource.cs 53
public static FrameSource CreateFrameSource_Video_CUDA(string fileName) { if (string.IsNullOrEmpty("fileName")) .... }
في بعض الأحيان وراء تحذير محلل
V3022 (التعبير صحيح / خطأ دائمًا) توجد أشياء غريبة أو مضحكة حقًا. في جميع الحالات الثلاث ، لوحظ نفس الوضع. يحتوي رمز الطريقة على معلمة من
سلسلة type ، يجب التحقق من قيمتها. ومع ذلك ، لا يتم تحديد قيمة المتغير ، ولكن السلسلة الحرفية مع اسمه ، أي الاسم عبثا نقلت.
على ما يبدو ، قام المطور بإغلاق مرة واحدة ، وباستخدام نسخ ولصق ، نشر هذا الخطأ عن طريق الرمز.
استنتاج
قام مطورو OpenCvSharp بعمل مهم وعظيم. وأنا ، بصفتي مستخدمًا لهذه المكتبة ، ممتن جدًا لهم. شكرا لك
ومع ذلك ، نظرًا لأنني في فريق PVS-Studio ونظرًا إلى الكود ، يجب أن أعترف بأن مسألة جودته لم يتم حلها بشكل كافٍ. على الأرجح ، لا يتم استخدام محلل الكود الثابت بشكل منتظم في هذا المشروع. ويتم تصحيح العديد من الأخطاء بطرق أكثر تكلفة (الاختبار ، وفقًا لمراجعات المستخدم ، على سبيل المثال). ويبقى البعض بشكل عام للعيش لفترة طويلة في الكود ، ونجدهم فقط. يتم تقديم هذه الفكرة بمزيد من التفصيل في
مذكرة قصيرة حول موضوع فلسفة استخدام منهجية التحليل الثابت.
نظرًا لأن المشروع مفتوح وموجود على GitHub ، فإن مطوريه لديهم الفرصة للاستفادة من
خيار الترخيص المجاني PVS-Studio وبدء تطبيق التحليل على أساس منتظم.
شكرا لاهتمامكم
قم بتنزيل واختبار مشاريعك باستخدام الإصدار التجريبي من PVS-Studio.

إذا كنت ترغب في مشاركة هذه المقالة مع جمهور يتحدث الإنجليزية ، فالرجاء استخدام الرابط الخاص بالترجمة: Ekaterina Nikiforova.
التحقق من التفاف OpenCvSharp بحثًا عن OpenCV باستخدام PVS-Studio .