كيفية إنشاء تطبيق iOS للتعرف على الصور باستخدام واجهات برمجة تطبيقات CoreML و Vision من Apple

مع إصدار CoreML و Vision APIs الجديدة في مؤتمر Apple World Wide Developers لهذا العام ، لم يكن التعلم الآلي أسهل من أي وقت مضى. سأوضح لكم اليوم كيفية إنشاء تطبيق بسيط للتعرف على الصور.

سوف نتعلم كيفية الوصول إلى كاميرا iPhone وكيفية تمرير ما تراه الكاميرا في نموذج التعلم الآلي للتحليل. سنفعل كل هذا برمجيًا ، دون استخدام القصص المصورة! مجنون ، أعلم.

فيما يلي نظرة على ما سنحققه اليوم:

// // ViewController.swift // cameraTest // // Created by Mark Mansur on 2017-08-01. // Copyright © 2017 Mark Mansur. All rights reserved. // import UIKit import AVFoundation import Vision class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate { let label: UILabel = { let label = UILabel() label.textColor = .white label.translatesAutoresizingMaskIntoConstraints = false label.text = "Label" label.font = label.font.withSize(30) return label }() override func viewDidLoad() { super.viewDidLoad() setupCaptureSession() view.addSubview(label) setupLabel() } func setupCaptureSession() { let captureSession = AVCaptureSession() // search for available capture devices let availableDevices = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back).devices // setup capture device, add input to our capture session do { if let captureDevice = availableDevices.first { let captureDeviceInput = try AVCaptureDeviceInput(device: captureDevice) captureSession.addInput(captureDeviceInput) } } catch { print(error.localizedDescription) } // setup output, add output to our capture session let captureOutput = AVCaptureVideoDataOutput() captureOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue")) captureSession.addOutput(captureOutput) let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) previewLayer.frame = view.frame view.layer.addSublayer(previewLayer) captureSession.startRunning() } // called everytime a frame is captured func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard let model = try? VNCoreMLModel(for: Resnet50().model) else {return} let request = VNCoreMLRequest(model: model) { (finishedRequest, error) in guard let results = finishedRequest.results as? [VNClassificationObservation] else { return } guard let Observation = results.first else { return } DispatchQueue.main.async(execute: { self.label.text = "\(Observation.identifier)" }) } guard let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } // executes request try? VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:]).perform([request]) } func setupLabel() { label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true label.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50).isActive = true } }

?? الخطوة الأولى: إنشاء مشروع جديد.

قم بتشغيل Xcode وإنشاء تطبيق عرض واحد جديد. أعطه اسمًا ، ربما "ImageRecognition". اختر swift كلغة رئيسية واحفظ مشروعك الجديد.

? الخطوة الثانية: وداعًا للوحة العمل.

في هذا البرنامج التعليمي ، سنفعل كل شيء برمجيًا ، دون الحاجة إلى لوحة العمل. ربما سأشرح لماذا في مقال آخر.

حذف main.storyboard.

انتقل إلى info.plistوانتقل لأسفل إلى معلومات النشر. نحتاج إلى إخبار Xcode بأننا لم نعد نستخدم لوحة العمل.

احذف الواجهة الرئيسية.

بدون لوحة العمل ، نحتاج إلى إنشاء نافذة التطبيق يدويًا ووحدة تحكم عرض الجذر.

أضف ما يلي إلى application()الوظيفة في AppDelegate.swift:

 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. window = UIWindow() window?.makeKeyAndVisible() let vc = ViewController() window?.rootViewController = vc return true }

نقوم يدويًا بإنشاء نافذة التطبيق باستخدام UIWindow()،قم بإنشاء وحدة تحكم العرض الخاصة بنا ، وأخبر النافذة باستخدامها كوحدة تحكم عرض الجذر.

يجب الآن إنشاء التطبيق وتشغيله بدون لوحة العمل ?

⚙️ الخطوة 3: إعداد AVCaptureSession.

قبل أن نبدأ ، قم باستيراد UIKit و AVFoundation و Vision. يعالج كائن AVCaptureSession نشاط الالتقاط ويدير تدفق البيانات بين أجهزة الإدخال (مثل الكاميرا الخلفية) والمخرجات.

سنبدأ بإنشاء وظيفة لإعداد جلسة الالتقاط الخاصة بنا.

ابتكر في setupCaptureSession()الداخلViewController.swiftوإنشاء نسخة جديدة AVCaptureSession.

