برنامج OpenCV التعليمي: اكتشاف الكائنات في الوقت الفعلي باستخدام MSER في iOS
نشرت: 2022-03-11على مدى السنوات القليلة الماضية ، زاد متوسط أداء الهاتف المحمول بشكل ملحوظ. سواء كان ذلك من أجل القدرة الحصانية لوحدة المعالجة المركزية أو سعة ذاكرة الوصول العشوائي (RAM) ، فقد أصبح من الأسهل الآن القيام بمهام حسابية ثقيلة على الأجهزة المحمولة. على الرغم من أن تقنيات الأجهزة المحمولة هذه تسير في الاتجاه الصحيح ، إلا أنه لا يزال هناك الكثير الذي يتعين القيام به على منصات الأجهزة المحمولة ، خاصة مع ظهور الواقع المعزز والواقع الافتراضي والذكاء الاصطناعي.
يتمثل التحدي الرئيسي في رؤية الكمبيوتر في اكتشاف الأشياء ذات الأهمية في الصور. تقوم العين والعقل البشريان بعمل استثنائي ، ولا يزال تكرار ذلك في الآلات حلما. على مدى العقود الأخيرة ، تم تطوير مناهج لتقليد هذا في الآلات ، وهو يتحسن.
في هذا البرنامج التعليمي ، سوف نستكشف الخوارزمية المستخدمة في اكتشاف النقاط في الصور. سنستخدم أيضًا الخوارزمية ، من مكتبة مفتوحة المصدر ، OpenCV ، لتنفيذ نموذج أولي لتطبيق iPhone يستخدم الكاميرا الخلفية للحصول على الصور واكتشاف الأشياء بداخلها.
برنامج OpenCV التعليمي
OpenCV هي مكتبة مفتوحة المصدر توفر تطبيقات للرؤية الحاسوبية الرئيسية وخوارزميات التعلم الآلي. إذا كنت ترغب في تنفيذ تطبيق لاكتشاف الوجوه أو لعب الورق على طاولة البوكر أو حتى تطبيق بسيط لإضافة تأثيرات على صورة عشوائية ، فإن OpenCV يعد خيارًا رائعًا.
OpenCV مكتوب بلغة C / C ++ ، ويحتوي على مكتبات مجمعة لجميع الأنظمة الأساسية الرئيسية. هذا يجعله سهل الاستخدام بشكل خاص في بيئة iOS. لاستخدامه في تطبيق Objective-C iOS ، قم بتنزيل OpenCV iOS Framework من الموقع الرسمي. من فضلك ، تأكد من أنك تستخدم الإصدار 2.4.11 من OpenCV لنظام iOS (الذي تفترض هذه المقالة أنك تستخدمه) ، حيث أن أحدث إصدار ، 3.0 ، به بعض التغييرات التي تكسر التوافق في كيفية تنظيم ملفات الرأس. تم توثيق المعلومات التفصيلية حول كيفية تثبيته على موقعه على الإنترنت.
MSER
MSER ، اختصار لـ Maximally Stable Extremal areas ، هي إحدى الطرق العديدة المتاحة لاكتشاف blob داخل الصور. بكلمات بسيطة ، تحدد الخوارزمية مجموعات متجاورة من وحدات البكسل التي تكون كثافة بكسلات الحدود الخارجية أعلى (بعتبة معينة) من شدة البكسل للحد الداخلي. يقال أن هذه المناطق مستقرة إلى أقصى حد إذا لم تتغير كثيرًا على قدر متفاوت من الشدة.
على الرغم من وجود عدد من خوارزميات اكتشاف blob الأخرى ، فقد تم اختيار MSER هنا لأنه يحتوي على تعقيد وقت تشغيل خفيف إلى حد ما لـ O (n log (log (n))) حيث n هو العدد الإجمالي للبكسل على الصورة. تعد الخوارزمية أيضًا قوية للتعتيم والتوسع ، وهو أمر مفيد عندما يتعلق الأمر بمعالجة الصور التي تم الحصول عليها من خلال مصادر الوقت الفعلي ، مثل كاميرا الهاتف المحمول.
لغرض هذا البرنامج التعليمي ، سنقوم بتصميم التطبيق لاكتشاف شعار Toptal. يحتوي الرمز على زوايا حادة ، وقد يؤدي ذلك إلى التفكير في مدى فعالية خوارزميات اكتشاف الزوايا في اكتشاف شعار Toptal. بعد كل شيء ، هذه الخوارزمية سهلة الاستخدام والفهم. على الرغم من أن الأساليب القائمة على الزاوية قد يكون لها معدل نجاح مرتفع عندما يتعلق الأمر باكتشاف الكائنات المنفصلة بوضوح عن الخلفية (مثل الكائنات السوداء على خلفيات بيضاء) ، سيكون من الصعب تحقيق الاكتشاف في الوقت الفعلي لشعار Toptal في العالم الحقيقي الصور ، حيث تكتشف الخوارزمية باستمرار مئات الزوايا.
إستراتيجية
لكل إطار من الصور التي يكتسبها التطبيق من خلال الكاميرا ، يتم تحويله أولاً إلى التدرج الرمادي. تحتوي الصور ذات التدرج الرمادي على قناة ألوان واحدة فقط ، ولكن الشعار سيكون مرئيًا مع ذلك. هذا يسهل على الخوارزمية التعامل مع الصورة ويقلل بشكل كبير من كمية البيانات التي يتعين على الخوارزمية معالجتها لتحقيق مكاسب قليلة أو معدومة.
بعد ذلك ، سوف نستخدم تطبيق OpenCV's الخوارزمية لاستخراج جميع MSERs. بعد ذلك ، سيتم تسوية كل MSER عن طريق تحويل مستطيل الحد الأدنى إلى مربع. هذه الخطوة مهمة لأنه يمكن الحصول على الشعار من زوايا ومسافات مختلفة وهذا سيزيد من تحمل تشويه المنظور.
علاوة على ذلك ، يتم حساب عدد من الخصائص لكل MSER:
- عدد الثقوب
- نسبة مساحة MSER إلى مساحة بدنها المحدب
- نسبة مساحة MSER إلى مساحة مستطيل المنطقة الدنيا
- نسبة طول الهيكل العظمي MSER إلى منطقة MSER
- نسبة مساحة MSER إلى مساحة أكبر محيط لها
من أجل اكتشاف شعار Toptal في صورة ما ، تتم مقارنة خصائص جميع MSERs بخصائص شعار Toptal التي تم تعلمها بالفعل. لغرض هذا البرنامج التعليمي ، تم اختيار الحد الأقصى للاختلافات المسموح بها لكل خاصية بشكل تجريبي.
أخيرًا ، يتم اختيار المنطقة الأكثر تشابهًا كنتيجة.
تطبيق iOS
يعد استخدام OpenCV من iOS أمرًا سهلاً. إذا لم تكن قد قمت بذلك حتى الآن ، فإليك موجزًا سريعًا للخطوات المتضمنة في إعداد Xcode لإنشاء تطبيق iOS واستخدام OpenCV فيه:
أنشئ اسم مشروع جديد "SuperCool Logo Detector". كلغة ، اترك Objective-C محددًا.
أضف ملف Prefix Header (.pch) الجديد وقم بتسميته PrefixHeader.pch
انتقل إلى مشروع بناء الهدف "SuperCool Logo Detector" وفي علامة التبويب "إعدادات البناء" ، ابحث عن إعداد "رؤوس البادئة". يمكنك العثور عليها في قسم لغة LLVM ، أو استخدام ميزة البحث.
أضف "PrefixHeader.pch" إلى إعداد رؤوس البادئة
في هذه المرحلة ، إذا لم تكن قد قمت بتثبيت OpenCV لنظام التشغيل iOS 2.4.11 ، فقم بذلك الآن.
قم بسحب وإفلات إطار العمل الذي تم تنزيله في المشروع. تحقق من "الأطر والمكتبات المرتبطة" في إعدادات الهدف. (يجب إضافته تلقائيًا ، ولكن من الأفضل أن يكون آمنًا.)
بالإضافة إلى ذلك ، قم بربط الأطر التالية:
- مؤسسة AVFoundation
- الأصول
- CoreMedia
افتح "PrefixHeader.pch" وأضف الأسطر الثلاثة التالية:
#ifdef __cplusplus #include <opencv2/opencv.hpp> #endif”
قم بتغيير امتدادات ملفات التعليمات البرمجية التي تم إنشاؤها تلقائيًا من “ .m” إلى “ .mm”. OpenCV مكتوب بلغة C ++ ومع * .mm تقول أنك ستستخدم Objective-C ++.
قم باستيراد “opencv2 / highgui / cap_ios.h” في ViewController.h وقم بتغيير ViewController ليتوافق مع بروتوكول CvVideoCameraDelegate:
#import <opencv2/highgui/cap_ios.h>
افتح Main.storyboard وضع UIImageView على وحدة تحكم العرض الأولية.
إنشاء منفذ لـ ViewController.mm باسم "imageView"
إنشاء متغير "كاميرا CvVideoCamera * ؛" في ViewController.h أو ViewController.mm ، وقم بتهيئته بالإشارة إلى الكاميرا الخلفية:
camera = [[CvVideoCamera alloc] initWithParentView: _imageView]; camera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack; camera.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480; camera.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait; camera.defaultFPS = 30; camera.grayscaleMode = NO; camera.delegate = self;
إذا قمت ببناء المشروع الآن ، فسوف يحذرك Xcode من أنك لم تنفذ طريقة "processImage" من CvVideoCameraDelegate. في الوقت الحالي ، ومن أجل البساطة ، سنحصل فقط على الصور من الكاميرا ونغلفها بنص بسيط:
- أضف سطرًا واحدًا إلى "viewDidAppear":
[camera start];
الآن ، إذا قمت بتشغيل التطبيق ، فسيطلب منك إذنًا للوصول إلى الكاميرا. وبعد ذلك يجب أن تشاهد الفيديو من الكاميرا.
في طريقة "processImage" أضف السطرين التاليين:
const char* str = [@"Toptal" cStringUsingEncoding: NSUTF8StringEncoding]; cv::putText(image, str, cv::Point(100, 100), CV_FONT_HERSHEY_PLAIN, 2.0, cv::Scalar(0, 0, 255));
هذا هو الى حد كبير ذلك. الآن لديك تطبيق بسيط للغاية يقوم برسم النص "Toptal" على الصور من الكاميرا. يمكننا الآن بناء تطبيق الكشف عن الشعار المستهدف من هذا التطبيق الأبسط. للإيجاز ، سنناقش في هذه المقالة عددًا قليلاً فقط من مقاطع التعليمات البرمجية التي تعتبر بالغة الأهمية لفهم كيفية عمل التطبيق بشكل عام. يحتوي الكود الموجود على GitHub على قدر معقول من التعليقات لشرح ما يفعله كل جزء.
نظرًا لأن التطبيق له غرض واحد فقط ، وهو اكتشاف شعار Toptal ، فبمجرد إطلاقه ، يتم استخراج ميزات MSER من صورة القالب المحددة ويتم تخزين القيم في الذاكرة:
cv::Mat logo = [ImageUtils cvMatFromUIImage: templateImage]; //get gray image cv::Mat gray; cvtColor(logo, gray, CV_BGRA2GRAY); //mser with maximum area is std::vector<cv::Point> maxMser = [ImageUtils maxMser: &gray]; //get 4 vertices of the maxMSER minrect cv::RotatedRect rect = cv::minAreaRect(maxMser); cv::Point2f points[4]; rect.points(points); //normalize image cv::Mat M = [GeometryUtil getPerspectiveMatrix: points toSize: rect.size]; cv::Mat normalizedImage = [GeometryUtil normalizeImage: &gray withTranformationMatrix: &M withSize: rect.size.width]; //get maxMser from normalized image std::vector<cv::Point> normalizedMser = [ImageUtils maxMser: &normalizedImage]; //remember the template self.logoTemplate = [[MSERManager sharedInstance] extractFeature: &normalizedMser]; //store the feature [self storeTemplate];
يحتوي التطبيق على شاشة واحدة فقط بها زر بدء / إيقاف ، ويتم رسم جميع المعلومات الضرورية ، مثل FPS وعدد MSERs المكتشفة ، تلقائيًا على الصورة. طالما لم يتم إيقاف التطبيق ، يتم استدعاء طريقة processImage التالية لكل إطار صورة في الكاميرا:
-(void)processImage:(cv::Mat &)image { cv::Mat gray; cvtColor(image, gray, CV_BGRA2GRAY); std::vector<std::vector<cv::Point>> msers; [[MSERManager sharedInstance] detectRegions: gray intoVector: msers]; if (msers.size() == 0) { return; }; std::vector<cv::Point> *bestMser = nil; double bestPoint = 10.0; std::for_each(msers.begin(), msers.end(), [&] (std::vector<cv::Point> &mser) { MSERFeature *feature = [[MSERManager sharedInstance] extractFeature: &mser]; if(feature != nil) { if([[MLManager sharedInstance] isToptalLogo: feature] ) { double tmp = [[MLManager sharedInstance] distance: feature ]; if ( bestPoint > tmp ) { bestPoint = tmp; bestMser = &mser; } } } }); if (bestMser) { NSLog(@"minDist: %f", bestPoint); cv::Rect bound = cv::boundingRect(*bestMser); cv::rectangle(image, bound, GREEN, 3); } else { cv::rectangle(image, cv::Rect(0, 0, W, H), RED, 3); } // Omitted debug code [FPS draw: image]; }
هذه الطريقة ، في جوهرها ، تنشئ نسخة بتدرج الرمادي من الصورة الأصلية. إنه يحدد جميع MSERs ويستخرج ميزاتها ذات الصلة ، ويحرز كل MSER للتشابه مع القالب ويختار الأفضل. أخيرًا ، يرسم حدودًا خضراء حول أفضل MSER ويغطي الصورة بالمعلومات الوصفية.
فيما يلي تعريفات لفئات قليلة مهمة وطرقها في هذا التطبيق. يتم وصف أغراضهم في التعليقات.
الهندسة
/* This static class provides perspective transformation function */ @interface GeometryUtil : NSObject /* Return perspective transformation matrix for given points to square with origin [0,0] and with size (size.width, size.width) */ + (cv::Mat) getPerspectiveMatrix: (cv::Point2f[]) points toSize: (cv::Size2f) size; /* Returns new perspecivly transformed image with given size */ + (cv::Mat) normalizeImage: (cv::Mat *) image withTranformationMatrix: (cv::Mat *) M withSize: (float) size; @end
MSERManager.h
/* Singelton class providing function related to msers */ @interface MSERManager : NSObject + (MSERManager *) sharedInstance; /* Extracts all msers into provided vector */ - (void) detectRegions: (cv::Mat &) gray intoVector: (std::vector<std::vector<cv::Point>> &) vector; /* Extracts feature from the mser. For some MSERs feature can be NULL !!! */ - (MSERFeature *) extractFeature: (std::vector<cv::Point> *) mser; @end
MLManager.h
/* This singleton class wraps object recognition function */ @interface MLManager : NSObject + (MLManager *) sharedInstance; /* Stores feature from the biggest MSER in the templateImage */ - (void) learn: (UIImage *) templateImage; /* Sum of the differences between logo feature and given feature */ - (double) distance: (MSERFeature *) feature; /* Returns true if the given feature is similar to the one learned from the template */ - (BOOL) isToptalLogo: (MSERFeature *) feature; @end
بعد توصيل كل شيء معًا ، باستخدام هذا التطبيق ، يجب أن تكون قادرًا على استخدام كاميرا جهاز iOS الخاص بك لاكتشاف شعار Toptal من زوايا واتجاهات مختلفة.
خاتمة
لقد أوضحنا في هذه المقالة مدى سهولة اكتشاف الكائنات البسيطة من صورة باستخدام OpenCV. الكود بالكامل متاح على جيثب. لا تتردد في إرسال طلبات الدفع ، حيث نرحب بالمساهمات.
كما هو الحال مع أي مشاكل في التعلم الآلي ، يمكن زيادة معدل نجاح اكتشاف الشعار في هذا التطبيق باستخدام مجموعة مختلفة من الميزات وطريقة مختلفة لتصنيف الكائنات. ومع ذلك ، آمل أن تساعدك هذه المقالة في البدء في اكتشاف الكائنات باستخدام MSER وتطبيقات تقنيات رؤية الكمبيوتر بشكل عام.
قراءة متعمقة
- ماتاس ، أو.شوم ، إم. أوربان ، وت.باجدلا. "ستريو أساسي عريض قوي من مناطق أقصى ثبات."
- نيومان ، لوكاس. ماتاس ، جيري (2011). "طريقة لتعريب النص والتعرف عليه في صور العالم الحقيقي"