-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathCameraViewController.swift
112 lines (95 loc) · 4.31 KB
/
CameraViewController.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//
// ViewController.swift
// Core ML Object Detection
//
// Created by Nicholas Bourdakos on 2/21/19.
// Copyright © 2019 Nicholas Bourdakos. All rights reserved.
//
import UIKit
import CoreML
import Vision
import AVFoundation
import Accelerate
class CameraViewController: UIViewController {
// MARK: - IBOutlets
@IBOutlet weak var cameraView: UIView!
// MARK: - Variable Declarations
let ssdPredictor = SSDPredictor(Model().model)
var boundingBoxes: [UIBoundingBox] = []
var screenHeight: CGFloat?
var screenWidth: CGFloat?
var videoHeight: Int32?
var videoWidth: Int32?
let videoOutput = AVCaptureVideoDataOutput()
lazy var captureSession: AVCaptureSession? = {
guard let backCamera = AVCaptureDevice.default(for: .video),
let input = try? AVCaptureDeviceInput(device: backCamera) else {
return nil
}
let dimensions = CMVideoFormatDescriptionGetDimensions(backCamera.activeFormat.formatDescription)
videoHeight = dimensions.height
videoWidth = dimensions.width
let captureSession = AVCaptureSession()
captureSession.sessionPreset = .high
captureSession.addInput(input)
if captureSession.canAddOutput(videoOutput) {
let videoOutput = AVCaptureVideoDataOutput()
videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "MyQueue"))
captureSession.addOutput(videoOutput)
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = CGRect(x: view.bounds.minX, y: view.bounds.minY, width: view.bounds.width, height: view.bounds.height)
// `.resize` allows the camera to fill the screen on the iPhone X.
previewLayer.videoGravity = .resize
previewLayer.connection?.videoOrientation = .portrait
cameraView.layer.addSublayer(previewLayer)
return captureSession
}
return nil
}()
override func viewDidLoad() {
super.viewDidLoad()
captureSession?.startRunning()
screenWidth = view.bounds.width
screenHeight = view.bounds.height
for _ in 0 ..< 20 {
let box = UIBoundingBox()
box.addToLayer(view.layer)
boundingBoxes.append(box)
}
}
}
// MARK: - AVCaptureVideoDataOutputSampleBufferDelegate
extension CameraViewController: AVCaptureVideoDataOutputSampleBufferDelegate {
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
return
}
ssdPredictor.predict(pixelBuffer) { predictions, error in
guard let predictions = predictions else {
return
}
DispatchQueue.main.async {
guard let screenWidth = self.screenWidth, let screenHeight = self.screenHeight, let videoWidth = self.videoWidth, let videoHeight = self.videoHeight else {
return
}
let topKPredictions = predictions.prefix(self.boundingBoxes.count)
for (index, prediction) in topKPredictions.enumerated() {
guard let label = prediction.labels.first else {
return
}
let width = screenWidth
let height = width * (CGFloat(videoWidth) / CGFloat(videoHeight))
let offsetY = (screenHeight - height) / 2
let scale = CGAffineTransform.identity.scaledBy(x: width, y: height)
let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -height - offsetY)
let rect = prediction.boundingBox.applying(scale).applying(transform)
let color = UIColor(red: 36/255, green: 101/255, blue: 255/255, alpha: 1.0)
self.boundingBoxes[index].show(frame: rect, label: label.identifier, color: color)
}
for index in topKPredictions.count ..< 20 {
self.boundingBoxes[index].hide()
}
}
}
}
}