Kaip sukurti „iOS“ vaizdo atpažinimo programą naudojant „Apple CoreML“ ir „Vision“ API

Šių metų „Apple World Wide“ kūrėjų konferencijoje išleidus „CoreML“ ir naujas „Vision“ API, mokytis mašinoje dar nebuvo taip paprasta. Šiandien aš jums parodysiu, kaip sukurti paprastą vaizdo atpažinimo programą.

Sužinosime, kaip gauti prieigą prie „iPhone“ fotoaparato ir kaip tai, ką mato kamera, perkelti į mašininio mokymosi modelį analizei atlikti. Visa tai atliksime programiškai, nenaudodami siužetinių kompiuterių! Pašėlęs, aš žinau.

Čia yra žvilgsnis į tai, ką mes ketiname nuveikti šiandien:

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

?? 1 žingsnis: sukurkite naują projektą.

Suaktyvinkite „Xcode“ ir sukurkite naują vieno rodinio programą. Suteikite jai pavadinimą, galbūt „ImageRecognition“. Pasirinkite greitą kaip pagrindinę kalbą ir išsaugokite naują projektą.

? 2 žingsnis: atsisveikinkite su siužetine schema.

Šioje pamokoje viską padarysime programiškai, nereikalaujant siužetinės schemos. Gal paaiškinsiu kodėl kitame straipsnyje.

Ištrinti main.storyboard.

Eikite į info.plistir slinkite žemyn į Informacija apie diegimą. Turime pasakyti „Xcode“, kad nebenaudojame siužetinės linijos.

Ištrinkite pagrindinę sąsają.

Be siužetinės linijos turime rankiniu būdu sukurti programos langą ir šakninio vaizdo valdiklį.

Prie application()funkcijos pridėkite 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 }

Mes rankiniu būdu sukurti app langą su UIWindow(),sukurkite mūsų rodinio valdiklį ir liepkite langui jį naudoti kaip pagrindinio rodinio valdiklį.

Dabar programa turėtų būti kuriama ir paleista be siužetinės yboard

3 žingsnis: nustatykite „AVCaptureSession“.

Prieš pradėdami importuokite „UIKit“, „AVFoundation“ ir „Vision“. „AVCaptureSession“ objektas tvarko fiksavimo veiklą ir valdo duomenų srautą tarp įvesties įrenginių (pvz., Galinės kameros) ir išvesties.

Pirmiausia sukursime funkciją, kad nustatytume fiksavimo sesiją.

Kurkite setupCaptureSession()vidujeViewController.swiftir sužinosite naują AVCaptureSession.

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

Nepamirškite paskambinti šia nauja funkcija ViewDidLoad().

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

Tada mums reikės nuorodos į galinio vaizdo kamerą. Mes galime naudoti aDiscoverySessionpateikti užklausą apie galimus fiksavimo įrenginius pagal mūsų paieškos kriterijus.

Pridėkite šį kodą:

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

AvailableDevicesdabar yra galimų įrenginių, atitinkančių mūsų paieškos kriterijus, sąrašas.

Dabar turime gauti prieigą prie savo captureDeviceir pridėti ją kaip indėlį į mūsų captureSession.

Pridėkite įvestį prie fiksavimo seanso.

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

Pirmasis turimas prietaisas bus galinė kamera. Mes kuriame naująAVCaptureDeviceInputnaudodami mūsų fiksavimo įrenginį ir pridėkite jį prie fiksavimo sesijos.

Dabar, kai turime įvesties sąranką, galime pradėti, kaip išleisti tai, ką kamera fiksuoja.

Pridėkite vaizdo įrašo išvestį prie mūsų fiksavimo sesijos.

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

AVCaptureVideoDataOutputyra išvestis, fiksuojanti vaizdo įrašą. Tai taip pat suteikia mums prieigą prie rėmelių, kurie yra užfiksuoti apdoroti naudojant „delegate“ metodą, kurį pamatysime vėliau.

Tada į savo rodinį turime pridėti fiksavimo sesijos išvestį kaip pasluoksnį.

Pridėkite fiksavimo seanso išvestį kaip posluoksnį prie rodinio valdiklių rodinio.

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

Mes sukuriame sluoksnį pagal savo fiksavimo sesiją ir įtraukiame šį sluoksnį į savo rodinį kaip pasluoksnį. CaptureSession.startRunning()pradeda srautą iš įėjimų į išėjimus, kuriuos mes sujungėme anksčiau.

4 4 žingsnis: Leidimas naudoti fotoaparatą? Leidimas suteiktas.

Nearly everyone has opened an app for the first time and has been prompted to allow the app to use the camera. Starting in iOS 10, our app will crash if we don’t prompt the user before attempting to access the camera.

Navigate to info.plistand add a new key named NSCameraUsageDescription. In the value column, simply explain to the user why your app needs camera access.

Now, when the user launches the app for the first time they will be prompted to allow access to the camera.

? Step 5: Getting the model.

The heart of this project is most likely the machine learning model. The model must be able to take in an image and give us back a prediction of what the image is. You can find free trained models here. The one I chose is ResNet50.

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.

Patinka tai, ką matai? Suteikite šiam įrašui nykštį follow, sekite mane „Twitter“, „GitHub“ arba peržiūrėkite mano asmeninį puslapį.