├── BarcodeDetectorDemo ├── BarcodeDetector.xcodeproj │ └── project.pbxproj ├── BarcodeDetector │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon_120.png │ │ │ └── logo_180.png │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── CameraViewController.h │ ├── CameraViewController.m │ ├── DrawingUtility.h │ ├── DrawingUtility.m │ ├── Images.xcassets │ │ ├── Camera.imageset │ │ │ ├── Contents.json │ │ │ ├── ic_camera_alt_black_1x_ios_36dp.png │ │ │ ├── ic_camera_alt_black_2x_ios_36dp.png │ │ │ └── ic_camera_alt_black_3x_ios_36dp.png │ │ ├── Contents.json │ │ └── Photo.imageset │ │ │ ├── Contents.json │ │ │ ├── ic_photo_black_1x_ios_36dp.png │ │ │ ├── ic_photo_black_2x_ios_36dp.png │ │ │ └── ic_photo_black_3x_ios_36dp.png │ ├── Info.plist │ ├── PhotoViewController.h │ ├── PhotoViewController.m │ ├── main.m │ └── multi_values.png └── Podfile ├── CONTRIBUTING ├── FaceDetectorDemo ├── FaceDetector.xcodeproj │ └── project.pbxproj ├── FaceDetector │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon_120.png │ │ │ └── logo_180.png │ │ ├── Camera.imageset │ │ │ ├── Contents.json │ │ │ ├── ic_camera_alt_black_1x_ios_36dp.png │ │ │ ├── ic_camera_alt_black_2x_ios_36dp.png │ │ │ └── ic_camera_alt_black_3x_ios_36dp.png │ │ ├── Contents.json │ │ └── Image.imageset │ │ │ ├── Contents.json │ │ │ ├── ic_image_black_1x_ios_36dp.png │ │ │ ├── ic_image_black_2x_ios_36dp.png │ │ │ └── ic_image_black_3x_ios_36dp.png │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── CameraViewController.h │ ├── CameraViewController.m │ ├── DrawingUtility.h │ ├── DrawingUtility.m │ ├── Info.plist │ ├── PhotoViewController.h │ ├── PhotoViewController.m │ ├── main.m │ └── multi-face.png └── Podfile ├── GooglyEyesDemo ├── GooglyEyes.xcodeproj │ └── project.pbxproj ├── GooglyEyes │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon_120.png │ │ │ └── logo_180.png │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── EyePhysics.h │ ├── EyePhysics.m │ ├── FaceTracker.h │ ├── FaceTracker.m │ ├── GooglyEyeView.h │ ├── GooglyEyeView.m │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ └── main.m └── Podfile ├── LICENSE ├── MultiDetectorDemo ├── MultiDetector.xcodeproj │ └── project.pbxproj ├── MultiDetector │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon_120.png │ │ │ └── logo_180.png │ ├── BarcodeTracker.h │ ├── BarcodeTracker.m │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── FaceTracker.h │ ├── FaceTracker.m │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ └── main.m └── Podfile └── README.md /BarcodeDetectorDemo/BarcodeDetector/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import UIKit; 18 | 19 | @interface AppDelegate : UIResponder 20 | 21 | @property(strong, nonatomic) UIWindow *window; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import "AppDelegate.h" 18 | 19 | @interface AppDelegate () 20 | 21 | @end 22 | 23 | @implementation AppDelegate 24 | 25 | - (BOOL)application:(UIApplication *)application 26 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 27 | 28 | UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController; 29 | tabBarController.delegate = self; 30 | return YES; 31 | } 32 | 33 | - (UIInterfaceOrientationMask) 34 | tabBarControllerSupportedInterfaceOrientations:(UITabBarController *)tabBarController { 35 | return UIInterfaceOrientationMaskAll; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "size" : "40x40", 20 | "idiom" : "iphone", 21 | "filename" : "icon_120.png", 22 | "scale" : "3x" 23 | }, 24 | { 25 | "idiom" : "iphone", 26 | "size" : "60x60", 27 | "scale" : "2x" 28 | }, 29 | { 30 | "size" : "60x60", 31 | "idiom" : "iphone", 32 | "filename" : "logo_180.png", 33 | "scale" : "3x" 34 | } 35 | ], 36 | "info" : { 37 | "version" : 1, 38 | "author" : "xcode" 39 | } 40 | } -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Assets.xcassets/AppIcon.appiconset/icon_120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/BarcodeDetectorDemo/BarcodeDetector/Assets.xcassets/AppIcon.appiconset/icon_120.png -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Assets.xcassets/AppIcon.appiconset/logo_180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/BarcodeDetectorDemo/BarcodeDetector/Assets.xcassets/AppIcon.appiconset/logo_180.png -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/CameraViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import UIKit; 18 | 19 | // View controller demonstraing how to use the barcode detector with the AVFoundation 20 | // video pipeline. 21 | @interface CameraViewController : UIViewController 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/CameraViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import AVFoundation; 18 | @import GoogleMobileVision; 19 | 20 | #import "CameraViewController.h" 21 | #import "DrawingUtility.h" 22 | 23 | 24 | @interface CameraViewController () 25 | 26 | @property(nonatomic, weak) IBOutlet UIView *placeHolderView; 27 | @property(nonatomic, weak) IBOutlet UIView *overlayView; 28 | 29 | @property(nonatomic, strong) AVCaptureSession *session; 30 | @property(nonatomic, strong) AVCaptureVideoDataOutput *videoDataOutput; 31 | @property(nonatomic, strong) dispatch_queue_t videoDataOutputQueue; 32 | @property(nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer; 33 | @property(nonatomic, assign) UIDeviceOrientation lastKnownDeviceOrientation; 34 | 35 | @property(nonatomic, strong) GMVDetector *barcodeDetector; 36 | 37 | @end 38 | 39 | @implementation CameraViewController 40 | 41 | - (id)initWithCoder:(NSCoder *)aDecoder { 42 | self = [super initWithCoder:aDecoder]; 43 | if (self) { 44 | _videoDataOutputQueue = dispatch_queue_create("VideoDataOutputQueue", 45 | DISPATCH_QUEUE_SERIAL); 46 | } 47 | return self; 48 | } 49 | 50 | - (void)viewDidLoad { 51 | [super viewDidLoad]; 52 | 53 | // Set up default camera settings. 54 | self.session = [[AVCaptureSession alloc] init]; 55 | self.session.sessionPreset = AVCaptureSessionPresetMedium; 56 | [self updateCameraSelection]; 57 | 58 | // Set up video processing pipeline. 59 | [self setUpVideoProcessing]; 60 | 61 | // Set up camera preview. 62 | [self setUpCameraPreview]; 63 | 64 | // Initialize barcode detector. 65 | self.barcodeDetector = [GMVDetector detectorOfType:GMVDetectorTypeBarcode options:nil]; 66 | } 67 | 68 | - (void)viewDidLayoutSubviews { 69 | [super viewDidLayoutSubviews]; 70 | 71 | self.previewLayer.frame = self.view.layer.bounds; 72 | self.previewLayer.position = CGPointMake(CGRectGetMidX(self.previewLayer.frame), 73 | CGRectGetMidY(self.previewLayer.frame)); 74 | } 75 | 76 | - (void)viewDidUnload { 77 | [self cleanupCaptureSession]; 78 | [super viewDidUnload]; 79 | } 80 | 81 | - (void)viewDidAppear:(BOOL)animated { 82 | [super viewDidAppear:animated]; 83 | [self.session startRunning]; 84 | } 85 | 86 | - (void)viewDidDisappear:(BOOL)animated { 87 | [super viewDidDisappear:animated]; 88 | [self.session stopRunning]; 89 | } 90 | 91 | - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 92 | duration:(NSTimeInterval)duration { 93 | // Camera rotation needs to be manually set when rotation changes. 94 | if (self.previewLayer) { 95 | if (toInterfaceOrientation == UIInterfaceOrientationPortrait) { 96 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortrait; 97 | } else if (toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) { 98 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortraitUpsideDown; 99 | } else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) { 100 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft; 101 | } else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) { 102 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; 103 | } 104 | } 105 | } 106 | 107 | #pragma mark - AVCaptureVideoPreviewLayer Helper method 108 | 109 | - (CGRect)scaleRect:(CGRect)rect 110 | xScale:(CGFloat)xscale 111 | yScale:(CGFloat)yscale 112 | offset:(CGPoint)offset { 113 | CGRect resultRect = CGRectMake(rect.origin.x * xscale, 114 | rect.origin.y * yscale, 115 | rect.size.width * xscale, 116 | rect.size.height * yscale); 117 | resultRect = CGRectOffset(resultRect, offset.x, offset.y); 118 | return resultRect; 119 | } 120 | 121 | - (CGPoint)scalePoint:(CGPoint)point 122 | xScale:(CGFloat)xscale 123 | yScale:(CGFloat)yscale 124 | offset:(CGPoint)offset { 125 | CGPoint resultPoint = CGPointMake(point.x * xscale + offset.x, point.y * yscale + offset.y); 126 | return resultPoint; 127 | } 128 | 129 | - (void)setLastKnownDeviceOrientation:(UIDeviceOrientation)orientation { 130 | if (orientation != UIDeviceOrientationUnknown && 131 | orientation != UIDeviceOrientationFaceUp && 132 | orientation != UIDeviceOrientationFaceDown) { 133 | _lastKnownDeviceOrientation = orientation; 134 | } 135 | } 136 | 137 | - (void)computeCameraDisplayFrameScaleProperties:(CMSampleBufferRef)sampleBuffer 138 | previewFrameSize:(CGSize)previewFrameSize 139 | yScale:(CGFloat *)previewYScale 140 | xScale:(CGFloat *)previewXScale 141 | offset:(CGPoint *)previewOffset { 142 | // The video frames captured by the camera have different size than video preview. 143 | // Calculates the scale factors and offset to properly display the features. 144 | CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer); 145 | CGRect cleanAperture = CMVideoFormatDescriptionGetCleanAperture(formatDescription, false); 146 | CGSize parentFrameSize; 147 | if (CGSizeEqualToSize(previewFrameSize, CGSizeZero)) { 148 | parentFrameSize = [[UIScreen mainScreen] bounds].size; 149 | } else { 150 | parentFrameSize = previewFrameSize; 151 | } 152 | 153 | // Assumes AVLayerVideoGravityResizeAspect 154 | CGFloat cameraRatio = cleanAperture.size.height / cleanAperture.size.width; 155 | CGFloat viewRatio = parentFrameSize.width / parentFrameSize.height; 156 | CGFloat xScale = 1; 157 | CGFloat yScale = 1; 158 | CGRect videoBox = CGRectZero; 159 | if (viewRatio > cameraRatio) { 160 | videoBox.size.width = parentFrameSize.height * cleanAperture.size.width / 161 | cleanAperture.size.height; 162 | videoBox.size.height = parentFrameSize.height; 163 | videoBox.origin.x = (parentFrameSize.width - videoBox.size.width) / 2; 164 | videoBox.origin.y = (videoBox.size.height - parentFrameSize.height) / 2; 165 | 166 | xScale = videoBox.size.width / cleanAperture.size.width; 167 | yScale = videoBox.size.height / cleanAperture.size.height; 168 | } else { 169 | videoBox.size.width = parentFrameSize.width; 170 | videoBox.size.height = cleanAperture.size.width * 171 | (parentFrameSize.width / cleanAperture.size.height); 172 | videoBox.origin.x = (videoBox.size.width - parentFrameSize.width) / 2; 173 | videoBox.origin.y = (parentFrameSize.height - videoBox.size.height) / 2; 174 | 175 | xScale = videoBox.size.width / cleanAperture.size.height; 176 | yScale = videoBox.size.height / cleanAperture.size.width; 177 | } 178 | *previewYScale = yScale; 179 | *previewXScale = xScale; 180 | *previewOffset = videoBox.origin; 181 | } 182 | 183 | #pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate 184 | 185 | - (void)captureOutput:(AVCaptureOutput *)captureOutput 186 | didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 187 | fromConnection:(AVCaptureConnection *)connection { 188 | 189 | UIImage *image = [GMVUtility sampleBufferTo32RGBA:sampleBuffer]; 190 | AVCaptureDevicePosition devicePosition = AVCaptureDevicePositionBack; 191 | 192 | // Establish the image orientation and detect features using GMVDetector. 193 | UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation]; 194 | GMVImageOrientation orientation = [GMVUtility 195 | imageOrientationFromOrientation:deviceOrientation 196 | withCaptureDevicePosition:devicePosition 197 | defaultDeviceOrientation:self.lastKnownDeviceOrientation]; 198 | NSDictionary *options = @{ 199 | GMVDetectorImageOrientation : @(orientation) 200 | }; 201 | 202 | NSArray *barcodes = [self.barcodeDetector featuresInImage:image 203 | options:options]; 204 | NSLog(@"Detected %lu barcodes.", (unsigned long)barcodes.count); 205 | 206 | // The video frames captured by the camera are a different size than the video preview. 207 | // Calculates the scale factors and offset to properly display the features. 208 | CGFloat yScale = 1; 209 | CGFloat xScale = 1; 210 | CGPoint offset = CGPointZero; 211 | 212 | [self computeCameraDisplayFrameScaleProperties:sampleBuffer 213 | previewFrameSize:self.previewLayer.frame.size 214 | yScale:&yScale 215 | xScale:&xScale 216 | offset:&offset]; 217 | 218 | dispatch_sync(dispatch_get_main_queue(), ^{ 219 | // Remove previously added feature 220 | for (UIView *featureview in self.overlayView.subviews) { 221 | [featureview removeFromSuperview]; 222 | } 223 | 224 | // Display detected features in overlay. 225 | for (GMVBarcodeFeature *barcode in barcodes) { 226 | CGPoint p0 = [self scalePoint:barcode.cornerPoints[0].CGPointValue 227 | xScale:xScale 228 | yScale:yScale 229 | offset:offset]; 230 | CGPoint p1 = [self scalePoint:barcode.cornerPoints[1].CGPointValue 231 | xScale:xScale 232 | yScale:yScale 233 | offset:offset]; 234 | CGPoint p2 = [self scalePoint:barcode.cornerPoints[2].CGPointValue 235 | xScale:xScale 236 | yScale:yScale 237 | offset:offset]; 238 | CGPoint p3 = [self scalePoint:barcode.cornerPoints[3].CGPointValue 239 | xScale:xScale 240 | yScale:yScale 241 | offset:offset]; 242 | NSArray *points = @[[NSValue valueWithCGPoint:p0], [NSValue valueWithCGPoint:p1], 243 | [NSValue valueWithCGPoint:p2], [NSValue valueWithCGPoint:p3]]; 244 | [DrawingUtility addShape:points toView:self.overlayView withColor:[UIColor purpleColor]]; 245 | 246 | CGRect textRect = CGRectMake(p0.x, p3.y, barcode.bounds.size.width, 50); 247 | UILabel *label = [[UILabel alloc] initWithFrame:textRect]; 248 | label.text = barcode.rawValue; 249 | [self.overlayView addSubview:label]; 250 | } 251 | }); 252 | } 253 | 254 | #pragma mark - Camera setup 255 | 256 | - (void)cleanupVideoProcessing { 257 | if (self.videoDataOutput) { 258 | [self.session removeOutput:self.videoDataOutput]; 259 | } 260 | self.videoDataOutput = nil; 261 | } 262 | 263 | - (void)cleanupCaptureSession { 264 | [self.session stopRunning]; 265 | [self cleanupVideoProcessing]; 266 | self.session = nil; 267 | [self.previewLayer removeFromSuperlayer]; 268 | } 269 | 270 | - (void)setUpVideoProcessing { 271 | self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init]; 272 | NSDictionary *rgbOutputSettings = @{ 273 | (__bridge NSString*)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) 274 | }; 275 | [self.videoDataOutput setVideoSettings:rgbOutputSettings]; 276 | 277 | if (![self.session canAddOutput:self.videoDataOutput]) { 278 | [self cleanupVideoProcessing]; 279 | NSLog(@"Failed to setup video output"); 280 | return; 281 | } 282 | [self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES]; 283 | [self.videoDataOutput setSampleBufferDelegate:self queue:self.videoDataOutputQueue]; 284 | [self.session addOutput:self.videoDataOutput]; 285 | } 286 | 287 | - (void)setUpCameraPreview { 288 | self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session]; 289 | [self.previewLayer setBackgroundColor:[UIColor whiteColor].CGColor]; 290 | [self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect]; 291 | CALayer *rootLayer = self.placeHolderView.layer; 292 | rootLayer.masksToBounds = YES; 293 | [self.previewLayer setFrame:rootLayer.bounds]; 294 | [rootLayer addSublayer:self.previewLayer]; 295 | } 296 | 297 | - (void)updateCameraSelection { 298 | [self.session beginConfiguration]; 299 | 300 | // Remove old inputs 301 | NSArray *oldInputs = [self.session inputs]; 302 | for (AVCaptureInput *oldInput in oldInputs) { 303 | [self.session removeInput:oldInput]; 304 | } 305 | 306 | AVCaptureDevicePosition desiredPosition = AVCaptureDevicePositionBack; 307 | AVCaptureDeviceInput *input = [self captureDeviceInputForPosition:desiredPosition]; 308 | if (!input) { 309 | // Failed, restore old inputs 310 | for (AVCaptureInput *oldInput in oldInputs) { 311 | [self.session addInput:oldInput]; 312 | } 313 | } else { 314 | // Succeeded, set input and update connection states 315 | [self.session addInput:input]; 316 | } 317 | [self.session commitConfiguration]; 318 | } 319 | 320 | - (AVCaptureDeviceInput *)captureDeviceInputForPosition:(AVCaptureDevicePosition)desiredPosition { 321 | for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { 322 | if (device.position == desiredPosition) { 323 | NSError *error = nil; 324 | AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device 325 | error:&error]; 326 | if (error) { 327 | NSLog(@"Could not initialize for AVMediaTypeVideo for device %@", device); 328 | } else if ([self.session canAddInput:input]) { 329 | return input; 330 | } 331 | } 332 | } 333 | return nil; 334 | } 335 | 336 | @end 337 | -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/DrawingUtility.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import Foundation; 18 | @import UIKit; 19 | 20 | // Utilities for adding shaped views to the view hierarchy. 21 | @interface DrawingUtility : NSObject 22 | 23 | + (void)addRectangle:(CGRect)rect 24 | toView:(UIView *)view 25 | withColor:(UIColor *)color; 26 | 27 | + (void)addShape:(NSArray *)points 28 | toView:(UIView *)view 29 | withColor:(UIColor *)color; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/DrawingUtility.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import "DrawingUtility.h" 18 | 19 | @implementation DrawingUtility 20 | 21 | + (void)addRectangle:(CGRect)rect 22 | toView:(UIView *)view 23 | withColor:(UIColor *)color { 24 | UIView *newView = [[UIView alloc] initWithFrame:rect]; 25 | newView.layer.cornerRadius = 10; 26 | newView.alpha = 0.3; 27 | newView.backgroundColor = color; 28 | [view addSubview:newView]; 29 | } 30 | 31 | + (void)addShape:(NSArray *)points 32 | toView:(UIView *)view 33 | withColor:(UIColor *)color { 34 | UIBezierPath *path = [[UIBezierPath alloc] init]; 35 | for (int i = 0; i < points.count; i++) { 36 | CGPoint point = [points[i] CGPointValue]; 37 | 38 | if (i == 0) { 39 | [path moveToPoint:point]; 40 | } else { 41 | [path addLineToPoint:point]; 42 | } 43 | 44 | if (i == [points count] - 1) { 45 | [path closePath]; 46 | } 47 | } 48 | 49 | CAShapeLayer *shapeLayer = [CAShapeLayer layer]; 50 | shapeLayer.path = path.CGPath; 51 | shapeLayer.fillColor = color.CGColor; 52 | 53 | CGRect rect = CGRectMake(0, 0, view.frame.size.width, view.frame.size.height); 54 | UIView *newView = [[UIView alloc] initWithFrame:rect]; 55 | newView.alpha = 0.3; 56 | [newView.layer addSublayer:shapeLayer]; 57 | newView.backgroundColor = [UIColor clearColor]; 58 | [view addSubview:newView]; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Images.xcassets/Camera.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "ic_camera_alt_black_1x_ios_36dp.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "ic_camera_alt_black_2x_ios_36dp.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "ic_camera_alt_black_3x_ios_36dp.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Images.xcassets/Camera.imageset/ic_camera_alt_black_1x_ios_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/BarcodeDetectorDemo/BarcodeDetector/Images.xcassets/Camera.imageset/ic_camera_alt_black_1x_ios_36dp.png -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Images.xcassets/Camera.imageset/ic_camera_alt_black_2x_ios_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/BarcodeDetectorDemo/BarcodeDetector/Images.xcassets/Camera.imageset/ic_camera_alt_black_2x_ios_36dp.png -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Images.xcassets/Camera.imageset/ic_camera_alt_black_3x_ios_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/BarcodeDetectorDemo/BarcodeDetector/Images.xcassets/Camera.imageset/ic_camera_alt_black_3x_ios_36dp.png -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Images.xcassets/Photo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "ic_photo_black_1x_ios_36dp.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "ic_photo_black_2x_ios_36dp.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x", 16 | "filename" : "ic_photo_black_3x_ios_36dp.png" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Images.xcassets/Photo.imageset/ic_photo_black_1x_ios_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/BarcodeDetectorDemo/BarcodeDetector/Images.xcassets/Photo.imageset/ic_photo_black_1x_ios_36dp.png -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Images.xcassets/Photo.imageset/ic_photo_black_2x_ios_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/BarcodeDetectorDemo/BarcodeDetector/Images.xcassets/Photo.imageset/ic_photo_black_2x_ios_36dp.png -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Images.xcassets/Photo.imageset/ic_photo_black_3x_ios_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/BarcodeDetectorDemo/BarcodeDetector/Images.xcassets/Photo.imageset/ic_photo_black_3x_ios_36dp.png -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/PhotoViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import UIKit; 18 | 19 | // View controller demonstrating how to use GMVDetector with a static image. 20 | @interface PhotoViewController : UIViewController 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/PhotoViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import GoogleMobileVision; 18 | 19 | #import "DrawingUtility.h" 20 | #import "PhotoViewController.h" 21 | 22 | @interface PhotoViewController () 23 | 24 | @property(nonatomic, weak) IBOutlet UIView *overlayView; 25 | @property(nonatomic, weak) IBOutlet UIImageView *imageView; 26 | 27 | @property(nonatomic, strong) GMVDetector *barcodeDetector; 28 | 29 | @end 30 | 31 | @implementation PhotoViewController 32 | 33 | - (void)viewDidLoad { 34 | [super viewDidLoad]; 35 | 36 | NSDictionary *options = @{ 37 | GMVDetectorBarcodeFormats : @(GMVDetectorBarcodeFormatQRCode | GMVDetectorBarcodeFormatAztec) 38 | }; 39 | 40 | // Initialize a barcode detector. 41 | self.barcodeDetector = [GMVDetector detectorOfType:GMVDetectorTypeBarcode options:options]; 42 | } 43 | 44 | - (IBAction)detectBarcodeButtonTapped:(id)sender { 45 | for (UIView *annotationView in [self.overlayView subviews]) { 46 | [annotationView removeFromSuperview]; 47 | } 48 | 49 | NSArray *barcodes = 50 | [self.barcodeDetector featuresInImage:self.imageView.image options:nil]; 51 | 52 | CGAffineTransform translate = CGAffineTransformTranslate(CGAffineTransformIdentity, 53 | (self.view.frame.size.width - self.imageView.image.size.width) / 2, 54 | (self.view.frame.size.height - self.imageView.image.size.height) / 2); 55 | 56 | for (GMVBarcodeFeature *barcode in barcodes) { 57 | CGRect rect = barcode.bounds; 58 | [DrawingUtility addRectangle:CGRectApplyAffineTransform(rect, translate) 59 | toView:self.overlayView 60 | withColor:[UIColor purpleColor]]; 61 | } 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/main.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import UIKit; 18 | 19 | #import "AppDelegate.h" 20 | 21 | int main(int argc, char * argv[]) { 22 | @autoreleasepool { 23 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /BarcodeDetectorDemo/BarcodeDetector/multi_values.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/BarcodeDetectorDemo/BarcodeDetector/multi_values.png -------------------------------------------------------------------------------- /BarcodeDetectorDemo/Podfile: -------------------------------------------------------------------------------- 1 | target "BarcodeDetector" do 2 | pod 'GoogleMobileVision/BarcodeDetector' 3 | end 4 | -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at the end). 2 | 3 | ### Before you contribute 4 | Before we can use your code, you must sign the 5 | [Google Individual Contributor License Agreement] 6 | (https://cla.developers.google.com/about/google-individual) 7 | (CLA), which you can do online. The CLA is necessary mainly because you own the 8 | copyright to your changes, even after your contribution becomes part of our 9 | codebase, so we need your permission to use and distribute your code. We also 10 | need to be sure of various other things—for instance that you'll tell us if you 11 | know that your code infringes on other people's patents. You don't have to sign 12 | the CLA until after you've submitted your code for review and a member has 13 | approved it, but you must do it before we can put your code into our codebase. 14 | Before you start working on a larger contribution, you should get in touch with 15 | us first through the issue tracker with your idea so that we can help out and 16 | possibly guide you. Coordinating up front makes it much easier to avoid 17 | frustration later on. 18 | 19 | ### Code reviews 20 | All submissions, including submissions by project members, require review. We 21 | use Github pull requests for this purpose. 22 | 23 | ### The small print 24 | Contributions made by corporations are covered by a different agreement than 25 | the one above, the 26 | [Software Grant and Corporate Contributor License Agreement] 27 | (https://cla.developers.google.com/about/google-corporate). 28 | -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import UIKit; 18 | 19 | @interface AppDelegate : UIResponder 20 | 21 | @property(nonatomic, strong) UIWindow *window; 22 | 23 | @end 24 | 25 | -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import "AppDelegate.h" 18 | 19 | @interface AppDelegate () 20 | 21 | @end 22 | 23 | @implementation AppDelegate 24 | 25 | - (BOOL)application:(UIApplication *)application 26 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 27 | 28 | UITabBarController *tabBarController = (UITabBarController *)[[self window] rootViewController]; 29 | tabBarController.delegate = self; 30 | return YES; 31 | } 32 | 33 | - (UIInterfaceOrientationMask) 34 | tabBarControllerSupportedInterfaceOrientations:(UITabBarController *)tabBarController { 35 | return UIInterfaceOrientationMaskAll; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "size" : "40x40", 20 | "idiom" : "iphone", 21 | "filename" : "icon_120.png", 22 | "scale" : "3x" 23 | }, 24 | { 25 | "idiom" : "iphone", 26 | "size" : "60x60", 27 | "scale" : "2x" 28 | }, 29 | { 30 | "size" : "60x60", 31 | "idiom" : "iphone", 32 | "filename" : "logo_180.png", 33 | "scale" : "3x" 34 | } 35 | ], 36 | "info" : { 37 | "version" : 1, 38 | "author" : "xcode" 39 | } 40 | } -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/Assets.xcassets/AppIcon.appiconset/icon_120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/FaceDetectorDemo/FaceDetector/Assets.xcassets/AppIcon.appiconset/icon_120.png -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/Assets.xcassets/AppIcon.appiconset/logo_180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/FaceDetectorDemo/FaceDetector/Assets.xcassets/AppIcon.appiconset/logo_180.png -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/Assets.xcassets/Camera.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ic_camera_alt_black_1x_ios_36dp.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "ic_camera_alt_black_2x_ios_36dp.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "ic_camera_alt_black_3x_ios_36dp.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/Assets.xcassets/Camera.imageset/ic_camera_alt_black_1x_ios_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/FaceDetectorDemo/FaceDetector/Assets.xcassets/Camera.imageset/ic_camera_alt_black_1x_ios_36dp.png -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/Assets.xcassets/Camera.imageset/ic_camera_alt_black_2x_ios_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/FaceDetectorDemo/FaceDetector/Assets.xcassets/Camera.imageset/ic_camera_alt_black_2x_ios_36dp.png -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/Assets.xcassets/Camera.imageset/ic_camera_alt_black_3x_ios_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/FaceDetectorDemo/FaceDetector/Assets.xcassets/Camera.imageset/ic_camera_alt_black_3x_ios_36dp.png -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/Assets.xcassets/Image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ic_image_black_1x_ios_36dp.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "ic_image_black_2x_ios_36dp.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "ic_image_black_3x_ios_36dp.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/Assets.xcassets/Image.imageset/ic_image_black_1x_ios_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/FaceDetectorDemo/FaceDetector/Assets.xcassets/Image.imageset/ic_image_black_1x_ios_36dp.png -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/Assets.xcassets/Image.imageset/ic_image_black_2x_ios_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/FaceDetectorDemo/FaceDetector/Assets.xcassets/Image.imageset/ic_image_black_2x_ios_36dp.png -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/Assets.xcassets/Image.imageset/ic_image_black_3x_ios_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/FaceDetectorDemo/FaceDetector/Assets.xcassets/Image.imageset/ic_image_black_3x_ios_36dp.png -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | 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 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/CameraViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import UIKit; 18 | 19 | // View controller demonstraing how to use the face detector with the AVFoundation video pipeline. 20 | @interface CameraViewController : UIViewController 21 | 22 | @end 23 | 24 | -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/CameraViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | @import AVFoundation; 19 | @import GoogleMobileVision; 20 | 21 | #import "CameraViewController.h" 22 | #import "DrawingUtility.h" 23 | 24 | @interface CameraViewController () 25 | // UI elements. 26 | @property(nonatomic, weak) IBOutlet UIView *placeHolder; 27 | @property(nonatomic, weak) IBOutlet UIView *overlayView; 28 | @property(nonatomic, weak) IBOutlet UISwitch *cameraSwitch; 29 | 30 | // Video objects. 31 | @property(nonatomic, strong) AVCaptureSession *session; 32 | @property(nonatomic, strong) AVCaptureVideoDataOutput *videoDataOutput; 33 | @property(nonatomic, strong) dispatch_queue_t videoDataOutputQueue; 34 | @property(nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer; 35 | @property(nonatomic, assign) UIDeviceOrientation lastKnownDeviceOrientation; 36 | 37 | // Detector. 38 | @property(nonatomic, strong) GMVDetector *faceDetector; 39 | 40 | @end 41 | 42 | @implementation CameraViewController 43 | 44 | - (id)initWithCoder:(NSCoder *)aDecoder { 45 | self = [super initWithCoder:aDecoder]; 46 | if (self) { 47 | self.videoDataOutputQueue = dispatch_queue_create("VideoDataOutputQueue", 48 | DISPATCH_QUEUE_SERIAL); 49 | } 50 | return self; 51 | } 52 | 53 | - (void)viewDidLoad { 54 | [super viewDidLoad]; 55 | 56 | // Set up default camera settings. 57 | self.session = [[AVCaptureSession alloc] init]; 58 | self.session.sessionPreset = AVCaptureSessionPresetMedium; 59 | self.cameraSwitch.on = YES; 60 | [self updateCameraSelection]; 61 | 62 | // Setup video processing pipeline. 63 | [self setupVideoProcessing]; 64 | 65 | // Setup camera preview. 66 | [self setupCameraPreview]; 67 | 68 | // Initialize the face detector. 69 | NSDictionary *options = @{ 70 | GMVDetectorFaceMinSize : @(0.3), 71 | GMVDetectorFaceTrackingEnabled : @(YES), 72 | GMVDetectorFaceLandmarkType : @(GMVDetectorFaceLandmarkAll) 73 | }; 74 | self.faceDetector = [GMVDetector detectorOfType:GMVDetectorTypeFace options:options]; 75 | } 76 | 77 | - (void)viewDidLayoutSubviews { 78 | [super viewDidLayoutSubviews]; 79 | 80 | self.previewLayer.frame = self.view.layer.bounds; 81 | self.previewLayer.position = CGPointMake(CGRectGetMidX(self.previewLayer.frame), 82 | CGRectGetMidY(self.previewLayer.frame)); 83 | } 84 | 85 | - (void)viewDidUnload { 86 | [self cleanupCaptureSession]; 87 | [super viewDidUnload]; 88 | } 89 | 90 | - (void)viewDidAppear:(BOOL)animated { 91 | [super viewDidAppear:animated]; 92 | [self.session startRunning]; 93 | } 94 | 95 | - (void)viewDidDisappear:(BOOL)animated { 96 | [super viewDidDisappear:animated]; 97 | [self.session stopRunning]; 98 | } 99 | 100 | - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 101 | duration:(NSTimeInterval)duration { 102 | // Camera rotation needs to be manually set when rotation changes. 103 | if (self.previewLayer) { 104 | if (toInterfaceOrientation == UIInterfaceOrientationPortrait) { 105 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortrait; 106 | } else if (toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) { 107 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortraitUpsideDown; 108 | } else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) { 109 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft; 110 | } else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) { 111 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; 112 | } 113 | } 114 | } 115 | 116 | #pragma mark - AVCaptureVideoPreviewLayer Helper method 117 | 118 | - (CGRect)scaledRect:(CGRect)rect 119 | xScale:(CGFloat)xscale 120 | yScale:(CGFloat)yscale 121 | offset:(CGPoint)offset { 122 | CGRect resultRect = CGRectMake(rect.origin.x * xscale, 123 | rect.origin.y * yscale, 124 | rect.size.width * xscale, 125 | rect.size.height * yscale); 126 | resultRect = CGRectOffset(resultRect, offset.x, offset.y); 127 | return resultRect; 128 | } 129 | 130 | - (CGPoint)scaledPoint:(CGPoint)point 131 | xScale:(CGFloat)xscale 132 | yScale:(CGFloat)yscale 133 | offset:(CGPoint)offset { 134 | CGPoint resultPoint = CGPointMake(point.x * xscale + offset.x, point.y * yscale + offset.y); 135 | return resultPoint; 136 | } 137 | 138 | - (void)setLastKnownDeviceOrientation:(UIDeviceOrientation)orientation { 139 | if (orientation != UIDeviceOrientationUnknown && 140 | orientation != UIDeviceOrientationFaceUp && 141 | orientation != UIDeviceOrientationFaceDown) { 142 | _lastKnownDeviceOrientation = orientation; 143 | } 144 | } 145 | 146 | #pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate 147 | 148 | - (void)captureOutput:(AVCaptureOutput *)captureOutput 149 | didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 150 | fromConnection:(AVCaptureConnection *)connection { 151 | 152 | UIImage *image = [GMVUtility sampleBufferTo32RGBA:sampleBuffer]; 153 | AVCaptureDevicePosition devicePosition = self.cameraSwitch.isOn ? AVCaptureDevicePositionFront : 154 | AVCaptureDevicePositionBack; 155 | 156 | // Establish the image orientation. 157 | UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation]; 158 | GMVImageOrientation orientation = [GMVUtility 159 | imageOrientationFromOrientation:deviceOrientation 160 | withCaptureDevicePosition:devicePosition 161 | defaultDeviceOrientation:self.lastKnownDeviceOrientation]; 162 | NSDictionary *options = @{ 163 | GMVDetectorImageOrientation : @(orientation) 164 | }; 165 | // Detect features using GMVDetector. 166 | NSArray *faces = [self.faceDetector featuresInImage:image options:options]; 167 | NSLog(@"Detected %lu face(s).", (unsigned long)[faces count]); 168 | 169 | // The video frames captured by the camera are a different size than the video preview. 170 | // Calculates the scale factors and offset to properly display the features. 171 | CMFormatDescriptionRef fdesc = CMSampleBufferGetFormatDescription(sampleBuffer); 172 | CGRect clap = CMVideoFormatDescriptionGetCleanAperture(fdesc, false); 173 | CGSize parentFrameSize = self.previewLayer.frame.size; 174 | 175 | // Assume AVLayerVideoGravityResizeAspect 176 | CGFloat cameraRatio = clap.size.height / clap.size.width; 177 | CGFloat viewRatio = parentFrameSize.width / parentFrameSize.height; 178 | CGFloat xScale = 1; 179 | CGFloat yScale = 1; 180 | CGRect videoBox = CGRectZero; 181 | if (viewRatio > cameraRatio) { 182 | videoBox.size.width = parentFrameSize.height * clap.size.width / clap.size.height; 183 | videoBox.size.height = parentFrameSize.height; 184 | videoBox.origin.x = (parentFrameSize.width - videoBox.size.width) / 2; 185 | videoBox.origin.y = (videoBox.size.height - parentFrameSize.height) / 2; 186 | 187 | xScale = videoBox.size.width / clap.size.width; 188 | yScale = videoBox.size.height / clap.size.height; 189 | } else { 190 | videoBox.size.width = parentFrameSize.width; 191 | videoBox.size.height = clap.size.width * (parentFrameSize.width / clap.size.height); 192 | videoBox.origin.x = (videoBox.size.width - parentFrameSize.width) / 2; 193 | videoBox.origin.y = (parentFrameSize.height - videoBox.size.height) / 2; 194 | 195 | xScale = videoBox.size.width / clap.size.height; 196 | yScale = videoBox.size.height / clap.size.width; 197 | } 198 | 199 | dispatch_sync(dispatch_get_main_queue(), ^{ 200 | // Remove previously added feature views. 201 | for (UIView *featureView in self.overlayView.subviews) { 202 | [featureView removeFromSuperview]; 203 | } 204 | 205 | // Display detected features in overlay. 206 | for (GMVFaceFeature *face in faces) { 207 | CGRect faceRect = [self scaledRect:face.bounds 208 | xScale:xScale 209 | yScale:yScale 210 | offset:videoBox.origin]; 211 | [DrawingUtility addRectangle:faceRect 212 | toView:self.overlayView 213 | withColor:[UIColor redColor]]; 214 | 215 | // Mouth 216 | if (face.hasBottomMouthPosition) { 217 | CGPoint point = [self scaledPoint:face.bottomMouthPosition 218 | xScale:xScale 219 | yScale:yScale 220 | offset:videoBox.origin]; 221 | [DrawingUtility addCircleAtPoint:point 222 | toView:self.overlayView 223 | withColor:[UIColor greenColor] 224 | withRadius:5]; 225 | } 226 | if (face.hasMouthPosition) { 227 | CGPoint point = [self scaledPoint:face.mouthPosition 228 | xScale:xScale 229 | yScale:yScale 230 | offset:videoBox.origin]; 231 | [DrawingUtility addCircleAtPoint:point 232 | toView:self.overlayView 233 | withColor:[UIColor greenColor] 234 | withRadius:10]; 235 | } 236 | if (face.hasRightMouthPosition) { 237 | CGPoint point = [self scaledPoint:face.rightMouthPosition 238 | xScale:xScale 239 | yScale:yScale 240 | offset:videoBox.origin]; 241 | [DrawingUtility addCircleAtPoint:point 242 | toView:self.overlayView 243 | withColor:[UIColor greenColor] 244 | withRadius:5]; 245 | } 246 | if (face.hasLeftMouthPosition) { 247 | CGPoint point = [self scaledPoint:face.leftMouthPosition 248 | xScale:xScale 249 | yScale:yScale 250 | offset:videoBox.origin]; 251 | [DrawingUtility addCircleAtPoint:point 252 | toView:self.overlayView 253 | withColor:[UIColor greenColor] 254 | withRadius:5]; 255 | } 256 | 257 | // Nose 258 | if (face.hasNoseBasePosition) { 259 | CGPoint point = [self scaledPoint:face.noseBasePosition 260 | xScale:xScale 261 | yScale:yScale 262 | offset:videoBox.origin]; 263 | [DrawingUtility addCircleAtPoint:point 264 | toView:self.overlayView 265 | withColor:[UIColor darkGrayColor] 266 | withRadius:10]; 267 | } 268 | 269 | // Eyes 270 | if (face.hasLeftEyePosition) { 271 | CGPoint point = [self scaledPoint:face.leftEyePosition 272 | xScale:xScale 273 | yScale:yScale 274 | offset:videoBox.origin]; 275 | [DrawingUtility addCircleAtPoint:point 276 | toView:self.overlayView 277 | withColor:[UIColor blueColor] 278 | withRadius:10]; 279 | } 280 | if (face.hasRightEyePosition) { 281 | CGPoint point = [self scaledPoint:face.rightEyePosition 282 | xScale:xScale 283 | yScale:yScale 284 | offset:videoBox.origin]; 285 | [DrawingUtility addCircleAtPoint:point 286 | toView:self.overlayView 287 | withColor:[UIColor blueColor] 288 | withRadius:10]; 289 | } 290 | 291 | // Ears 292 | if (face.hasLeftEarPosition) { 293 | CGPoint point = [self scaledPoint:face.leftEarPosition 294 | xScale:xScale 295 | yScale:yScale 296 | offset:videoBox.origin]; 297 | [DrawingUtility addCircleAtPoint:point 298 | toView:self.overlayView 299 | withColor:[UIColor purpleColor] 300 | withRadius:10]; 301 | } 302 | if (face.hasRightEarPosition) { 303 | CGPoint point = [self scaledPoint:face.rightEarPosition 304 | xScale:xScale 305 | yScale:yScale 306 | offset:videoBox.origin]; 307 | [DrawingUtility addCircleAtPoint:point 308 | toView:self.overlayView 309 | withColor:[UIColor purpleColor] 310 | withRadius:10]; 311 | } 312 | 313 | // Cheeks 314 | if (face.hasLeftCheekPosition) { 315 | CGPoint point = [self scaledPoint:face.leftCheekPosition 316 | xScale:xScale 317 | yScale:yScale 318 | offset:videoBox.origin]; 319 | [DrawingUtility addCircleAtPoint:point 320 | toView:self.overlayView 321 | withColor:[UIColor magentaColor] 322 | withRadius:10]; 323 | } 324 | if (face.hasRightCheekPosition) { 325 | CGPoint point = [self scaledPoint:face.rightCheekPosition 326 | xScale:xScale 327 | yScale:yScale 328 | offset:videoBox.origin]; 329 | [DrawingUtility addCircleAtPoint:point 330 | toView:self.overlayView 331 | withColor:[UIColor magentaColor] 332 | withRadius:10]; 333 | } 334 | 335 | // Tracking Id. 336 | if (face.hasTrackingID) { 337 | CGPoint point = [self scaledPoint:face.bounds.origin 338 | xScale:xScale 339 | yScale:yScale 340 | offset:videoBox.origin]; 341 | UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(point.x, point.y, 100, 20)]; 342 | label.text = [NSString stringWithFormat:@"id: %lu", (unsigned long)face.trackingID]; 343 | [self.overlayView addSubview:label]; 344 | } 345 | } 346 | }); 347 | } 348 | 349 | #pragma mark - Camera setup 350 | 351 | - (void)cleanupVideoProcessing { 352 | if (self.videoDataOutput) { 353 | [self.session removeOutput:self.videoDataOutput]; 354 | } 355 | self.videoDataOutput = nil; 356 | } 357 | 358 | - (void)cleanupCaptureSession { 359 | [self.session stopRunning]; 360 | [self cleanupVideoProcessing]; 361 | self.session = nil; 362 | [self.previewLayer removeFromSuperlayer]; 363 | } 364 | 365 | - (void)setupVideoProcessing { 366 | self.videoDataOutput = [[AVCaptureVideoDataOutput alloc] init]; 367 | NSDictionary *rgbOutputSettings = @{ 368 | (__bridge NSString*)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) 369 | }; 370 | [self.videoDataOutput setVideoSettings:rgbOutputSettings]; 371 | 372 | if (![self.session canAddOutput:self.videoDataOutput]) { 373 | [self cleanupVideoProcessing]; 374 | NSLog(@"Failed to setup video output"); 375 | return; 376 | } 377 | [self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES]; 378 | [self.videoDataOutput setSampleBufferDelegate:self queue:self.videoDataOutputQueue]; 379 | [self.session addOutput:self.videoDataOutput]; 380 | } 381 | 382 | - (void)setupCameraPreview { 383 | self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session]; 384 | [self.previewLayer setBackgroundColor:[[UIColor whiteColor] CGColor]]; 385 | [self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect]; 386 | CALayer *rootLayer = [self.placeHolder layer]; 387 | [rootLayer setMasksToBounds:YES]; 388 | [self.previewLayer setFrame:[rootLayer bounds]]; 389 | [rootLayer addSublayer:self.previewLayer]; 390 | } 391 | 392 | - (void)updateCameraSelection { 393 | [self.session beginConfiguration]; 394 | 395 | // Remove old inputs 396 | NSArray *oldInputs = [self.session inputs]; 397 | for (AVCaptureInput *oldInput in oldInputs) { 398 | [self.session removeInput:oldInput]; 399 | } 400 | 401 | AVCaptureDevicePosition desiredPosition = self.cameraSwitch.isOn ? 402 | AVCaptureDevicePositionFront : AVCaptureDevicePositionBack; 403 | AVCaptureDeviceInput *input = [self cameraForPosition:desiredPosition]; 404 | if (!input) { 405 | // Failed, restore old inputs 406 | for (AVCaptureInput *oldInput in oldInputs) { 407 | [self.session addInput:oldInput]; 408 | } 409 | } else { 410 | // Succeeded, set input and update connection states 411 | [self.session addInput:input]; 412 | } 413 | [self.session commitConfiguration]; 414 | } 415 | 416 | - (AVCaptureDeviceInput *)cameraForPosition:(AVCaptureDevicePosition)desiredPosition { 417 | for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { 418 | if ([device position] == desiredPosition) { 419 | NSError *error = nil; 420 | AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device 421 | error:&error]; 422 | if ([self.session canAddInput:input]) { 423 | return input; 424 | } 425 | } 426 | } 427 | return nil; 428 | } 429 | 430 | - (IBAction)cameraDeviceChanged:(id)sender { 431 | [self updateCameraSelection]; 432 | } 433 | 434 | @end 435 | -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/DrawingUtility.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import Foundation; 18 | @import UIKit; 19 | 20 | @interface DrawingUtility : NSObject 21 | 22 | + (void)addCircleAtPoint:(CGPoint)point 23 | toView:(UIView *)view 24 | withColor:(UIColor *)color 25 | withRadius:(NSInteger)width; 26 | 27 | + (void)addRectangle:(CGRect)rect 28 | toView:(UIView *)view 29 | withColor:(UIColor *)color; 30 | 31 | + (void)addTextLabel:(NSString *)text 32 | atRect:(CGRect)rect 33 | toView:(UIView *)view 34 | withColor:(UIColor *)color; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/DrawingUtility.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import "DrawingUtility.h" 18 | 19 | @implementation DrawingUtility 20 | 21 | + (void)addCircleAtPoint:(CGPoint)point 22 | toView:(UIView *)view 23 | withColor:(UIColor *)color 24 | withRadius:(NSInteger)width { 25 | CGRect circleRect = CGRectMake(point.x - width / 2, point.y - width / 2, width, width); 26 | UIView *circleView = [[UIView alloc] initWithFrame:circleRect]; 27 | circleView.layer.cornerRadius = width / 2; 28 | circleView.alpha = 0.7; 29 | circleView.backgroundColor = color; 30 | [view addSubview:circleView]; 31 | } 32 | 33 | + (void)addRectangle:(CGRect)rect 34 | toView:(UIView *)view 35 | withColor:(UIColor *)color { 36 | UIView *newView = [[UIView alloc] initWithFrame:rect]; 37 | newView.layer.cornerRadius = 10; 38 | newView.alpha = 0.3; 39 | newView.backgroundColor = color; 40 | [view addSubview:newView]; 41 | } 42 | 43 | + (void)addTextLabel:(NSString *)text 44 | atRect:(CGRect)rect 45 | toView:(UIView *)view 46 | withColor:(UIColor *)color { 47 | UILabel *label = [[UILabel alloc] initWithFrame:rect]; 48 | [label setTextColor:color]; 49 | label.text = text; 50 | [view addSubview:label]; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarTintParameters 34 | 35 | UINavigationBar 36 | 37 | Style 38 | UIBarStyleDefault 39 | Translucent 40 | 41 | 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | UIInterfaceOrientationPortraitUpsideDown 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/PhotoViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import UIKit; 18 | 19 | // View controller demonstrating how to use face detector with a static image. 20 | @interface PhotoViewController : UIViewController 21 | 22 | @end 23 | 24 | -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/PhotoViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import GoogleMobileVision; 18 | 19 | #import "PhotoViewController.h" 20 | #import "DrawingUtility.h" 21 | 22 | @interface PhotoViewController () 23 | 24 | // UI views. 25 | @property(nonatomic, weak) IBOutlet UIImageView *faceImageView; 26 | @property(nonatomic, weak) IBOutlet UIView *overlayView; 27 | 28 | // Face detector. 29 | @property(nonatomic, strong) GMVDetector *faceDetector; 30 | 31 | @end 32 | 33 | @implementation PhotoViewController 34 | 35 | - (void)viewDidLoad { 36 | [super viewDidLoad]; 37 | 38 | // Instantiate a face detector that searches for all landmarks and classifications. 39 | NSDictionary *options = @{ 40 | GMVDetectorFaceLandmarkType : @(GMVDetectorFaceLandmarkAll), 41 | GMVDetectorFaceClassificationType : @(GMVDetectorFaceClassificationAll), 42 | GMVDetectorFaceMinSize : @(0.3), 43 | GMVDetectorFaceTrackingEnabled : @(NO) 44 | }; 45 | self.faceDetector = [GMVDetector detectorOfType:GMVDetectorTypeFace options:options]; 46 | } 47 | 48 | - (IBAction)faceRecognitionClicked:(id)sender { 49 | for (UIView *annotationView in [self.overlayView subviews]) { 50 | [annotationView removeFromSuperview]; 51 | } 52 | 53 | // Invoke features detection. 54 | NSArray *faces = [self.faceDetector featuresInImage:self.faceImageView.image 55 | options:nil]; 56 | 57 | // Compute image offset. 58 | CGAffineTransform translate = CGAffineTransformTranslate(CGAffineTransformIdentity, 59 | (self.view.frame.size.width - self.faceImageView.image.size.width) / 2, 60 | (self.view.frame.size.height - self.faceImageView.image.size.height) / 2); 61 | 62 | // Add annotation view for each detected face. 63 | for (GMVFaceFeature *face in faces) { 64 | // Face 65 | CGRect rect = face.bounds; 66 | [DrawingUtility addRectangle:CGRectApplyAffineTransform(rect, translate) 67 | toView:self.overlayView 68 | withColor:[UIColor redColor]]; 69 | 70 | // Mouth 71 | if (face.hasBottomMouthPosition) { 72 | CGPoint point = CGPointApplyAffineTransform(face.bottomMouthPosition, translate); 73 | [DrawingUtility addCircleAtPoint:point 74 | toView:self.overlayView 75 | withColor:[UIColor greenColor] 76 | withRadius:2]; 77 | } 78 | if (face.hasMouthPosition) { 79 | [DrawingUtility addCircleAtPoint:CGPointApplyAffineTransform(face.mouthPosition, translate) 80 | toView:self.overlayView 81 | withColor:[UIColor greenColor] 82 | withRadius:2]; 83 | } 84 | if (face.hasRightMouthPosition) { 85 | CGPoint point = CGPointApplyAffineTransform(face.rightMouthPosition, translate); 86 | [DrawingUtility addCircleAtPoint:point 87 | toView:self.overlayView 88 | withColor:[UIColor greenColor] 89 | withRadius:2]; 90 | } 91 | if (face.hasLeftMouthPosition) { 92 | CGPoint point = CGPointApplyAffineTransform(face.leftMouthPosition, translate); 93 | [DrawingUtility addCircleAtPoint:point 94 | toView:self.overlayView 95 | withColor:[UIColor greenColor] 96 | withRadius:2]; 97 | } 98 | 99 | // Nose 100 | if (face.hasNoseBasePosition) { 101 | [DrawingUtility addCircleAtPoint:CGPointApplyAffineTransform(face.noseBasePosition, translate) 102 | toView:self.overlayView 103 | withColor:[UIColor darkGrayColor] 104 | withRadius:4]; 105 | } 106 | 107 | // Eyes 108 | if (face.hasLeftEyePosition) { 109 | [DrawingUtility addCircleAtPoint:CGPointApplyAffineTransform(face.leftEyePosition, translate) 110 | toView:self.overlayView 111 | withColor:[UIColor blueColor] 112 | withRadius:4]; 113 | } 114 | if (face.hasRightEyePosition) { 115 | [DrawingUtility addCircleAtPoint:CGPointApplyAffineTransform(face.rightEyePosition, translate) 116 | toView:self.overlayView 117 | withColor:[UIColor blueColor] 118 | withRadius:4]; 119 | } 120 | 121 | // Ears 122 | if (face.hasLeftEarPosition) { 123 | [DrawingUtility addCircleAtPoint:CGPointApplyAffineTransform(face.leftEarPosition, translate) 124 | toView:self.overlayView 125 | withColor:[UIColor purpleColor] 126 | withRadius:4]; 127 | } 128 | if (face.hasRightEarPosition) { 129 | [DrawingUtility addCircleAtPoint:CGPointApplyAffineTransform(face.rightEarPosition, translate) 130 | toView:self.overlayView 131 | withColor:[UIColor purpleColor] 132 | withRadius:4]; 133 | } 134 | 135 | // Cheeks 136 | if (face.hasLeftCheekPosition) { 137 | CGPoint point = CGPointApplyAffineTransform(face.leftCheekPosition, translate); 138 | [DrawingUtility addCircleAtPoint:point 139 | toView:self.overlayView 140 | withColor:[UIColor magentaColor] 141 | withRadius:4]; 142 | } 143 | if (face.hasRightCheekPosition) { 144 | CGPoint point = CGPointApplyAffineTransform(face.rightCheekPosition, translate); 145 | [DrawingUtility addCircleAtPoint:point 146 | toView:self.overlayView 147 | withColor:[UIColor magentaColor] 148 | withRadius:4]; 149 | } 150 | 151 | // Smiling 152 | if (face.hasSmilingProbability && face.smilingProbability > 0.4) { 153 | NSString *text = [NSString stringWithFormat:@"smiling %0.2f", face.smilingProbability]; 154 | CGRect rect = CGRectMake(CGRectGetMinX(face.bounds), 155 | CGRectGetMaxY(face.bounds) + 10, 156 | self.overlayView.frame.size.width, 157 | 30); 158 | rect = CGRectApplyAffineTransform(rect, translate); 159 | [DrawingUtility addTextLabel:text 160 | atRect:rect 161 | toView:self.overlayView 162 | withColor:[UIColor greenColor]]; 163 | 164 | } 165 | } 166 | 167 | } 168 | 169 | @end 170 | -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/main.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | #import "AppDelegate.h" 19 | 20 | int main(int argc, char *argv[]) { 21 | @autoreleasepool { 22 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /FaceDetectorDemo/FaceDetector/multi-face.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/FaceDetectorDemo/FaceDetector/multi-face.png -------------------------------------------------------------------------------- /FaceDetectorDemo/Podfile: -------------------------------------------------------------------------------- 1 | target "FaceDetector" do 2 | pod 'GoogleMobileVision/FaceDetector' 3 | end 4 | -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6F0165821D25DB5000F2722F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0165811D25DB5000F2722F /* main.m */; }; 11 | 6F0165851D25DB5000F2722F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0165841D25DB5000F2722F /* AppDelegate.m */; }; 12 | 6F0165881D25DB5000F2722F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0165871D25DB5000F2722F /* ViewController.m */; }; 13 | 6F01658B1D25DB5000F2722F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6F0165891D25DB5000F2722F /* Main.storyboard */; }; 14 | 6F01658D1D25DB5000F2722F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6F01658C1D25DB5000F2722F /* Assets.xcassets */; }; 15 | 6F0165901D25DB5000F2722F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6F01658E1D25DB5000F2722F /* LaunchScreen.storyboard */; }; 16 | 6F0166101D2713B600F2722F /* GooglyEyeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F01660F1D2713B600F2722F /* GooglyEyeView.m */; }; 17 | 6F0166131D2713BF00F2722F /* EyePhysics.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0166121D2713BF00F2722F /* EyePhysics.m */; }; 18 | 6F0166161D2713C800F2722F /* FaceTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F0166151D2713C800F2722F /* FaceTracker.m */; }; 19 | DD35709111882BE5D3319F64 /* Pods_GooglyEyes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 19CBFEE151D40A14EC58BD17 /* Pods_GooglyEyes.framework */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | 19CBFEE151D40A14EC58BD17 /* Pods_GooglyEyes.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_GooglyEyes.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | 6F01657D1D25DB5000F2722F /* GooglyEyes.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GooglyEyes.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | 6F0165811D25DB5000F2722F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 26 | 6F0165831D25DB5000F2722F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 27 | 6F0165841D25DB5000F2722F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 28 | 6F0165861D25DB5000F2722F /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 29 | 6F0165871D25DB5000F2722F /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 30 | 6F01658A1D25DB5000F2722F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 31 | 6F01658C1D25DB5000F2722F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 32 | 6F01658F1D25DB5000F2722F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 33 | 6F0165911D25DB5000F2722F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34 | 6F01660E1D2713B600F2722F /* GooglyEyeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GooglyEyeView.h; sourceTree = ""; }; 35 | 6F01660F1D2713B600F2722F /* GooglyEyeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GooglyEyeView.m; sourceTree = ""; }; 36 | 6F0166111D2713BF00F2722F /* EyePhysics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EyePhysics.h; sourceTree = ""; }; 37 | 6F0166121D2713BF00F2722F /* EyePhysics.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EyePhysics.m; sourceTree = ""; }; 38 | 6F0166141D2713C800F2722F /* FaceTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FaceTracker.h; sourceTree = ""; }; 39 | 6F0166151D2713C800F2722F /* FaceTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FaceTracker.m; sourceTree = ""; }; 40 | E364F12AA77C2688759FA0D0 /* Pods-GooglyEyes.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GooglyEyes.release.xcconfig"; path = "Pods/Target Support Files/Pods-GooglyEyes/Pods-GooglyEyes.release.xcconfig"; sourceTree = ""; }; 41 | E924D0460DF0B29C71E44565 /* Pods-GooglyEyes.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GooglyEyes.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GooglyEyes/Pods-GooglyEyes.debug.xcconfig"; sourceTree = ""; }; 42 | /* End PBXFileReference section */ 43 | 44 | /* Begin PBXFrameworksBuildPhase section */ 45 | 6F01657A1D25DB5000F2722F /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | DD35709111882BE5D3319F64 /* Pods_GooglyEyes.framework in Frameworks */, 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXFrameworksBuildPhase section */ 54 | 55 | /* Begin PBXGroup section */ 56 | 6F0165741D25DB5000F2722F = { 57 | isa = PBXGroup; 58 | children = ( 59 | 6F01657F1D25DB5000F2722F /* GooglyEyes */, 60 | 6F01657E1D25DB5000F2722F /* Products */, 61 | D750CF79B735B531A9ED71A5 /* Pods */, 62 | E0043F4BFD0DDB8A8E64EDC6 /* Frameworks */, 63 | ); 64 | sourceTree = ""; 65 | }; 66 | 6F01657E1D25DB5000F2722F /* Products */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 6F01657D1D25DB5000F2722F /* GooglyEyes.app */, 70 | ); 71 | name = Products; 72 | sourceTree = ""; 73 | }; 74 | 6F01657F1D25DB5000F2722F /* GooglyEyes */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 6F0166141D2713C800F2722F /* FaceTracker.h */, 78 | 6F0166151D2713C800F2722F /* FaceTracker.m */, 79 | 6F0166111D2713BF00F2722F /* EyePhysics.h */, 80 | 6F0166121D2713BF00F2722F /* EyePhysics.m */, 81 | 6F01660E1D2713B600F2722F /* GooglyEyeView.h */, 82 | 6F01660F1D2713B600F2722F /* GooglyEyeView.m */, 83 | 6F0165831D25DB5000F2722F /* AppDelegate.h */, 84 | 6F0165841D25DB5000F2722F /* AppDelegate.m */, 85 | 6F0165861D25DB5000F2722F /* ViewController.h */, 86 | 6F0165871D25DB5000F2722F /* ViewController.m */, 87 | 6F0165891D25DB5000F2722F /* Main.storyboard */, 88 | 6F01658C1D25DB5000F2722F /* Assets.xcassets */, 89 | 6F01658E1D25DB5000F2722F /* LaunchScreen.storyboard */, 90 | 6F0165911D25DB5000F2722F /* Info.plist */, 91 | 6F0165801D25DB5000F2722F /* Supporting Files */, 92 | ); 93 | path = GooglyEyes; 94 | sourceTree = ""; 95 | }; 96 | 6F0165801D25DB5000F2722F /* Supporting Files */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 6F0165811D25DB5000F2722F /* main.m */, 100 | ); 101 | name = "Supporting Files"; 102 | sourceTree = ""; 103 | }; 104 | D750CF79B735B531A9ED71A5 /* Pods */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | E924D0460DF0B29C71E44565 /* Pods-GooglyEyes.debug.xcconfig */, 108 | E364F12AA77C2688759FA0D0 /* Pods-GooglyEyes.release.xcconfig */, 109 | ); 110 | name = Pods; 111 | sourceTree = ""; 112 | }; 113 | E0043F4BFD0DDB8A8E64EDC6 /* Frameworks */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 19CBFEE151D40A14EC58BD17 /* Pods_GooglyEyes.framework */, 117 | ); 118 | name = Frameworks; 119 | sourceTree = ""; 120 | }; 121 | /* End PBXGroup section */ 122 | 123 | /* Begin PBXNativeTarget section */ 124 | 6F01657C1D25DB5000F2722F /* GooglyEyes */ = { 125 | isa = PBXNativeTarget; 126 | buildConfigurationList = 6F0165941D25DB5000F2722F /* Build configuration list for PBXNativeTarget "GooglyEyes" */; 127 | buildPhases = ( 128 | 41D0DECFE5ACA7713255C307 /* [CP] Check Pods Manifest.lock */, 129 | 6F0165791D25DB5000F2722F /* Sources */, 130 | 6F01657A1D25DB5000F2722F /* Frameworks */, 131 | 6F01657B1D25DB5000F2722F /* Resources */, 132 | F30961F9451EE34852CAF28B /* [CP] Embed Pods Frameworks */, 133 | 2A2EA62B8BF002CD9351860B /* [CP] Copy Pods Resources */, 134 | ); 135 | buildRules = ( 136 | ); 137 | dependencies = ( 138 | ); 139 | name = GooglyEyes; 140 | productName = GooglyEyes; 141 | productReference = 6F01657D1D25DB5000F2722F /* GooglyEyes.app */; 142 | productType = "com.apple.product-type.application"; 143 | }; 144 | /* End PBXNativeTarget section */ 145 | 146 | /* Begin PBXProject section */ 147 | 6F0165751D25DB5000F2722F /* Project object */ = { 148 | isa = PBXProject; 149 | attributes = { 150 | LastUpgradeCheck = 0720; 151 | ORGANIZATIONNAME = Google; 152 | TargetAttributes = { 153 | 6F01657C1D25DB5000F2722F = { 154 | CreatedOnToolsVersion = 7.2.1; 155 | }; 156 | }; 157 | }; 158 | buildConfigurationList = 6F0165781D25DB5000F2722F /* Build configuration list for PBXProject "GooglyEyes" */; 159 | compatibilityVersion = "Xcode 3.2"; 160 | developmentRegion = English; 161 | hasScannedForEncodings = 0; 162 | knownRegions = ( 163 | en, 164 | Base, 165 | ); 166 | mainGroup = 6F0165741D25DB5000F2722F; 167 | productRefGroup = 6F01657E1D25DB5000F2722F /* Products */; 168 | projectDirPath = ""; 169 | projectRoot = ""; 170 | targets = ( 171 | 6F01657C1D25DB5000F2722F /* GooglyEyes */, 172 | ); 173 | }; 174 | /* End PBXProject section */ 175 | 176 | /* Begin PBXResourcesBuildPhase section */ 177 | 6F01657B1D25DB5000F2722F /* Resources */ = { 178 | isa = PBXResourcesBuildPhase; 179 | buildActionMask = 2147483647; 180 | files = ( 181 | 6F0165901D25DB5000F2722F /* LaunchScreen.storyboard in Resources */, 182 | 6F01658D1D25DB5000F2722F /* Assets.xcassets in Resources */, 183 | 6F01658B1D25DB5000F2722F /* Main.storyboard in Resources */, 184 | ); 185 | runOnlyForDeploymentPostprocessing = 0; 186 | }; 187 | /* End PBXResourcesBuildPhase section */ 188 | 189 | /* Begin PBXShellScriptBuildPhase section */ 190 | 2A2EA62B8BF002CD9351860B /* [CP] Copy Pods Resources */ = { 191 | isa = PBXShellScriptBuildPhase; 192 | buildActionMask = 2147483647; 193 | files = ( 194 | ); 195 | inputPaths = ( 196 | ); 197 | name = "[CP] Copy Pods Resources"; 198 | outputPaths = ( 199 | ); 200 | runOnlyForDeploymentPostprocessing = 0; 201 | shellPath = /bin/sh; 202 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-GooglyEyes/Pods-GooglyEyes-resources.sh\"\n"; 203 | showEnvVarsInLog = 0; 204 | }; 205 | 41D0DECFE5ACA7713255C307 /* [CP] Check Pods Manifest.lock */ = { 206 | isa = PBXShellScriptBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | ); 210 | inputPaths = ( 211 | ); 212 | name = "[CP] Check Pods Manifest.lock"; 213 | outputPaths = ( 214 | ); 215 | runOnlyForDeploymentPostprocessing = 0; 216 | shellPath = /bin/sh; 217 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 218 | showEnvVarsInLog = 0; 219 | }; 220 | F30961F9451EE34852CAF28B /* [CP] Embed Pods Frameworks */ = { 221 | isa = PBXShellScriptBuildPhase; 222 | buildActionMask = 2147483647; 223 | files = ( 224 | ); 225 | inputPaths = ( 226 | ); 227 | name = "[CP] Embed Pods Frameworks"; 228 | outputPaths = ( 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | shellPath = /bin/sh; 232 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-GooglyEyes/Pods-GooglyEyes-frameworks.sh\"\n"; 233 | showEnvVarsInLog = 0; 234 | }; 235 | /* End PBXShellScriptBuildPhase section */ 236 | 237 | /* Begin PBXSourcesBuildPhase section */ 238 | 6F0165791D25DB5000F2722F /* Sources */ = { 239 | isa = PBXSourcesBuildPhase; 240 | buildActionMask = 2147483647; 241 | files = ( 242 | 6F0165881D25DB5000F2722F /* ViewController.m in Sources */, 243 | 6F0166161D2713C800F2722F /* FaceTracker.m in Sources */, 244 | 6F0166101D2713B600F2722F /* GooglyEyeView.m in Sources */, 245 | 6F0165851D25DB5000F2722F /* AppDelegate.m in Sources */, 246 | 6F0166131D2713BF00F2722F /* EyePhysics.m in Sources */, 247 | 6F0165821D25DB5000F2722F /* main.m in Sources */, 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | /* End PBXSourcesBuildPhase section */ 252 | 253 | /* Begin PBXVariantGroup section */ 254 | 6F0165891D25DB5000F2722F /* Main.storyboard */ = { 255 | isa = PBXVariantGroup; 256 | children = ( 257 | 6F01658A1D25DB5000F2722F /* Base */, 258 | ); 259 | name = Main.storyboard; 260 | sourceTree = ""; 261 | }; 262 | 6F01658E1D25DB5000F2722F /* LaunchScreen.storyboard */ = { 263 | isa = PBXVariantGroup; 264 | children = ( 265 | 6F01658F1D25DB5000F2722F /* Base */, 266 | ); 267 | name = LaunchScreen.storyboard; 268 | sourceTree = ""; 269 | }; 270 | /* End PBXVariantGroup section */ 271 | 272 | /* Begin XCBuildConfiguration section */ 273 | 6F0165921D25DB5000F2722F /* Debug */ = { 274 | isa = XCBuildConfiguration; 275 | buildSettings = { 276 | ALWAYS_SEARCH_USER_PATHS = NO; 277 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 278 | CLANG_CXX_LIBRARY = "libc++"; 279 | CLANG_ENABLE_MODULES = YES; 280 | CLANG_ENABLE_OBJC_ARC = YES; 281 | CLANG_WARN_BOOL_CONVERSION = YES; 282 | CLANG_WARN_CONSTANT_CONVERSION = YES; 283 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 284 | CLANG_WARN_EMPTY_BODY = YES; 285 | CLANG_WARN_ENUM_CONVERSION = YES; 286 | CLANG_WARN_INT_CONVERSION = YES; 287 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 288 | CLANG_WARN_UNREACHABLE_CODE = YES; 289 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 290 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 291 | COPY_PHASE_STRIP = NO; 292 | DEBUG_INFORMATION_FORMAT = dwarf; 293 | ENABLE_STRICT_OBJC_MSGSEND = YES; 294 | ENABLE_TESTABILITY = YES; 295 | GCC_C_LANGUAGE_STANDARD = gnu99; 296 | GCC_DYNAMIC_NO_PIC = NO; 297 | GCC_NO_COMMON_BLOCKS = YES; 298 | GCC_OPTIMIZATION_LEVEL = 0; 299 | GCC_PREPROCESSOR_DEFINITIONS = ( 300 | "DEBUG=1", 301 | "$(inherited)", 302 | ); 303 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 304 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 305 | GCC_WARN_UNDECLARED_SELECTOR = YES; 306 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 307 | GCC_WARN_UNUSED_FUNCTION = YES; 308 | GCC_WARN_UNUSED_VARIABLE = YES; 309 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 310 | MTL_ENABLE_DEBUG_INFO = YES; 311 | ONLY_ACTIVE_ARCH = YES; 312 | SDKROOT = iphoneos; 313 | }; 314 | name = Debug; 315 | }; 316 | 6F0165931D25DB5000F2722F /* Release */ = { 317 | isa = XCBuildConfiguration; 318 | buildSettings = { 319 | ALWAYS_SEARCH_USER_PATHS = NO; 320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 321 | CLANG_CXX_LIBRARY = "libc++"; 322 | CLANG_ENABLE_MODULES = YES; 323 | CLANG_ENABLE_OBJC_ARC = YES; 324 | CLANG_WARN_BOOL_CONVERSION = YES; 325 | CLANG_WARN_CONSTANT_CONVERSION = YES; 326 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 327 | CLANG_WARN_EMPTY_BODY = YES; 328 | CLANG_WARN_ENUM_CONVERSION = YES; 329 | CLANG_WARN_INT_CONVERSION = YES; 330 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 331 | CLANG_WARN_UNREACHABLE_CODE = YES; 332 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 333 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 334 | COPY_PHASE_STRIP = NO; 335 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 336 | ENABLE_NS_ASSERTIONS = NO; 337 | ENABLE_STRICT_OBJC_MSGSEND = YES; 338 | GCC_C_LANGUAGE_STANDARD = gnu99; 339 | GCC_NO_COMMON_BLOCKS = YES; 340 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 341 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 342 | GCC_WARN_UNDECLARED_SELECTOR = YES; 343 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 344 | GCC_WARN_UNUSED_FUNCTION = YES; 345 | GCC_WARN_UNUSED_VARIABLE = YES; 346 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 347 | MTL_ENABLE_DEBUG_INFO = NO; 348 | SDKROOT = iphoneos; 349 | VALIDATE_PRODUCT = YES; 350 | }; 351 | name = Release; 352 | }; 353 | 6F0165951D25DB5000F2722F /* Debug */ = { 354 | isa = XCBuildConfiguration; 355 | baseConfigurationReference = E924D0460DF0B29C71E44565 /* Pods-GooglyEyes.debug.xcconfig */; 356 | buildSettings = { 357 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 358 | INFOPLIST_FILE = GooglyEyes/Info.plist; 359 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 360 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 361 | PRODUCT_BUNDLE_IDENTIFIER = com.google.mobilevision.GooglyEyes; 362 | PRODUCT_NAME = "$(TARGET_NAME)"; 363 | }; 364 | name = Debug; 365 | }; 366 | 6F0165961D25DB5000F2722F /* Release */ = { 367 | isa = XCBuildConfiguration; 368 | baseConfigurationReference = E364F12AA77C2688759FA0D0 /* Pods-GooglyEyes.release.xcconfig */; 369 | buildSettings = { 370 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 371 | INFOPLIST_FILE = GooglyEyes/Info.plist; 372 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 373 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 374 | PRODUCT_BUNDLE_IDENTIFIER = com.google.mobilevision.GooglyEyes; 375 | PRODUCT_NAME = "$(TARGET_NAME)"; 376 | }; 377 | name = Release; 378 | }; 379 | /* End XCBuildConfiguration section */ 380 | 381 | /* Begin XCConfigurationList section */ 382 | 6F0165781D25DB5000F2722F /* Build configuration list for PBXProject "GooglyEyes" */ = { 383 | isa = XCConfigurationList; 384 | buildConfigurations = ( 385 | 6F0165921D25DB5000F2722F /* Debug */, 386 | 6F0165931D25DB5000F2722F /* Release */, 387 | ); 388 | defaultConfigurationIsVisible = 0; 389 | defaultConfigurationName = Release; 390 | }; 391 | 6F0165941D25DB5000F2722F /* Build configuration list for PBXNativeTarget "GooglyEyes" */ = { 392 | isa = XCConfigurationList; 393 | buildConfigurations = ( 394 | 6F0165951D25DB5000F2722F /* Debug */, 395 | 6F0165961D25DB5000F2722F /* Release */, 396 | ); 397 | defaultConfigurationIsVisible = 0; 398 | defaultConfigurationName = Release; 399 | }; 400 | /* End XCConfigurationList section */ 401 | }; 402 | rootObject = 6F0165751D25DB5000F2722F /* Project object */; 403 | } 404 | -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import UIKit; 18 | 19 | @interface AppDelegate : UIResponder 20 | 21 | @property(nonatomic, strong) UIWindow *window; 22 | 23 | @end 24 | 25 | -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import "AppDelegate.h" 18 | 19 | @implementation AppDelegate 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "size" : "60x60", 25 | "idiom" : "iphone", 26 | "filename" : "icon_120.png", 27 | "scale" : "2x" 28 | }, 29 | { 30 | "size" : "60x60", 31 | "idiom" : "iphone", 32 | "filename" : "logo_180.png", 33 | "scale" : "3x" 34 | } 35 | ], 36 | "info" : { 37 | "version" : 1, 38 | "author" : "xcode" 39 | } 40 | } -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/Assets.xcassets/AppIcon.appiconset/icon_120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/GooglyEyesDemo/GooglyEyes/Assets.xcassets/AppIcon.appiconset/icon_120.png -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/Assets.xcassets/AppIcon.appiconset/logo_180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/GooglyEyesDemo/GooglyEyes/Assets.xcassets/AppIcon.appiconset/logo_180.png -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/EyePhysics.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | @import UIKit; 17 | 18 | // Simulates the physics of motion for an iris which moves within a googly eye. The iris moves 19 | // independently of the motion of the face/eye. 20 | @interface EyePhysics : NSObject 21 | 22 | // Generates the next position of the iris based on simulated velocity, eye boundaries, gravity, 23 | // friction, and bounce momentum. 24 | // @param eyeRect the current eye rect in parent view coordinates. 25 | // @param irisRect the last computed iris rect. 26 | - (CGRect)nextIrisRectFrom:(CGRect)eyeRect withIrisRect:(CGRect)irisRect; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/EyePhysics.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import "EyePhysics.h" 18 | 19 | CGFloat const kFriction = 2.2; 20 | CGFloat const kGravity = 10; 21 | CGFloat const kBounceMultiplier = 20; 22 | CGFloat const kZeroTolerance = 0.001; 23 | CGFloat const kIrisRatio = 0.45; 24 | 25 | @interface EyePhysics() 26 | 27 | @property(nonatomic, strong) NSDate *lastUpdated; 28 | @property(nonatomic, assign) CGFloat xVelocity; 29 | @property(nonatomic, assign) CGFloat yVelocity; 30 | @property(nonatomic, assign) NSUInteger consecutiveBounces; 31 | 32 | @end 33 | 34 | @implementation EyePhysics 35 | 36 | - (instancetype)init { 37 | self = [super init]; 38 | if (self) { 39 | self.lastUpdated = [NSDate date]; 40 | } 41 | return self; 42 | } 43 | 44 | // Generate the next position of the iris based on simulated velocity, eye boundaries, gravity, 45 | // friction, and bounce momentum. This is independent from face/eye motion. 46 | - (CGRect)nextIrisRectFrom:(CGRect)eyeRect withIrisRect:(CGRect)irisRect { 47 | 48 | NSDate *now = [NSDate date]; 49 | NSTimeInterval elapsed = [now timeIntervalSinceDate:self.lastUpdated]; 50 | self.lastUpdated = now; 51 | CGFloat irisRadius = eyeRect.size.width * kIrisRatio / 2; 52 | 53 | if (CGRectIsNull(irisRect) || CGRectIsEmpty(irisRect) || CGRectIsInfinite(irisRect)) { 54 | // Initialize eyeball at the top of the eye. 55 | irisRect = CGRectMake(CGRectGetMidX(eyeRect) - irisRadius, 56 | eyeRect.origin.y, 57 | irisRadius * 2, 58 | irisRadius * 2); 59 | } 60 | 61 | if (![self isStopped:eyeRect irisRect:irisRect]) { 62 | // Only apply gravity when the iris is not stopped at the bottom of the eye. 63 | self.yVelocity += kGravity * elapsed; 64 | } 65 | 66 | // Apply friction in the opposite direction of motion, so that the iris slows in the absence 67 | // of other head motion. 68 | self.xVelocity = [self applyFriction:self.xVelocity simulationRate:elapsed]; 69 | self.yVelocity = [self applyFriction:self.yVelocity simulationRate:elapsed]; 70 | 71 | // Update iris rect based on velocity. 72 | CGFloat irisX = irisRect.origin.x + (self.xVelocity * irisRadius * elapsed); 73 | CGFloat irisY = irisRect.origin.y + (self.yVelocity * irisRadius * elapsed); 74 | 75 | CGRect nextIris = CGRectMake(irisX, irisY, irisRect.size.width, irisRect.size.height); 76 | nextIris = [self makeIris:nextIris inEyeBounds:eyeRect simulationRate:elapsed]; 77 | 78 | return nextIris; 79 | } 80 | 81 | // The iris is stopped if it is at the bottom of the eye and its velocity is zero. 82 | - (BOOL)isStopped:(CGRect)eyeRect irisRect:(CGRect)irisRect { 83 | if (CGRectContainsRect(eyeRect, irisRect)) { 84 | return false; 85 | } 86 | CGFloat offsetY = CGRectGetMaxY(irisRect) - CGRectGetMaxY(eyeRect); 87 | CGFloat maxDistance = (eyeRect.size.height - irisRect.size.height) / 2; 88 | if (offsetY < maxDistance) { 89 | return false; 90 | } 91 | return [self isZero:self.xVelocity] && [self isZero:self.yVelocity]; 92 | } 93 | 94 | - (BOOL)isZero:(CGFloat)number { 95 | return isnan(number) || (number < kZeroTolerance && number > -kZeroTolerance); 96 | } 97 | 98 | // Friction slows velocity in the opposite direction of motion, until zero velocity is reached. 99 | - (CGFloat)applyFriction:(CGFloat)velocity simulationRate:(NSTimeInterval)elapsed { 100 | if ([self isZero:velocity]) { 101 | velocity = 0; 102 | } else if (velocity > 0) { 103 | velocity = fmaxf(0, velocity - kFriction * elapsed); 104 | } else { 105 | velocity = fminf(0, velocity + kFriction * elapsed); 106 | } 107 | return velocity; 108 | } 109 | 110 | // Correct the iris position to be in-bounds within the eye, if it is now out of bounds. Being 111 | // out of bounds could have been due to a sudden movement of the head and/or camera, or the 112 | // result of just bouncing/rolling around. 113 | // In addition, modify the velocity to cause a bounce in the opposite direction. 114 | - (CGRect)makeIris:(CGRect)nextIrisRect 115 | inEyeBounds:(CGRect)eyeRect 116 | simulationRate:(NSTimeInterval)elapsed { 117 | 118 | if (CGRectContainsRect(eyeRect, nextIrisRect)) { 119 | self.consecutiveBounces = 0; 120 | return nextIrisRect; 121 | } 122 | 123 | // Accumulate a consecutive bounce count to aid for velocity calculation. 124 | self.consecutiveBounces++; 125 | 126 | // Move the iris back to where it would have been when it would have contacted the side of 127 | // the eye. 128 | CGPoint newOrigin = nextIrisRect.origin; 129 | CGRect intersectRect = CGRectIntersection(eyeRect, nextIrisRect); 130 | if (!CGRectIsNull(intersectRect)) { 131 | // Handle overlapping case. 132 | newOrigin.x += (intersectRect.origin.x <= nextIrisRect.origin.x ? -1 : 1) * 133 | (nextIrisRect.size.width - intersectRect.size.width); 134 | newOrigin.y += (intersectRect.origin.y > eyeRect.origin.y ? -1 : 1) * 135 | (nextIrisRect.size.height - intersectRect.size.height); 136 | } else { 137 | // Handle not overlapping case. 138 | if (nextIrisRect.origin.x < eyeRect.origin.x) { 139 | // Iris to the left of the eye. 140 | newOrigin.x = eyeRect.origin.x; 141 | } else { 142 | // Iris to the right of the eye. 143 | newOrigin.x = eyeRect.origin.x + eyeRect.size.width - nextIrisRect.size.width; 144 | } 145 | if (nextIrisRect.origin.y < eyeRect.origin.y) { 146 | // Iris to the top of the eye. 147 | newOrigin.y = eyeRect.origin.y; 148 | } else { 149 | // Iris to the bottom of the eye. 150 | newOrigin.y = eyeRect.origin.y + eyeRect.size.height - nextIrisRect.size.height; 151 | } 152 | } 153 | 154 | // Update the velocity direction and magnitude to cause a bounce. 155 | CGFloat dx = newOrigin.x - nextIrisRect.origin.x; 156 | self.xVelocity = [self applyBounce:self.xVelocity 157 | distanceOutBound:dx 158 | simulationRate:elapsed 159 | irisRect:nextIrisRect] / self.consecutiveBounces; 160 | CGFloat dy = newOrigin.y - nextIrisRect.origin.y; 161 | self.yVelocity = [self applyBounce:self.yVelocity 162 | distanceOutBound:dy 163 | simulationRate:elapsed 164 | irisRect:nextIrisRect] / self.consecutiveBounces; 165 | return CGRectMake(newOrigin.x, newOrigin.y, nextIrisRect.size.width, nextIrisRect.size.height); 166 | } 167 | 168 | - (CGFloat)applyBounce:(CGFloat)velocity 169 | distanceOutBound:(CGFloat)distance 170 | simulationRate:(NSTimeInterval)elapsed 171 | irisRect:(CGRect)irisRect { 172 | if ([self isZero:distance]) { 173 | return velocity; 174 | } 175 | velocity *= -1; 176 | 177 | CGFloat bounce = kBounceMultiplier * fabs(distance / irisRect.size.width / 2); 178 | if (velocity > 0) { 179 | velocity += bounce * elapsed; 180 | } else { 181 | velocity -= bounce * elapsed; 182 | } 183 | return velocity; 184 | } 185 | 186 | @end 187 | -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/FaceTracker.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import Foundation; 18 | @import GoogleMVDataOutput; 19 | 20 | // The data source provides the FaceTracker object with the information it needs to display 21 | // superimposed googly eyes. 22 | @protocol FaceTrackerDatasource 23 | 24 | // Display scaling offset. 25 | - (CGFloat)xScale; 26 | - (CGFloat)yScale; 27 | - (CGPoint)offset; 28 | 29 | // View to display googly eyes. 30 | - (UIView *)overlayView; 31 | 32 | @end 33 | 34 | // Manages GooglyEyeViews. This class implements GMVOutputTrackerDelegate to receive 35 | // face and landmarks tracking notifications. It updates the GooglyEyeViews' positions and 36 | // sizes accordingly. 37 | @interface FaceTracker : NSObject 38 | 39 | @property(nonatomic, weak) id delegate; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/FaceTracker.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import "FaceTracker.h" 18 | #import "GooglyEyeView.h" 19 | 20 | @interface FaceTracker() 21 | 22 | @property(nonatomic, strong) GooglyEyeView *leftEyeView; 23 | @property(nonatomic, strong) GooglyEyeView *rightEyeView; 24 | @property(nonatomic, assign) CGPoint lastLeftEyePosition; 25 | @property(nonatomic, assign) CGPoint lastRightEyePosition; 26 | 27 | @end 28 | 29 | @implementation FaceTracker 30 | 31 | #pragma mark - GMVOutputTrackerDelegate 32 | 33 | - (void)dataOutput:(GMVDataOutput *)dataOutput detectedFeature:(GMVFeature *)feature { 34 | self.leftEyeView = [[GooglyEyeView alloc] init]; 35 | self.rightEyeView = [[GooglyEyeView alloc] init]; 36 | [[self.delegate overlayView] addSubview:self.leftEyeView]; 37 | [[self.delegate overlayView] addSubview:self.rightEyeView]; 38 | } 39 | 40 | - (void)dataOutput:(GMVDataOutput *)dataOutput 41 | updateFocusingFeature:(GMVFaceFeature *)face 42 | forResultSet:(NSArray *)features { 43 | self.leftEyeView.hidden = NO; 44 | self.rightEyeView.hidden = NO; 45 | 46 | // Update left eye rect. 47 | CGPoint newLeftEyePosition = face.hasLeftEyePosition ? face.leftEyePosition : CGPointZero; 48 | CGRect leftEyeRect = [self eyeRect:self.lastLeftEyePosition 49 | newEyePosition:newLeftEyePosition 50 | faceRect:face.bounds]; 51 | [self.leftEyeView updateEyeRect:leftEyeRect]; 52 | 53 | // Update right eye rect. 54 | CGPoint newRightEyePosition = face.hasRightEyePosition ? face.rightEyePosition : CGPointZero; 55 | CGRect rightEyeRect = [self eyeRect:self.lastRightEyePosition 56 | newEyePosition:newRightEyePosition 57 | faceRect:face.bounds]; 58 | [self.rightEyeView updateEyeRect:rightEyeRect]; 59 | 60 | // Remeber last known eyes positions. 61 | [self updateLastFaceFeature:face]; 62 | } 63 | 64 | - (void)dataOutput:(GMVDataOutput *)dataOutput 65 | updateMissingFeatures:(NSArray *)features { 66 | self.leftEyeView.hidden = YES; 67 | self.rightEyeView.hidden = YES; 68 | } 69 | 70 | - (void)dataOutputCompletedWithFocusingFeature:(GMVDataOutput *)dataOutput{ 71 | [self.leftEyeView removeFromSuperview]; 72 | [self.rightEyeView removeFromSuperview]; 73 | } 74 | 75 | #pragma mark - Helper methods 76 | 77 | - (CGRect)scaledRect:(CGRect)rect 78 | xScale:(CGFloat)xscale 79 | yScale:(CGFloat)yscale 80 | offset:(CGPoint)offset { 81 | 82 | CGRect resultRect = CGRectMake(floor(rect.origin.x * xscale), 83 | floor(rect.origin.y * yscale), 84 | floor(rect.size.width * xscale), 85 | floor(rect.size.height * yscale)); 86 | resultRect = CGRectOffset(resultRect, offset.x, offset.y); 87 | return resultRect; 88 | } 89 | 90 | - (CGRect)eyeRect:(CGPoint)lastEyePosition 91 | newEyePosition:(CGPoint)newEyePosition 92 | faceRect:(CGRect)faceRect { 93 | CGPoint eye = lastEyePosition; 94 | if (!CGPointEqualToPoint(newEyePosition, CGPointZero)) { 95 | eye = newEyePosition; 96 | } 97 | 98 | CGFloat faceToEyeRatio = 4.0; 99 | CGFloat width = faceRect.size.width / faceToEyeRatio; 100 | CGRect rect = CGRectMake(eye.x - width / 2, 101 | eye.y - width / 2, 102 | width, 103 | width); 104 | rect = [self scaledRect:rect 105 | xScale:[self.delegate xScale] 106 | yScale:[self.delegate yScale] 107 | offset:[self.delegate offset]]; 108 | return rect; 109 | } 110 | 111 | - (void)updateLastFaceFeature:(GMVFaceFeature *)feature { 112 | if (feature.hasLeftEyePosition) { 113 | self.lastLeftEyePosition = feature.leftEyePosition; 114 | } 115 | if (feature.hasRightEyePosition) { 116 | self.lastRightEyePosition = feature.rightEyePosition; 117 | } 118 | } 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/GooglyEyeView.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import UIKit; 18 | 19 | // Googly eye rendering. 20 | @interface GooglyEyeView : UIView 21 | 22 | - (void)updateEyeRect:(CGRect)eyeRect; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/GooglyEyeView.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import QuartzCore; 18 | 19 | #import "GooglyEyeView.h" 20 | #import "EyePhysics.h" 21 | 22 | @interface GooglyEyeView () 23 | 24 | @property(nonatomic, strong) EyePhysics *physics; 25 | @property(nonatomic, assign) CGRect irisRect; 26 | 27 | @end 28 | 29 | @implementation GooglyEyeView 30 | 31 | - (instancetype)init { 32 | self = [super init]; 33 | if (self) { 34 | self.physics = [[EyePhysics alloc] init]; 35 | self.irisRect = CGRectZero; 36 | self.opaque = NO; 37 | self.backgroundColor = [UIColor whiteColor]; 38 | self.layer.borderColor = [UIColor blackColor].CGColor; 39 | self.layer.borderWidth = 4; 40 | self.layer.masksToBounds = YES; 41 | } 42 | return self; 43 | } 44 | 45 | - (void)updateEyeRect:(CGRect)eyeRect { 46 | self.frame = eyeRect; 47 | self.layer.cornerRadius = self.frame.size.height / 2; 48 | [self setNeedsDisplay]; 49 | } 50 | 51 | - (void)drawRect:(CGRect)rect { 52 | self.irisRect = [self.physics nextIrisRectFrom:self.frame withIrisRect:self.irisRect]; 53 | CGRect iris = [self.superview convertRect:self.irisRect toView:self]; 54 | [[UIColor blackColor] setFill]; 55 | UIBezierPath *irisPath = [UIBezierPath bezierPathWithOvalInRect:iris]; 56 | [irisPath fill]; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | UIInterfaceOrientationPortraitUpsideDown 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/ViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import UIKit; 18 | 19 | // Main view conroller for Googly Eyes, an app that uses the camera to track faces and 20 | // superimpose Googly Eyes animated graphics over the eyes. 21 | // 22 | // The app supports both a front facing mode and a rear facing mode, which demonstrate different 23 | // API functionality trade-offs: 24 | // 25 | // Front facing mode uses the device's front facing camera to track one user, in a "selfie" fashion. 26 | // The settings for the face detector and its associated processing pipeline are set to optimize for 27 | // the single face case, where the face is relatively large. These factors allow the face detector 28 | // to be faster and more responsive to quick motion. 29 | // 30 | // Rear facing mode uses the device's rear facing camera to track any number of faces. The settings 31 | // for the face detector and its associated processing pipeline support finding multiple faces, and 32 | // attempt to find smaller faces in comparison to the front facing mode. But since this requires 33 | // more scanning at finer levels of detail, rear facing mode may not be as responsive as front 34 | // facing mode. 35 | @interface ViewController : UIViewController 36 | 37 | @end 38 | 39 | -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/ViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import AVFoundation; 18 | @import GoogleMobileVision; 19 | @import GoogleMVDataOutput; 20 | 21 | #import "ViewController.h" 22 | #import "FaceTracker.h" 23 | 24 | @interface ViewController () 25 | 26 | @property(nonatomic, weak) IBOutlet UIView *placeHolder; 27 | @property(nonatomic, weak) IBOutlet UIView *overlay; 28 | 29 | @property(nonatomic, weak) IBOutlet UISwitch *cameraSwitch; 30 | @property(nonatomic, strong) AVCaptureSession *session; 31 | @property(nonatomic, strong) GMVDataOutput *dataOutput; 32 | @property(nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer; 33 | 34 | @end 35 | 36 | @implementation ViewController 37 | 38 | #pragma mark - Life cycle methods 39 | 40 | - (void)viewDidLoad { 41 | [super viewDidLoad]; 42 | 43 | // Set up default camera settings. 44 | self.session = [[AVCaptureSession alloc] init]; 45 | self.session.sessionPreset = AVCaptureSessionPresetMedium; 46 | self.cameraSwitch.on = YES; 47 | [self updateCameraSelection]; 48 | 49 | // Set up processing pipeline. 50 | [self setupGMVDataOutput]; 51 | 52 | // Set up camera preview. 53 | [self setupCameraPreview]; 54 | } 55 | 56 | - (void)viewDidLayoutSubviews { 57 | [super viewDidLayoutSubviews]; 58 | 59 | self.previewLayer.frame = self.view.layer.bounds; 60 | self.previewLayer.position = CGPointMake(CGRectGetMidX(self.previewLayer.frame), 61 | CGRectGetMidY(self.previewLayer.frame)); 62 | } 63 | 64 | - (void)viewDidUnload { 65 | [self cleanupCaptureSession]; 66 | [super viewDidUnload]; 67 | } 68 | 69 | - (void)viewDidAppear:(BOOL)animated { 70 | [super viewDidAppear:animated]; 71 | 72 | [self.session startRunning]; 73 | } 74 | 75 | - (void)viewDidDisappear:(BOOL)animated { 76 | [super viewDidDisappear:animated]; 77 | [self.session stopRunning]; 78 | } 79 | 80 | - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 81 | duration:(NSTimeInterval)duration { 82 | // Camera rotation needs to be manually set when rotation changes. 83 | if (self.previewLayer) { 84 | if (toInterfaceOrientation == UIInterfaceOrientationPortrait) { 85 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortrait; 86 | } else if (toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) { 87 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortraitUpsideDown; 88 | } else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) { 89 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft; 90 | } else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) { 91 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; 92 | } 93 | } 94 | self.dataOutput.previewFrameSize = self.previewLayer.frame.size; 95 | } 96 | 97 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations { 98 | return UIInterfaceOrientationMaskAll; 99 | } 100 | 101 | #pragma mark - GMV Pipeline Setup 102 | 103 | - (void)setupGMVDataOutput { 104 | NSDictionary *options = @{ 105 | GMVDetectorFaceTrackingEnabled : @(YES), 106 | GMVDetectorFaceMode : @(GMVDetectorFaceFastMode), 107 | GMVDetectorFaceLandmarkType : @(GMVDetectorFaceLandmarkAll), 108 | GMVDetectorFaceClassificationType : @(GMVDetectorFaceClassificationAll), 109 | GMVDetectorFaceMinSize : @(self.cameraSwitch.isOn ? 0.35 : 0.15) 110 | }; 111 | GMVDetector *detector = [GMVDetector detectorOfType:GMVDetectorTypeFace options:options]; 112 | 113 | if (self.cameraSwitch.isOn) { 114 | self.dataOutput = [[GMVLargestFaceFocusingDataOutput alloc] initWithDetector:detector]; 115 | FaceTracker *tracker = [[FaceTracker alloc] init]; 116 | tracker.delegate = self; 117 | ((GMVLargestFaceFocusingDataOutput *)self.dataOutput).trackerDelegate = tracker; 118 | } else { 119 | self.dataOutput = [[GMVMultiDataOutput alloc] initWithDetector:detector]; 120 | ((GMVMultiDataOutput *)self.dataOutput).multiDataDelegate = self; 121 | } 122 | 123 | if (![self.session canAddOutput:self.dataOutput]) { 124 | [self cleanupGMVDataOutput]; 125 | NSLog(@"Failed to setup video output"); 126 | return; 127 | } 128 | [self.session addOutput:self.dataOutput]; 129 | } 130 | 131 | - (void)cleanupGMVDataOutput { 132 | if (self.dataOutput) { 133 | [self.session removeOutput:self.dataOutput]; 134 | } 135 | [self.dataOutput cleanup]; 136 | self.dataOutput = nil; 137 | } 138 | 139 | - (IBAction)switchCamera:(UISwitch *)switchControl { 140 | [self updateCameraSelection]; 141 | 142 | [self cleanupGMVDataOutput]; 143 | [self setupGMVDataOutput]; 144 | } 145 | 146 | #pragma mark - FaceTrackerDatasource 147 | 148 | - (UIView *)overlayView { 149 | return self.overlay; 150 | } 151 | 152 | - (CGFloat)xScale { 153 | return self.dataOutput.xScale; 154 | } 155 | 156 | - (CGFloat)yScale { 157 | return self.dataOutput.yScale; 158 | } 159 | 160 | - (CGPoint)offset { 161 | return self.dataOutput.offset; 162 | } 163 | 164 | 165 | #pragma mark - GMVMultiDataOutputDelegate 166 | 167 | - (id)dataOutput:(GMVDataOutput *)dataOutput 168 | trackerForFeature:(GMVFeature *)feature { 169 | FaceTracker *tracker = [[FaceTracker alloc] init]; 170 | tracker.delegate = self; 171 | return tracker; 172 | } 173 | 174 | #pragma mark - Camera setup 175 | 176 | - (void)cleanupCaptureSession { 177 | [self.session stopRunning]; 178 | [self cleanupGMVDataOutput]; 179 | self.session = nil; 180 | [self.previewLayer removeFromSuperlayer]; 181 | } 182 | 183 | - (void)setupCameraPreview { 184 | self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session]; 185 | [self.previewLayer setBackgroundColor:[[UIColor whiteColor] CGColor]]; 186 | [self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect]; 187 | CALayer *rootLayer = [self.placeHolder layer]; 188 | [rootLayer setMasksToBounds:YES]; 189 | [self.previewLayer setFrame:[rootLayer bounds]]; 190 | [rootLayer addSublayer:self.previewLayer]; 191 | } 192 | 193 | - (void)updateCameraSelection { 194 | [self.session beginConfiguration]; 195 | 196 | // Remove old inputs 197 | NSArray *oldInputs = [self.session inputs]; 198 | for (AVCaptureInput *oldInput in oldInputs) { 199 | [self.session removeInput:oldInput]; 200 | } 201 | 202 | AVCaptureDevicePosition desiredPosition = self.cameraSwitch.isOn? 203 | AVCaptureDevicePositionFront : AVCaptureDevicePositionBack; 204 | AVCaptureDeviceInput *input = [self cameraForPosition:desiredPosition]; 205 | if (!input) { 206 | // Failed, restore old inputs 207 | for (AVCaptureInput *oldInput in oldInputs) { 208 | [self.session addInput:oldInput]; 209 | } 210 | } else { 211 | // Succeeded, set input and update connection states 212 | [self.session addInput:input]; 213 | } 214 | [self.session commitConfiguration]; 215 | } 216 | 217 | - (AVCaptureDeviceInput *)cameraForPosition:(AVCaptureDevicePosition)desiredPosition { 218 | BOOL hadError = NO; 219 | for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { 220 | if ([device position] == desiredPosition) { 221 | NSError *error = nil; 222 | AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device 223 | error:&error]; 224 | if (error) { 225 | hadError = YES; 226 | NSLog(@"Could not initialize for AVMediaTypeVideo for device %@", device); 227 | } else if ([self.session canAddInput:input]) { 228 | return input; 229 | } 230 | } 231 | } 232 | if (!hadError) { 233 | NSLog(@"No camera found for requested orientation"); 234 | } 235 | return nil; 236 | } 237 | 238 | @end 239 | -------------------------------------------------------------------------------- /GooglyEyesDemo/GooglyEyes/main.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import 18 | #import "AppDelegate.h" 19 | 20 | int main(int argc, char *argv[]) { 21 | @autoreleasepool { 22 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /GooglyEyesDemo/Podfile: -------------------------------------------------------------------------------- 1 | target "GooglyEyes" do 2 | pod 'GoogleMobileVision/FaceDetector' 3 | pod 'GoogleMobileVision/MVDataOutput' 4 | end 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright ${copyright_attribution} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 40421411C022EBF6E0FD282F /* Pods_MultiDetector.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EC8222FA769EF86E84BE2024 /* Pods_MultiDetector.framework */; }; 11 | 6FB898181DA6D7AB006234BA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FB898171DA6D7AB006234BA /* main.m */; }; 12 | 6FB8981B1DA6D7AB006234BA /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FB8981A1DA6D7AB006234BA /* AppDelegate.m */; }; 13 | 6FB8981E1DA6D7AB006234BA /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FB8981D1DA6D7AB006234BA /* ViewController.m */; }; 14 | 6FB898211DA6D7AB006234BA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6FB8981F1DA6D7AB006234BA /* Main.storyboard */; }; 15 | 6FB898231DA6D7AB006234BA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FB898221DA6D7AB006234BA /* Assets.xcassets */; }; 16 | 6FB898261DA6D7AB006234BA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6FB898241DA6D7AB006234BA /* LaunchScreen.storyboard */; }; 17 | 6FB898781DA6DBE6006234BA /* FaceTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FB898771DA6DBE6006234BA /* FaceTracker.m */; }; 18 | 6FB8987B1DA6DBF8006234BA /* BarcodeTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 6FB8987A1DA6DBF8006234BA /* BarcodeTracker.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 6FB898131DA6D7AB006234BA /* MultiDetector.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MultiDetector.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 6FB898171DA6D7AB006234BA /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 24 | 6FB898191DA6D7AB006234BA /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 25 | 6FB8981A1DA6D7AB006234BA /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 26 | 6FB8981C1DA6D7AB006234BA /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 27 | 6FB8981D1DA6D7AB006234BA /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 28 | 6FB898201DA6D7AB006234BA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 29 | 6FB898221DA6D7AB006234BA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 30 | 6FB898251DA6D7AB006234BA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 31 | 6FB898271DA6D7AB006234BA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 32 | 6FB898761DA6DBE6006234BA /* FaceTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FaceTracker.h; sourceTree = ""; }; 33 | 6FB898771DA6DBE6006234BA /* FaceTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FaceTracker.m; sourceTree = ""; }; 34 | 6FB898791DA6DBF8006234BA /* BarcodeTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BarcodeTracker.h; sourceTree = ""; }; 35 | 6FB8987A1DA6DBF8006234BA /* BarcodeTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BarcodeTracker.m; sourceTree = ""; }; 36 | A1CB93E770D4F9EE0543B457 /* Pods-MultiDetector.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MultiDetector.release.xcconfig"; path = "Pods/Target Support Files/Pods-MultiDetector/Pods-MultiDetector.release.xcconfig"; sourceTree = ""; }; 37 | C84DAC7A06F3BEFBEBFB7773 /* Pods-MultiDetector.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MultiDetector.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MultiDetector/Pods-MultiDetector.debug.xcconfig"; sourceTree = ""; }; 38 | EC8222FA769EF86E84BE2024 /* Pods_MultiDetector.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MultiDetector.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | /* End PBXFileReference section */ 40 | 41 | /* Begin PBXFrameworksBuildPhase section */ 42 | 6FB898101DA6D7AB006234BA /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | 40421411C022EBF6E0FD282F /* Pods_MultiDetector.framework in Frameworks */, 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | /* End PBXFrameworksBuildPhase section */ 51 | 52 | /* Begin PBXGroup section */ 53 | 6FB8980A1DA6D7AB006234BA = { 54 | isa = PBXGroup; 55 | children = ( 56 | 6FB898151DA6D7AB006234BA /* MultiDetector */, 57 | 6FB898141DA6D7AB006234BA /* Products */, 58 | B8BBB4B6BB6BA20E2BE6677A /* Pods */, 59 | E8DEE499199E1CB4853794C3 /* Frameworks */, 60 | ); 61 | sourceTree = ""; 62 | }; 63 | 6FB898141DA6D7AB006234BA /* Products */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 6FB898131DA6D7AB006234BA /* MultiDetector.app */, 67 | ); 68 | name = Products; 69 | sourceTree = ""; 70 | }; 71 | 6FB898151DA6D7AB006234BA /* MultiDetector */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 6FB898791DA6DBF8006234BA /* BarcodeTracker.h */, 75 | 6FB8987A1DA6DBF8006234BA /* BarcodeTracker.m */, 76 | 6FB898761DA6DBE6006234BA /* FaceTracker.h */, 77 | 6FB898771DA6DBE6006234BA /* FaceTracker.m */, 78 | 6FB898191DA6D7AB006234BA /* AppDelegate.h */, 79 | 6FB8981A1DA6D7AB006234BA /* AppDelegate.m */, 80 | 6FB8981C1DA6D7AB006234BA /* ViewController.h */, 81 | 6FB8981D1DA6D7AB006234BA /* ViewController.m */, 82 | 6FB8981F1DA6D7AB006234BA /* Main.storyboard */, 83 | 6FB898221DA6D7AB006234BA /* Assets.xcassets */, 84 | 6FB898241DA6D7AB006234BA /* LaunchScreen.storyboard */, 85 | 6FB898271DA6D7AB006234BA /* Info.plist */, 86 | 6FB898161DA6D7AB006234BA /* Supporting Files */, 87 | ); 88 | path = MultiDetector; 89 | sourceTree = ""; 90 | }; 91 | 6FB898161DA6D7AB006234BA /* Supporting Files */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 6FB898171DA6D7AB006234BA /* main.m */, 95 | ); 96 | name = "Supporting Files"; 97 | sourceTree = ""; 98 | }; 99 | B8BBB4B6BB6BA20E2BE6677A /* Pods */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | C84DAC7A06F3BEFBEBFB7773 /* Pods-MultiDetector.debug.xcconfig */, 103 | A1CB93E770D4F9EE0543B457 /* Pods-MultiDetector.release.xcconfig */, 104 | ); 105 | name = Pods; 106 | sourceTree = ""; 107 | }; 108 | E8DEE499199E1CB4853794C3 /* Frameworks */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | EC8222FA769EF86E84BE2024 /* Pods_MultiDetector.framework */, 112 | ); 113 | name = Frameworks; 114 | sourceTree = ""; 115 | }; 116 | /* End PBXGroup section */ 117 | 118 | /* Begin PBXNativeTarget section */ 119 | 6FB898121DA6D7AB006234BA /* MultiDetector */ = { 120 | isa = PBXNativeTarget; 121 | buildConfigurationList = 6FB8982A1DA6D7AB006234BA /* Build configuration list for PBXNativeTarget "MultiDetector" */; 122 | buildPhases = ( 123 | 336569846CD538157168C532 /* [CP] Check Pods Manifest.lock */, 124 | 6FB8980F1DA6D7AB006234BA /* Sources */, 125 | 6FB898101DA6D7AB006234BA /* Frameworks */, 126 | 6FB898111DA6D7AB006234BA /* Resources */, 127 | 9A7B52514173321DEBD99E92 /* [CP] Embed Pods Frameworks */, 128 | 89EFB1A54DE57C39DD13A486 /* [CP] Copy Pods Resources */, 129 | ); 130 | buildRules = ( 131 | ); 132 | dependencies = ( 133 | ); 134 | name = MultiDetector; 135 | productName = MultiDetector; 136 | productReference = 6FB898131DA6D7AB006234BA /* MultiDetector.app */; 137 | productType = "com.apple.product-type.application"; 138 | }; 139 | /* End PBXNativeTarget section */ 140 | 141 | /* Begin PBXProject section */ 142 | 6FB8980B1DA6D7AB006234BA /* Project object */ = { 143 | isa = PBXProject; 144 | attributes = { 145 | LastUpgradeCheck = 0720; 146 | ORGANIZATIONNAME = Google; 147 | TargetAttributes = { 148 | 6FB898121DA6D7AB006234BA = { 149 | CreatedOnToolsVersion = 7.2.1; 150 | }; 151 | }; 152 | }; 153 | buildConfigurationList = 6FB8980E1DA6D7AB006234BA /* Build configuration list for PBXProject "MultiDetector" */; 154 | compatibilityVersion = "Xcode 3.2"; 155 | developmentRegion = English; 156 | hasScannedForEncodings = 0; 157 | knownRegions = ( 158 | en, 159 | Base, 160 | ); 161 | mainGroup = 6FB8980A1DA6D7AB006234BA; 162 | productRefGroup = 6FB898141DA6D7AB006234BA /* Products */; 163 | projectDirPath = ""; 164 | projectRoot = ""; 165 | targets = ( 166 | 6FB898121DA6D7AB006234BA /* MultiDetector */, 167 | ); 168 | }; 169 | /* End PBXProject section */ 170 | 171 | /* Begin PBXResourcesBuildPhase section */ 172 | 6FB898111DA6D7AB006234BA /* Resources */ = { 173 | isa = PBXResourcesBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | 6FB898261DA6D7AB006234BA /* LaunchScreen.storyboard in Resources */, 177 | 6FB898231DA6D7AB006234BA /* Assets.xcassets in Resources */, 178 | 6FB898211DA6D7AB006234BA /* Main.storyboard in Resources */, 179 | ); 180 | runOnlyForDeploymentPostprocessing = 0; 181 | }; 182 | /* End PBXResourcesBuildPhase section */ 183 | 184 | /* Begin PBXShellScriptBuildPhase section */ 185 | 336569846CD538157168C532 /* [CP] Check Pods Manifest.lock */ = { 186 | isa = PBXShellScriptBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | ); 190 | inputPaths = ( 191 | ); 192 | name = "[CP] Check Pods Manifest.lock"; 193 | outputPaths = ( 194 | ); 195 | runOnlyForDeploymentPostprocessing = 0; 196 | shellPath = /bin/sh; 197 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 198 | showEnvVarsInLog = 0; 199 | }; 200 | 89EFB1A54DE57C39DD13A486 /* [CP] Copy Pods Resources */ = { 201 | isa = PBXShellScriptBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | ); 205 | inputPaths = ( 206 | ); 207 | name = "[CP] Copy Pods Resources"; 208 | outputPaths = ( 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | shellPath = /bin/sh; 212 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MultiDetector/Pods-MultiDetector-resources.sh\"\n"; 213 | showEnvVarsInLog = 0; 214 | }; 215 | 9A7B52514173321DEBD99E92 /* [CP] Embed Pods Frameworks */ = { 216 | isa = PBXShellScriptBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | ); 220 | inputPaths = ( 221 | ); 222 | name = "[CP] Embed Pods Frameworks"; 223 | outputPaths = ( 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | shellPath = /bin/sh; 227 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MultiDetector/Pods-MultiDetector-frameworks.sh\"\n"; 228 | showEnvVarsInLog = 0; 229 | }; 230 | /* End PBXShellScriptBuildPhase section */ 231 | 232 | /* Begin PBXSourcesBuildPhase section */ 233 | 6FB8980F1DA6D7AB006234BA /* Sources */ = { 234 | isa = PBXSourcesBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | 6FB8981E1DA6D7AB006234BA /* ViewController.m in Sources */, 238 | 6FB8981B1DA6D7AB006234BA /* AppDelegate.m in Sources */, 239 | 6FB8987B1DA6DBF8006234BA /* BarcodeTracker.m in Sources */, 240 | 6FB898181DA6D7AB006234BA /* main.m in Sources */, 241 | 6FB898781DA6DBE6006234BA /* FaceTracker.m in Sources */, 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | }; 245 | /* End PBXSourcesBuildPhase section */ 246 | 247 | /* Begin PBXVariantGroup section */ 248 | 6FB8981F1DA6D7AB006234BA /* Main.storyboard */ = { 249 | isa = PBXVariantGroup; 250 | children = ( 251 | 6FB898201DA6D7AB006234BA /* Base */, 252 | ); 253 | name = Main.storyboard; 254 | sourceTree = ""; 255 | }; 256 | 6FB898241DA6D7AB006234BA /* LaunchScreen.storyboard */ = { 257 | isa = PBXVariantGroup; 258 | children = ( 259 | 6FB898251DA6D7AB006234BA /* Base */, 260 | ); 261 | name = LaunchScreen.storyboard; 262 | sourceTree = ""; 263 | }; 264 | /* End PBXVariantGroup section */ 265 | 266 | /* Begin XCBuildConfiguration section */ 267 | 6FB898281DA6D7AB006234BA /* Debug */ = { 268 | isa = XCBuildConfiguration; 269 | buildSettings = { 270 | ALWAYS_SEARCH_USER_PATHS = NO; 271 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 272 | CLANG_CXX_LIBRARY = "libc++"; 273 | CLANG_ENABLE_MODULES = YES; 274 | CLANG_ENABLE_OBJC_ARC = YES; 275 | CLANG_WARN_BOOL_CONVERSION = YES; 276 | CLANG_WARN_CONSTANT_CONVERSION = YES; 277 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 278 | CLANG_WARN_EMPTY_BODY = YES; 279 | CLANG_WARN_ENUM_CONVERSION = YES; 280 | CLANG_WARN_INT_CONVERSION = YES; 281 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 282 | CLANG_WARN_UNREACHABLE_CODE = YES; 283 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 284 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 285 | COPY_PHASE_STRIP = NO; 286 | DEBUG_INFORMATION_FORMAT = dwarf; 287 | ENABLE_STRICT_OBJC_MSGSEND = YES; 288 | ENABLE_TESTABILITY = YES; 289 | GCC_C_LANGUAGE_STANDARD = gnu99; 290 | GCC_DYNAMIC_NO_PIC = NO; 291 | GCC_NO_COMMON_BLOCKS = YES; 292 | GCC_OPTIMIZATION_LEVEL = 0; 293 | GCC_PREPROCESSOR_DEFINITIONS = ( 294 | "DEBUG=1", 295 | "$(inherited)", 296 | ); 297 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 298 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 299 | GCC_WARN_UNDECLARED_SELECTOR = YES; 300 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 301 | GCC_WARN_UNUSED_FUNCTION = YES; 302 | GCC_WARN_UNUSED_VARIABLE = YES; 303 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 304 | MTL_ENABLE_DEBUG_INFO = YES; 305 | ONLY_ACTIVE_ARCH = YES; 306 | SDKROOT = iphoneos; 307 | }; 308 | name = Debug; 309 | }; 310 | 6FB898291DA6D7AB006234BA /* Release */ = { 311 | isa = XCBuildConfiguration; 312 | buildSettings = { 313 | ALWAYS_SEARCH_USER_PATHS = NO; 314 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 315 | CLANG_CXX_LIBRARY = "libc++"; 316 | CLANG_ENABLE_MODULES = YES; 317 | CLANG_ENABLE_OBJC_ARC = YES; 318 | CLANG_WARN_BOOL_CONVERSION = YES; 319 | CLANG_WARN_CONSTANT_CONVERSION = YES; 320 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 321 | CLANG_WARN_EMPTY_BODY = YES; 322 | CLANG_WARN_ENUM_CONVERSION = YES; 323 | CLANG_WARN_INT_CONVERSION = YES; 324 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 325 | CLANG_WARN_UNREACHABLE_CODE = YES; 326 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 327 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 328 | COPY_PHASE_STRIP = NO; 329 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 330 | ENABLE_NS_ASSERTIONS = NO; 331 | ENABLE_STRICT_OBJC_MSGSEND = YES; 332 | GCC_C_LANGUAGE_STANDARD = gnu99; 333 | GCC_NO_COMMON_BLOCKS = YES; 334 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 335 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 336 | GCC_WARN_UNDECLARED_SELECTOR = YES; 337 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 338 | GCC_WARN_UNUSED_FUNCTION = YES; 339 | GCC_WARN_UNUSED_VARIABLE = YES; 340 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 341 | MTL_ENABLE_DEBUG_INFO = NO; 342 | SDKROOT = iphoneos; 343 | VALIDATE_PRODUCT = YES; 344 | }; 345 | name = Release; 346 | }; 347 | 6FB8982B1DA6D7AB006234BA /* Debug */ = { 348 | isa = XCBuildConfiguration; 349 | baseConfigurationReference = C84DAC7A06F3BEFBEBFB7773 /* Pods-MultiDetector.debug.xcconfig */; 350 | buildSettings = { 351 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 352 | INFOPLIST_FILE = MultiDetector/Info.plist; 353 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 354 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 355 | PRODUCT_BUNDLE_IDENTIFIER = com.google.mobilevision.MultiDetector; 356 | PRODUCT_NAME = "$(TARGET_NAME)"; 357 | }; 358 | name = Debug; 359 | }; 360 | 6FB8982C1DA6D7AB006234BA /* Release */ = { 361 | isa = XCBuildConfiguration; 362 | baseConfigurationReference = A1CB93E770D4F9EE0543B457 /* Pods-MultiDetector.release.xcconfig */; 363 | buildSettings = { 364 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 365 | INFOPLIST_FILE = MultiDetector/Info.plist; 366 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 367 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 368 | PRODUCT_BUNDLE_IDENTIFIER = com.google.mobilevision.MultiDetector; 369 | PRODUCT_NAME = "$(TARGET_NAME)"; 370 | }; 371 | name = Release; 372 | }; 373 | /* End XCBuildConfiguration section */ 374 | 375 | /* Begin XCConfigurationList section */ 376 | 6FB8980E1DA6D7AB006234BA /* Build configuration list for PBXProject "MultiDetector" */ = { 377 | isa = XCConfigurationList; 378 | buildConfigurations = ( 379 | 6FB898281DA6D7AB006234BA /* Debug */, 380 | 6FB898291DA6D7AB006234BA /* Release */, 381 | ); 382 | defaultConfigurationIsVisible = 0; 383 | defaultConfigurationName = Release; 384 | }; 385 | 6FB8982A1DA6D7AB006234BA /* Build configuration list for PBXNativeTarget "MultiDetector" */ = { 386 | isa = XCConfigurationList; 387 | buildConfigurations = ( 388 | 6FB8982B1DA6D7AB006234BA /* Debug */, 389 | 6FB8982C1DA6D7AB006234BA /* Release */, 390 | ); 391 | defaultConfigurationIsVisible = 0; 392 | defaultConfigurationName = Release; 393 | }; 394 | /* End XCConfigurationList section */ 395 | }; 396 | rootObject = 6FB8980B1DA6D7AB006234BA /* Project object */; 397 | } 398 | -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import UIKit; 18 | 19 | @interface AppDelegate : UIResponder 20 | 21 | @property(nonatomic, strong) UIWindow *window; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import "AppDelegate.h" 18 | 19 | @implementation AppDelegate 20 | @end 21 | -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "size" : "40x40", 20 | "idiom" : "iphone", 21 | "filename" : "icon_120.png", 22 | "scale" : "3x" 23 | }, 24 | { 25 | "idiom" : "iphone", 26 | "size" : "60x60", 27 | "scale" : "2x" 28 | }, 29 | { 30 | "size" : "60x60", 31 | "idiom" : "iphone", 32 | "filename" : "logo_180.png", 33 | "scale" : "3x" 34 | } 35 | ], 36 | "info" : { 37 | "version" : 1, 38 | "author" : "xcode" 39 | } 40 | } -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector/Assets.xcassets/AppIcon.appiconset/icon_120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/MultiDetectorDemo/MultiDetector/Assets.xcassets/AppIcon.appiconset/icon_120.png -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector/Assets.xcassets/AppIcon.appiconset/logo_180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/ios-vision/4a242dc34b0fd568341e658dee665cfbbd554bfa/MultiDetectorDemo/MultiDetector/Assets.xcassets/AppIcon.appiconset/logo_180.png -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector/BarcodeTracker.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import Foundation; 18 | @import GoogleMVDataOutput; 19 | 20 | // The data source provides the BarcodeTracker object with the information it needs to display 21 | // an overlay and barcode value. 22 | @interface BarcodeTracker : NSObject 23 | 24 | @property(nonatomic, weak) UIView *overlay; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector/BarcodeTracker.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import "BarcodeTracker.h" 18 | 19 | @interface BarcodeTracker () 20 | 21 | @property(nonatomic, strong) UIView *barcodeView; 22 | @property(nonatomic, strong) UILabel *valueLabel; 23 | @property(nonatomic, strong) NSArray *colors; 24 | @property(nonatomic, assign) NSUInteger colorIndex; 25 | 26 | @end 27 | 28 | @implementation BarcodeTracker 29 | 30 | - (id)init { 31 | self = [super init]; 32 | if (self) { 33 | self.colorIndex = 0; 34 | self.colors = @[ 35 | [UIColor blueColor], 36 | [UIColor cyanColor], 37 | [UIColor greenColor], 38 | [UIColor purpleColor] 39 | ]; 40 | } 41 | return self; 42 | } 43 | 44 | #pragma mark - GMVOutputTrackerDelegate 45 | 46 | - (void)dataOutput:(GMVDataOutput *)dataOutput detectedFeature:(GMVFeature *)feature { 47 | self.barcodeView = [[UIView alloc] initWithFrame:CGRectZero]; 48 | self.barcodeView.backgroundColor = self.colors[self.colorIndex]; 49 | self.barcodeView.alpha = 0.5; 50 | [self.overlay addSubview:self.barcodeView]; 51 | 52 | self.valueLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 53 | self.valueLabel.textColor = self.colors[self.colorIndex]; 54 | [self.overlay addSubview:self.valueLabel]; 55 | } 56 | 57 | - (void)dataOutput:(GMVDataOutput *)dataOutput 58 | updateFocusingFeature:(GMVBarcodeFeature *)barcode 59 | forResultSet:(NSArray *)features { 60 | self.barcodeView.hidden = NO; 61 | CGRect rect = CGRectMake(floor(barcode.bounds.origin.x * dataOutput.xScale) + dataOutput.offset.x, 62 | floor(barcode.bounds.origin.y * dataOutput.yScale) + dataOutput.offset.y, 63 | floor(barcode.bounds.size.width * dataOutput.xScale), 64 | floor(barcode.bounds.size.height * dataOutput.yScale)); 65 | self.barcodeView.frame = rect; 66 | 67 | self.valueLabel.hidden = NO; 68 | self.valueLabel.text = barcode.rawValue; 69 | self.valueLabel.frame = CGRectMake(rect.origin.x, rect.origin.y + rect.size.height, 200, 20); 70 | } 71 | 72 | - (void)dataOutput:(GMVDataOutput *)dataOutput 73 | updateMissingFeatures:(NSArray *)features { 74 | self.barcodeView.hidden = YES; 75 | self.valueLabel.hidden = YES; 76 | self.colorIndex = (self.colorIndex + 1) % self.colors.count; 77 | } 78 | 79 | - (void)dataOutputCompletedWithFocusingFeature:(GMVDataOutput *)dataOutput{ 80 | [self.barcodeView removeFromSuperview]; 81 | [self.valueLabel removeFromSuperview]; 82 | } 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector/FaceTracker.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import Foundation; 18 | @import GoogleMVDataOutput; 19 | 20 | // The data source provides the FaceTracker object with the information it needs to display 21 | // an overlay and face ID. 22 | @interface FaceTracker : NSObject 23 | 24 | @property(nonatomic, weak) UIView *overlay; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector/FaceTracker.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #import "FaceTracker.h" 18 | 19 | @interface FaceTracker () 20 | 21 | @property(nonatomic, strong) UIView *faceView; 22 | @property(nonatomic, strong) UILabel *idLabel; 23 | @property(nonatomic, strong) NSArray *colors; 24 | @property(nonatomic, assign) NSUInteger colorIndex; 25 | 26 | @end 27 | 28 | @implementation FaceTracker 29 | 30 | - (id)init { 31 | self = [super init]; 32 | if (self) { 33 | self.colorIndex = 0; 34 | self.colors = @[ 35 | [UIColor redColor], 36 | [UIColor orangeColor], 37 | [UIColor magentaColor], 38 | [UIColor brownColor] 39 | ]; 40 | } 41 | return self; 42 | } 43 | 44 | #pragma mark - GMVOutputTrackerDelegate 45 | 46 | - (void)dataOutput:(GMVDataOutput *)dataOutput detectedFeature:(GMVFeature *)feature { 47 | self.faceView = [[UIView alloc] initWithFrame:CGRectZero]; 48 | self.faceView.backgroundColor = self.colors[self.colorIndex]; 49 | self.faceView.alpha = 0.5; 50 | self.faceView.layer.cornerRadius = 25.0; 51 | [self.overlay addSubview:self.faceView]; 52 | 53 | self.idLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 54 | self.idLabel.textColor = self.colors[self.colorIndex]; 55 | [self.overlay addSubview:self.idLabel]; 56 | } 57 | 58 | - (void)dataOutput:(GMVDataOutput *)dataOutput 59 | updateFocusingFeature:(GMVFaceFeature *)face 60 | forResultSet:(NSArray *)features { 61 | self.faceView.hidden = NO; 62 | CGFloat fx = floor(CGRectGetMinX(face.bounds) * dataOutput.xScale) + dataOutput.offset.x; 63 | CGFloat fy = floor(CGRectGetMinY(face.bounds) * dataOutput.yScale) + dataOutput.offset.y; 64 | CGFloat fwidth = floor(CGRectGetWidth(face.bounds) * dataOutput.xScale); 65 | CGFloat fheight = floor(CGRectGetHeight(face.bounds) * dataOutput.yScale); 66 | CGRect rect = CGRectMake(fx, fy, fwidth, fheight); 67 | self.faceView.frame = rect; 68 | 69 | self.idLabel.hidden = NO; 70 | self.idLabel.text = [NSString stringWithFormat:@"id : %lu", face.trackingID]; 71 | self.idLabel.frame = CGRectMake(fx, fy + fheight, 200, 20); 72 | } 73 | 74 | - (void)dataOutput:(GMVDataOutput *)dataOutput 75 | updateMissingFeatures:(NSArray *)features { 76 | self.faceView.hidden = YES; 77 | self.idLabel.hidden = YES; 78 | self.colorIndex = (self.colorIndex + 1) % [self.colors count]; 79 | } 80 | 81 | - (void)dataOutputCompletedWithFocusingFeature:(GMVDataOutput *)dataOutput{ 82 | [self.faceView removeFromSuperview]; 83 | [self.idLabel removeFromSuperview]; 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector/ViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import UIKit; 18 | 19 | // Main view conroller for MultiDetector, an app that uses the camera to track faces and barcodes, 20 | // superimposing overlays, barcode values and face IDs. 21 | @interface ViewController : UIViewController 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector/ViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import AVFoundation; 18 | @import GoogleMVDataOutput; 19 | 20 | #import "ViewController.h" 21 | 22 | #import "BarcodeTracker.h" 23 | #import "FaceTracker.h" 24 | 25 | @interface ViewController () 26 | 27 | @property(nonatomic, weak) IBOutlet UIView *preview; 28 | @property(nonatomic, weak) IBOutlet UIView *overlay; 29 | 30 | @property(nonatomic, strong) AVCaptureSession *session; 31 | @property(nonatomic, strong) GMVDataOutput *dataOutput; 32 | @property(nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer; 33 | 34 | @end 35 | 36 | @implementation ViewController 37 | 38 | #pragma mark - Life cycle methods 39 | 40 | - (void)viewDidLoad { 41 | [super viewDidLoad]; 42 | 43 | // Set up default camera settings. 44 | self.session = [[AVCaptureSession alloc] init]; 45 | self.session.sessionPreset = AVCaptureSessionPresetMedium; 46 | [self updateCameraSelection]; 47 | 48 | // Set up processing pipeline. 49 | [self setupGMVDataOutput]; 50 | 51 | // Set up camera preview. 52 | [self setupCameraPreview]; 53 | } 54 | 55 | - (void)viewDidLayoutSubviews { 56 | [super viewDidLayoutSubviews]; 57 | 58 | self.previewLayer.frame = self.view.layer.bounds; 59 | self.previewLayer.position = CGPointMake(CGRectGetMidX(self.previewLayer.frame), 60 | CGRectGetMidY(self.previewLayer.frame)); 61 | } 62 | 63 | - (void)viewDidUnload { 64 | [self cleanupCaptureSession]; 65 | [super viewDidUnload]; 66 | } 67 | 68 | - (void)viewDidAppear:(BOOL)animated { 69 | [super viewDidAppear:animated]; 70 | 71 | [self.session startRunning]; 72 | } 73 | 74 | - (void)viewDidDisappear:(BOOL)animated { 75 | [super viewDidDisappear:animated]; 76 | [self.session stopRunning]; 77 | } 78 | 79 | - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation 80 | duration:(NSTimeInterval)duration { 81 | // Camera rotation needs to be manually set when rotation changes. 82 | if (self.previewLayer) { 83 | if (toInterfaceOrientation == UIInterfaceOrientationPortrait) { 84 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortrait; 85 | } else if (toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) { 86 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortraitUpsideDown; 87 | } else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) { 88 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft; 89 | } else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) { 90 | self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; 91 | } 92 | } 93 | self.dataOutput.previewFrameSize = self.previewLayer.frame.size; 94 | } 95 | 96 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations { 97 | return UIInterfaceOrientationMaskAll; 98 | } 99 | 100 | #pragma mark - GMV Pipeline Setup 101 | 102 | - (void)setupGMVDataOutput { 103 | NSDictionary *faceOptions = @{ 104 | GMVDetectorFaceTrackingEnabled : @(YES), 105 | GMVDetectorFaceMinSize : @(0.15) 106 | }; 107 | GMVDetector *faceDetector = [GMVDetector detectorOfType:GMVDetectorTypeFace options:faceOptions]; 108 | GMVDetector *barcodeDetector = [GMVDetector detectorOfType:GMVDetectorTypeBarcode options:nil]; 109 | NSArray *detectors = @[faceDetector, barcodeDetector]; 110 | self.dataOutput = [[GMVMultiDetectorDataOutput alloc] initWithDetectors:detectors]; 111 | ((GMVMultiDetectorDataOutput *)self.dataOutput).multiDetectorDataDelegate = self; 112 | 113 | if (![self.session canAddOutput:self.dataOutput]) { 114 | [self cleanupGMVDataOutput]; 115 | NSLog(@"Failed to setup video output"); 116 | return; 117 | } 118 | [self.session addOutput:self.dataOutput]; 119 | } 120 | 121 | - (void)cleanupGMVDataOutput { 122 | if (self.dataOutput) { 123 | [self.session removeOutput:self.dataOutput]; 124 | } 125 | [self.dataOutput cleanup]; 126 | self.dataOutput = nil; 127 | } 128 | 129 | #pragma mark - FaceTrackerDelegate 130 | 131 | - (UIView *)overlayView { 132 | return self.overlay; 133 | } 134 | 135 | #pragma mark - GMVMultiDetectorDataOutputDelegate 136 | 137 | - (id)dataOutput:(GMVDataOutput *)dataOutput 138 | fromDetector:(GMVDetector *)detector 139 | trackerForFeature:(GMVFeature *)feature { 140 | if ([feature.type isEqualToString:GMVFeatureTypeFace]) { 141 | FaceTracker *tracker = [[FaceTracker alloc] init]; 142 | tracker.overlay = self.view; 143 | return tracker; 144 | } else if ([feature.type isEqualToString:GMVFeatureTypeBarcode]) { 145 | BarcodeTracker *tracker = [[BarcodeTracker alloc] init]; 146 | tracker.overlay = self.view; 147 | return tracker; 148 | } 149 | return nil; 150 | } 151 | 152 | #pragma mark - Camera setup 153 | 154 | - (void)cleanupCaptureSession { 155 | [self.session stopRunning]; 156 | [self cleanupGMVDataOutput]; 157 | self.session = nil; 158 | [self.previewLayer removeFromSuperlayer]; 159 | } 160 | 161 | - (void)setupCameraPreview { 162 | self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session]; 163 | [self.previewLayer setBackgroundColor:[[UIColor whiteColor] CGColor]]; 164 | [self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect]; 165 | CALayer *rootLayer = [self.preview layer]; 166 | [rootLayer setMasksToBounds:YES]; 167 | [self.previewLayer setFrame:[rootLayer bounds]]; 168 | [rootLayer addSublayer:self.previewLayer]; 169 | } 170 | 171 | - (void)updateCameraSelection { 172 | [self.session beginConfiguration]; 173 | 174 | // Remove old inputs 175 | NSArray *oldInputs = [self.session inputs]; 176 | for (AVCaptureInput *oldInput in oldInputs) { 177 | [self.session removeInput:oldInput]; 178 | } 179 | 180 | AVCaptureDeviceInput *input = [self cameraForPosition:AVCaptureDevicePositionBack]; 181 | if (!input) { 182 | // Failed, restore old inputs 183 | for (AVCaptureInput *oldInput in oldInputs) { 184 | [self.session addInput:oldInput]; 185 | } 186 | } else { 187 | // Succeeded, set input and update connection states 188 | [self.session addInput:input]; 189 | } 190 | [self.session commitConfiguration]; 191 | } 192 | 193 | - (AVCaptureDeviceInput *)cameraForPosition:(AVCaptureDevicePosition)desiredPosition { 194 | for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { 195 | if ([device position] == desiredPosition) { 196 | NSError *error = nil; 197 | AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device 198 | error:&error]; 199 | if (error) { 200 | NSLog(@"Could not initialize for AVMediaTypeVideo for device %@", device); 201 | } else if ([self.session canAddInput:input]) { 202 | return input; 203 | } 204 | } 205 | } 206 | return nil; 207 | } 208 | 209 | @end 210 | -------------------------------------------------------------------------------- /MultiDetectorDemo/MultiDetector/main.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | @import UIKit; 18 | 19 | #import "AppDelegate.h" 20 | 21 | int main(int argc, char * argv[]) { 22 | @autoreleasepool { 23 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /MultiDetectorDemo/Podfile: -------------------------------------------------------------------------------- 1 | target "MultiDetector" do 2 | pod 'GoogleMobileVision/FaceDetector' 3 | pod 'GoogleMobileVision/BarcodeDetector' 4 | pod 'GoogleMobileVision/MVDataOutput' 5 | end 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iOS Vision API Samples 2 | 3 | At this time, these samples demonstrate the vision API for detecting faces. 4 | 5 | ## A note on CocoaPods 6 | 7 | The Google Mobile Vision iOS SDK and related samples are distributed through CocoaPods. 8 | Set up CocoaPods by going to cocoapods.org and following the directions. 9 | 10 | ## Try the sample apps 11 | 12 | After installing CocoaPods, run the command `pod try GoogleMobileVision` from Terminal 13 | to open up any example projects for the library. There are 2 sample apps available: 14 | 15 | * FaceDetectorDemo: This demo demonstrates basic face detection and integration with 16 | AVFoundation. The app highlights face, eyes, nose, mouth, cheeks, and ears within detected faces. 17 | 18 | * GooglyEyesDemo: This demo demonstrates how to use the `GoogleMVDataOutput` pod to simplify 19 | integration with the video pipeline. The app draws cartoon eyes on top of detected faces. 20 | 21 | If you want to try the samples from the github source code. Do the following: 22 | 23 | - Run the command `pod install` from Terminal in the folder that contains the Podfile. This will 24 | download the required dependencies. 25 | - Launch the [Project Name].xcworkspace. This will open the sample app with xcode. 26 | 27 | 28 | ## Support 29 | 30 | For General questions and discussion on StackOverflow: 31 | - Stack Overflow: http://stackoverflow.com/questions/tagged/google-ios-vision 32 | 33 | If you've found an error in this sample, please file an issue: 34 | https://github.com/googlesamples/ios-vision/issues 35 | 36 | Patches are encouraged, and may be submitted by forking this project and 37 | submitting a pull request through GitHub. 38 | 39 | License 40 | ------- 41 | 42 | Copyright 2016 Google, Inc. All Rights Reserved. 43 | 44 | Licensed to the Apache Software Foundation (ASF) under one or more contributor 45 | license agreements. See the NOTICE file distributed with this work for 46 | additional information regarding copyright ownership. The ASF licenses this 47 | file to you under the Apache License, Version 2.0 (the "License"); you may not 48 | use this file except in compliance with the License. You may obtain a copy of 49 | the License at 50 | 51 | http://www.apache.org/licenses/LICENSE-2.0 52 | 53 | Unless required by applicable law or agreed to in writing, software 54 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 55 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 56 | License for the specific language governing permissions and limitations under 57 | the License. 58 | 59 | --------------------------------------------------------------------------------