func setupCaptureSession() { // creates a new capture session let captureSession = AVCaptureSession() }

لا تنس استدعاء هذه الوظيفة الجديدة من ViewDidLoad().

override func viewDidLoad() { super.viewDidLoad() setupCaptureSession() }

بعد ذلك ، سنحتاج إلى مرجع لكاميرا الرؤية الخلفية. يمكننا استخدام ملفDiscoverySessionللاستعلام عن أجهزة الالتقاط المتاحة بناءً على معايير البحث الخاصة بنا.

أضف الكود التالي:

// search for available capture devices let availableDevices = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back).devices 

AvailableDevicesيحتوي الآن على قائمة بالأجهزة المتوفرة التي تطابق معايير البحث الخاصة بنا.

نحن الآن بحاجة للوصول إلى موقعنا captureDeviceوإضافته كمدخل إلى captureSession.

أضف مُدخلاً إلى جلسة الالتقاط.

// get capture device, add device input to capture session do { if let captureDevice = availableDevices.first { captureSession.addInput(try AVCaptureDeviceInput(device: captureDevice)) } } catch { print(error.localizedDescription) }

سيكون أول جهاز متاح هو الكاميرا الخلفية. نخلق ملفAVCaptureDeviceInputباستخدام جهاز الالتقاط الخاص بنا وإضافته إلى جلسة الالتقاط.

الآن بعد أن أصبح لدينا إعداد الإدخال ، يمكننا البدء في كيفية إخراج ما تلتقطه الكاميرا.

أضف إخراج فيديو إلى جلسة الالتقاط الخاصة بنا.

// setup output, add output to our capture session let captureOutput = AVCaptureVideoDataOutput() captureSession.addOutput(captureOutput)

AVCaptureVideoDataOutputهو إخراج يلتقط الفيديو. كما يوفر لنا الوصول إلى الإطارات التي يتم التقاطها للمعالجة باستخدام طريقة التفويض التي سنراها لاحقًا.

بعد ذلك ، نحتاج إلى إضافة إخراج جلسة الالتقاط كطبقة فرعية إلى وجهة نظرنا.

أضف إخراج جلسة الالتقاط كطبقة فرعية إلى عرض وحدات التحكم في العرض.

let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) previewLayer.frame = view.frame view.layer.addSublayer(previewLayer) captureSession.startRunning()

نقوم بإنشاء طبقة بناءً على جلسة الالتقاط الخاصة بنا ونضيف هذه الطبقة كطبقة فرعية إلى وجهة نظرنا. CaptureSession.startRunning()يبدأ التدفق من المدخلات إلى المخرجات التي قمنا بتوصيلها سابقًا.

? الخطوة 4: إذن لاستخدام الكاميرا؟ تم تأمين التصريح.

فتح الجميع تقريبًا تطبيقًا لأول مرة وطُلب منهم السماح للتطبيق باستخدام الكاميرا. بدءًا من iOS 10 ، سيتعطل تطبيقنا إذا لم نطالب المستخدم قبل محاولة الوصول إلى الكاميرا.

انتقل إلى info.plistوإضافة مفتاح جديد اسمه NSCameraUsageDescription. في عمود القيمة ، اشرح ببساطة للمستخدم سبب احتياج تطبيقك للوصول إلى الكاميرا.

الآن ، عندما يقوم المستخدم بتشغيل التطبيق لأول مرة ، سيُطلب منه السماح بالوصول إلى الكاميرا.

? الخطوة 5: الحصول على النموذج.

جوهر هذا المشروع هو على الأرجح نموذج التعلم الآلي. يجب أن يكون النموذج قادرًا على التقاط صورة ويعطينا توقعًا لما هي الصورة. يمكنك العثور على نماذج مجانية مدربة هنا. الذي اخترته هو ResNet50.

بمجرد الحصول على النموذج الخاص بك ، قم بسحبه وإفلاته في Xcode. سيقوم تلقائيًا بإنشاء الفئات الضرورية ، مما يوفر لك واجهة للتفاعل مع نموذجك.

? الخطوة السادسة: تحليل الصورة.

لتحليل ما تراه الكاميرا ، نحتاج بطريقة ما إلى الوصول إلى الإطارات التي تلتقطها الكاميرا.

يتوافق مع AVCaptureVideoDataOutputSampleBufferDelegateيمنحنا واجهة للتفاعل معها وإخطارنا في كل مرة تلتقط فيها الكاميرا إطارًا.

تتفق ViewControllerمع AVCaptureVideoDataOutputSampleBufferDelegate.

نحتاج إلى إخبار إخراج الفيديو لدينا أن ViewController هو مندوب عينة المخزن المؤقت.

أضف السطر التالي في SetupCaptureSession():

captureOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue")) 

أضف الوظيفة التالية:

func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard let model = try? VNCoreMLModel(for: Resnet50().model) else { return } let request = VNCoreMLRequest(model: model) { (finishedRequest, error) in guard let results = finishedRequest.results as? [VNClassificationObservation] else { return } guard let Observation = results.first else { return } DispatchQueue.main.async(execute: { self.label.text = "\(Observation.identifier)" }) } guard let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } // executes request try? VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:]).perform([request]) }

في كل مرة يتم فيها التقاط إطار ، يتم إخطار المفوض بالاتصال captureOutput(). هذا هو المكان المثالي لإجراء تحليل الصور الخاص بنا باستخدام CoreML.

أولاً ، نقوم بإنشاء ملف VNCoreMLModelwhich is essentially a CoreML model used with the vision framework. We create it with a Resnet50 Model.

Next, we create our vision request. In the completion handler, we update the onscreen UILabel with the identifier returned by the model. We then convert the frame passed to us from a CMSampleBuffer to a CVPixelBuffer. Which is the format our model needs for analysis.

Lastly, we perform the Vision request with a VNImageRequestHandler.

? Step 7: Create a label.

The last step is to create a UILabel containing the model’s prediction.

Create a new UILabeland position it using constraints.

let label: UILabel = { let label = UILabel() label.textColor = .white label.translatesAutoresizingMaskIntoConstraints = false label.text = "Label" label.font = label.font.withSize(30) return label }() func setupLabel() { label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true label.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50).isActive = true }

Don’t forget to add the label as a subview and call setupLabel() from within ViewDidLoad().

view.addSubview(label) setupLabel()

You can download the completed project from GitHub here.

مثل ما ترى؟ أعط هذا المنشور إعجابًا أو تابعني على Twitter أو GitHub أو تحقق من صفحتي الشخصية.