So erstellen Sie eine iOS-App zur Bilderkennung mit den CoreML- und Vision-APIs von Apple

Mit der Veröffentlichung von CoreML und neuen Vision-APIs auf der diesjährigen Apple World Wide Developers Conference war maschinelles Lernen noch nie so einfach. Heute werde ich Ihnen zeigen, wie Sie eine einfache Bilderkennungs-App erstellen.

Wir werden lernen, wie Sie Zugriff auf die iPhone-Kamera erhalten und wie Sie das, was die Kamera sieht, zur Analyse in ein Modell für maschinelles Lernen übertragen. Wir machen das alles programmgesteuert, ohne Storyboards! Verrückt, ich weiß.

Hier ist ein Blick darauf, was wir heute erreichen werden:

// // 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 } }

🙌🏻 Schritt 1: Erstellen Sie ein neues Projekt.

Starten Sie Xcode und erstellen Sie eine neue Einzelansicht-Anwendung. Geben Sie ihm einen Namen, vielleicht "ImageRecognition". Wählen Sie schnell als Hauptsprache und speichern Sie Ihr neues Projekt.

👋 Schritt 2: Verabschieden Sie sich vom Storyboard.

In diesem Tutorial werden wir alles programmgesteuert ausführen, ohne dass das Storyboard erforderlich ist. Vielleicht erkläre ich in einem anderen Artikel, warum.

Löschen main.storyboard.

Navigieren Sie zu info.plistund scrollen Sie nach unten zu Bereitstellungsinformationen. Wir müssen Xcode mitteilen, dass wir das Storyboard nicht mehr verwenden.

Löschen Sie die Hauptschnittstelle.

Ohne das Storyboard müssen wir das App-Fenster und den Root-View-Controller manuell erstellen.

Fügen Sie der application()Funktion in Folgendes hinzu 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 }

Wir erstellen das App-Fenster manuell mit UIWindow(),Erstellen Sie unseren View Controller und weisen Sie das Fenster an, ihn als Root View Controller zu verwenden.

Die App sollte jetzt ohne das Storyboard erstellt und ausgeführt werden 😎

⚙️ Schritt 3: Richten Sie AVCaptureSession ein.

Bevor wir beginnen, importieren Sie UIKit, AVFoundation und Vision. Das AVCaptureSession-Objekt verwaltet die Erfassungsaktivität und verwaltet den Datenfluss zwischen Eingabegeräten (z. B. der Rückkamera) und Ausgängen.

Wir beginnen mit der Erstellung einer Funktion zum Einrichten unserer Erfassungssitzung.

setupCaptureSession()Innen erstellenViewController.swiftund instanziieren Sie eine neue AVCaptureSession.

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

Vergessen Sie nicht, diese neue Funktion von aufzurufen ViewDidLoad().

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

Als nächstes benötigen wir einen Verweis auf die Rückfahrkamera. Wir können a verwendenDiscoverySessionum verfügbare Erfassungsgeräte basierend auf unseren Suchkriterien abzufragen.

Fügen Sie den folgenden Code hinzu:

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

AvailableDevicesEnthält jetzt eine Liste der verfügbaren Geräte, die unseren Suchkriterien entsprechen.

Wir müssen jetzt Zugang zu unserem erhalten captureDeviceund es als Input zu unserem hinzufügen captureSession.

Fügen Sie der Erfassungssitzung eine Eingabe hinzu.

// 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) }

Das erste verfügbare Gerät ist die nach hinten gerichtete Kamera. Wir schaffen eine neueAVCaptureDeviceInputVerwenden Sie unser Erfassungsgerät und fügen Sie es der Erfassungssitzung hinzu.

Nachdem wir unsere Eingabe eingerichtet haben, können wir mit der Ausgabe der von der Kamera aufgenommenen Daten beginnen.

Fügen Sie unserer Erfassungssitzung eine Videoausgabe hinzu.

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

AVCaptureVideoDataOutputist eine Ausgabe, die Videos aufzeichnet. Es bietet uns auch Zugriff auf die Frames, die zur Verarbeitung mit einer Delegate-Methode erfasst werden, die wir später sehen werden.

Als Nächstes müssen wir die Ausgabe der Erfassungssitzung als Unterebene zu unserer Ansicht hinzufügen.

Fügen Sie der Ansicht der Ansichtscontroller die Ausgabe der Erfassungssitzung als Unterebene hinzu.

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

Wir erstellen eine Ebene basierend auf unserer Erfassungssitzung und fügen diese Ebene unserer Ansicht als Unterebene hinzu. CaptureSession.startRunning()Startet den Fluss von den Eingängen zu den Ausgängen, die wir zuvor verbunden haben.

📷 Schritt 4: Erlaubnis zur Benutzung der Kamera? Erlaubnis erteilt.

Fast jeder hat zum ersten Mal eine App geöffnet und wurde aufgefordert, der App die Verwendung der Kamera zu erlauben. Ab iOS 10 stürzt unsere App ab, wenn der Benutzer nicht aufgefordert wird, auf die Kamera zuzugreifen.

Navigieren Sie zu info.plistund fügen Sie einen neuen Schlüssel mit dem Namen hinzu NSCameraUsageDescription. Erklären Sie dem Benutzer in der Wertespalte einfach, warum Ihre App Kamerazugriff benötigt.

Wenn der Benutzer die App zum ersten Mal startet, wird er aufgefordert, den Zugriff auf die Kamera zuzulassen.

📊 Schritt 5: Abrufen des Modells.

Das Herzstück dieses Projekts ist höchstwahrscheinlich das Modell des maschinellen Lernens. Das Modell muss in der Lage sein, ein Bild aufzunehmen und uns eine Vorhersage darüber zu geben, was das Bild ist. Hier finden Sie kostenlos trainierte Modelle. Ich habe ResNet50 gewählt.

Once you obtain your model, drag and drop it into Xcode. It will automatically generate the necessary classes, providing you an interface to interact with your model.

🏞 Step 6: Image analysis.

To analyze what the camera is seeing, we need to somehow gain access to the frames being captured by the camera.

Conforming to the AVCaptureVideoDataOutputSampleBufferDelegategives us an interface to interact with and be notified every time a frame is captured by the camera.

Conform ViewController to the AVCaptureVideoDataOutputSampleBufferDelegate.

We need to tell our Video output that ViewController is its sample buffer delegate.

Add the following line in SetupCaptureSession():

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

Add the following function:

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]) }

Each time a frame is captured, the delegate is notified by calling captureOutput(). This is a perfect place to do our image analysis with CoreML.

First, we create a 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.

Like what you see? Give this post a thumbs up 👍, follow me on Twitter, GitHub, or check out my personal page.