├── README.md ├── DetectFaceLandmarks.xcodeproj ├── xcuserdata │ └── mathieu.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── DetectFaceLandmarks.xcscheme ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── mathieu.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── project.pbxproj ├── DetectFaceLandmarksTests ├── Info.plist └── DetectFaceLandmarksTests.swift ├── DetectFaceLandmarksUITests ├── Info.plist └── DetectFaceLandmarksUITests.swift ├── DetectFaceLandmarks ├── Info.plist ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── AppDelegate.swift ├── ViewController.swift ├── UIImage+Scale.swift └── FaceLandmarksDetector.swift └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | # Face Landmarks Detection 2 | Face Landmarks Detection using iOS11 Vision framework 3 | 4 | ![](http://i63.tinypic.com/j10k7b.gif "Face Landmarks Demo") -------------------------------------------------------------------------------- /DetectFaceLandmarks.xcodeproj/xcuserdata/mathieu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /DetectFaceLandmarks.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DetectFaceLandmarks.xcodeproj/project.xcworkspace/xcuserdata/mathieu.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattlawer/FaceLandmarksDetection/HEAD/DetectFaceLandmarks.xcodeproj/project.xcworkspace/xcuserdata/mathieu.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /DetectFaceLandmarksTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /DetectFaceLandmarksUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /DetectFaceLandmarks.xcodeproj/xcuserdata/mathieu.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | DetectFaceLandmarks.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 4EDCF94B1EFAA5E300571ED9 16 | 17 | primary 18 | 19 | 20 | 4EDCF95F1EFAA5E300571ED9 21 | 22 | primary 23 | 24 | 25 | 4EDCF96A1EFAA5E300571ED9 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /DetectFaceLandmarksTests/DetectFaceLandmarksTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetectFaceLandmarksTests.swift 3 | // DetectFaceLandmarksTests 4 | // 5 | // Created by mathieu on 21/06/2017. 6 | // Copyright © 2017 mathieu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import DetectFaceLandmarks 11 | 12 | class DetectFaceLandmarksTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /DetectFaceLandmarksUITests/DetectFaceLandmarksUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetectFaceLandmarksUITests.swift 3 | // DetectFaceLandmarksUITests 4 | // 5 | // Created by mathieu on 21/06/2017. 6 | // Copyright © 2017 mathieu. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class DetectFaceLandmarksUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /DetectFaceLandmarks/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSCameraUsageDescription 24 | Camera ? 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /DetectFaceLandmarks/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 | -------------------------------------------------------------------------------- /DetectFaceLandmarks/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /DetectFaceLandmarks/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DetectFaceLandmarks 4 | // 5 | // Created by mathieu on 21/06/2017. 6 | // Copyright © 2017 mathieu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /DetectFaceLandmarks/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DetectFaceLandmarks 4 | // 5 | // Created by mathieu on 21/06/2017. 6 | // Copyright © 2017 mathieu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AVFoundation 11 | import Vision 12 | 13 | class ViewController: UIViewController { 14 | 15 | let faceDetector = FaceLandmarksDetector() 16 | let captureSession = AVCaptureSession() 17 | @IBOutlet weak var imageView: UIImageView! 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | 22 | // Do any additional setup after loading the view, typically from a nib. 23 | configureDevice() 24 | } 25 | 26 | private func getDevice() -> AVCaptureDevice? { 27 | let discoverSession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInDualCamera, .builtInTelephotoCamera, .builtInWideAngleCamera], mediaType: .video, position: .front) 28 | return discoverSession.devices.first 29 | } 30 | 31 | private func configureDevice() { 32 | if let device = getDevice() { 33 | do { 34 | try device.lockForConfiguration() 35 | if device.isFocusModeSupported(.continuousAutoFocus) { 36 | device.focusMode = .continuousAutoFocus 37 | } 38 | device.unlockForConfiguration() 39 | } catch { print("failed to lock config") } 40 | 41 | do { 42 | let input = try AVCaptureDeviceInput(device: device) 43 | captureSession.addInput(input) 44 | } catch { print("failed to create AVCaptureDeviceInput") } 45 | 46 | captureSession.startRunning() 47 | 48 | let videoOutput = AVCaptureVideoDataOutput() 49 | videoOutput.videoSettings = [String(kCVPixelBufferPixelFormatTypeKey): Int(kCVPixelFormatType_32BGRA)] 50 | videoOutput.alwaysDiscardsLateVideoFrames = true 51 | videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.global(qos: .utility)) 52 | 53 | if captureSession.canAddOutput(videoOutput) { 54 | captureSession.addOutput(videoOutput) 55 | } 56 | } 57 | } 58 | 59 | override func didReceiveMemoryWarning() { 60 | super.didReceiveMemoryWarning() 61 | // Dispose of any resources that can be recreated. 62 | } 63 | 64 | } 65 | 66 | extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate { 67 | 68 | func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { 69 | 70 | // Scale image to process it faster 71 | let maxSize = CGSize(width: 1024, height: 1024) 72 | 73 | if let image = UIImage(sampleBuffer: sampleBuffer)?.flipped()?.imageWithAspectFit(size: maxSize) { 74 | faceDetector.highlightFaces(for: image) { (resultImage) in 75 | DispatchQueue.main.async { 76 | self.imageView?.image = resultImage 77 | } 78 | } 79 | } 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /DetectFaceLandmarks/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 | -------------------------------------------------------------------------------- /DetectFaceLandmarks/UIImage+Scale.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Scale.swift 3 | // AICamera 4 | // 5 | // Created by mathieu on 08/06/2017. 6 | // Copyright © 2017 mathieu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreMedia 11 | 12 | extension UIImage { 13 | 14 | convenience init?(sampleBuffer: CMSampleBuffer) { 15 | 16 | guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return nil } 17 | CVPixelBufferLockBaseAddress(imageBuffer, CVPixelBufferLockFlags.readOnly) 18 | 19 | let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer) 20 | let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer) 21 | let width = CVPixelBufferGetWidth(imageBuffer) 22 | let height = CVPixelBufferGetHeight(imageBuffer) 23 | let colorSpace = CGColorSpaceCreateDeviceRGB() 24 | 25 | let bitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue & CGBitmapInfo.alphaInfoMask.rawValue 26 | guard let context = CGContext(data: baseAddress, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else { return nil } 27 | let quartzImage = context.makeImage() 28 | CVPixelBufferUnlockBaseAddress(imageBuffer, CVPixelBufferLockFlags.readOnly) 29 | 30 | if let quartz = quartzImage { 31 | self.init(cgImage: quartz, scale: 1.0, orientation: .right) 32 | //self.init(cgImage: quartz) 33 | } else { 34 | return nil 35 | } 36 | } 37 | 38 | func hasAlpha() -> Bool { 39 | if let cgImage = self.cgImage { 40 | let alpha = cgImage.alphaInfo 41 | return alpha == .first || alpha == .last || alpha == .premultipliedFirst || alpha == .premultipliedLast 42 | } 43 | return false 44 | } 45 | 46 | func flipped() -> UIImage? { 47 | UIGraphicsBeginImageContextWithOptions(self.size, !self.hasAlpha(), self.scale) 48 | let context = UIGraphicsGetCurrentContext()! 49 | context.translateBy(x: self.size.width, y: 0.0) 50 | context.scaleBy(x: -1.0, y: 1.0) 51 | let rect = CGRect(origin: .zero, size: self.size) 52 | self.draw(in: rect) 53 | let retVal = UIGraphicsGetImageFromCurrentImageContext() 54 | UIGraphicsEndImageContext() 55 | return retVal 56 | } 57 | 58 | func imageWithAspectFit(size:CGSize) -> UIImage? { 59 | let aspectFitSize = self.getAspectFitSize(destination: size) 60 | let resizedImage = self.resize(size: aspectFitSize) 61 | return resizedImage 62 | } 63 | 64 | private func getAspectFitSize(destination dst:CGSize) -> CGSize { 65 | var result = CGSize.zero 66 | var scaleRatio = CGPoint() 67 | 68 | if (dst.width != 0) {scaleRatio.x = self.size.width / dst.width} 69 | if (dst.height != 0) {scaleRatio.y = self.size.height / dst.height} 70 | let scaleFactor = max(scaleRatio.x, scaleRatio.y) 71 | 72 | result.width = scaleRatio.x * dst.width / scaleFactor 73 | result.height = scaleRatio.y * dst.height / scaleFactor 74 | return result 75 | } 76 | 77 | func resize(size:CGSize) -> UIImage? { 78 | let imageRect = CGRect(origin: .zero, size: size); 79 | 80 | UIGraphicsBeginImageContextWithOptions(size, false, 1.0); 81 | self.draw(in: imageRect) 82 | let scaled = UIGraphicsGetImageFromCurrentImageContext(); 83 | UIGraphicsEndImageContext(); 84 | return scaled 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /DetectFaceLandmarks.xcodeproj/xcuserdata/mathieu.xcuserdatad/xcschemes/DetectFaceLandmarks.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 44 | 50 | 51 | 52 | 53 | 54 | 60 | 61 | 62 | 63 | 64 | 65 | 76 | 78 | 84 | 85 | 86 | 87 | 88 | 89 | 95 | 97 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /DetectFaceLandmarks/FaceLandmarksDetector.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FaceLandmarksDetector.swift 3 | // DetectFaceLandmarks 4 | // 5 | // Created by mathieu on 09/07/2017. 6 | // Copyright © 2017 mathieu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Vision 11 | 12 | class FaceLandmarksDetector { 13 | 14 | open func highlightFaces(for source: UIImage, complete: @escaping (UIImage) -> Void) { 15 | var resultImage = source 16 | let detectFaceRequest = VNDetectFaceLandmarksRequest { (request, error) in 17 | if error == nil { 18 | if let results = request.results as? [VNFaceObservation] { 19 | for faceObservation in results { 20 | guard let landmarks = faceObservation.landmarks else { 21 | continue 22 | } 23 | let boundingRect = faceObservation.boundingBox 24 | 25 | resultImage = self.drawOnImage(source: resultImage, boundingRect: boundingRect, faceLandmarks: landmarks) 26 | } 27 | } 28 | } else { 29 | print(error!.localizedDescription) 30 | } 31 | complete(resultImage) 32 | } 33 | 34 | let vnImage = VNImageRequestHandler(cgImage: source.cgImage!, options: [:]) 35 | try? vnImage.perform([detectFaceRequest]) 36 | } 37 | 38 | private func drawOnImage(source: UIImage, boundingRect: CGRect, faceLandmarks: VNFaceLandmarks2D) -> UIImage { 39 | UIGraphicsBeginImageContextWithOptions(source.size, false, 1) 40 | let context = UIGraphicsGetCurrentContext()! 41 | context.translateBy(x: 0.0, y: source.size.height) 42 | context.scaleBy(x: 1.0, y: -1.0) 43 | //context.setBlendMode(CGBlendMode.colorBurn) 44 | context.setLineJoin(.round) 45 | context.setLineCap(.round) 46 | context.setShouldAntialias(true) 47 | context.setAllowsAntialiasing(true) 48 | 49 | let rectWidth = source.size.width * boundingRect.size.width 50 | let rectHeight = source.size.height * boundingRect.size.height 51 | 52 | //draw image 53 | let rect = CGRect(x: 0, y:0, width: source.size.width, height: source.size.height) 54 | context.draw(source.cgImage!, in: rect) 55 | 56 | 57 | //draw bound rect 58 | context.setStrokeColor(UIColor.green.cgColor) 59 | context.addRect(CGRect(x: boundingRect.origin.x * source.size.width, y:boundingRect.origin.y * source.size.height, width: rectWidth, height: rectHeight)) 60 | context.drawPath(using: CGPathDrawingMode.stroke) 61 | 62 | //draw overlay 63 | context.setLineWidth(1.0) 64 | 65 | func drawFeature(_ feature: VNFaceLandmarkRegion2D, color: CGColor, close: Bool = false) { 66 | context.setStrokeColor(color) 67 | context.setFillColor(color) 68 | for point in feature.normalizedPoints { 69 | // Draw DEBUG numbers 70 | let textFontAttributes = [ 71 | NSAttributedStringKey.font: UIFont.systemFont(ofSize: 16), 72 | NSAttributedStringKey.foregroundColor: UIColor.white 73 | ] 74 | context.saveGState() 75 | // rotate to draw numbers 76 | context.translateBy(x: 0.0, y: source.size.height) 77 | context.scaleBy(x: 1.0, y: -1.0) 78 | let mp = CGPoint(x: boundingRect.origin.x * source.size.width + point.x * rectWidth, y: source.size.height - (boundingRect.origin.y * source.size.height + point.y * rectHeight)) 79 | context.fillEllipse(in: CGRect(origin: CGPoint(x: mp.x-2.0, y: mp.y-2), size: CGSize(width: 4.0, height: 4.0))) 80 | if let index = feature.normalizedPoints.index(of: point) { 81 | NSString(format: "%d", index).draw(at: mp, withAttributes: textFontAttributes) 82 | } 83 | context.restoreGState() 84 | } 85 | let mappedPoints = feature.normalizedPoints.map { CGPoint(x: boundingRect.origin.x * source.size.width + $0.x * rectWidth, y: boundingRect.origin.y * source.size.height + $0.y * rectHeight) } 86 | context.addLines(between: mappedPoints) 87 | if close, let first = mappedPoints.first, let lats = mappedPoints.last { 88 | context.addLines(between: [lats, first]) 89 | } 90 | context.strokePath() 91 | } 92 | 93 | if let faceContour = faceLandmarks.faceContour { 94 | drawFeature(faceContour, color: UIColor.magenta.cgColor) 95 | } 96 | 97 | if let leftEye = faceLandmarks.leftEye { 98 | drawFeature(leftEye, color: UIColor.cyan.cgColor, close: true) 99 | } 100 | if let rightEye = faceLandmarks.rightEye { 101 | drawFeature(rightEye, color: UIColor.cyan.cgColor, close: true) 102 | } 103 | if let leftPupil = faceLandmarks.leftPupil { 104 | drawFeature(leftPupil, color: UIColor.cyan.cgColor, close: true) 105 | } 106 | if let rightPupil = faceLandmarks.rightPupil { 107 | drawFeature(rightPupil, color: UIColor.cyan.cgColor, close: true) 108 | } 109 | 110 | if let nose = faceLandmarks.nose { 111 | drawFeature(nose, color: UIColor.green.cgColor) 112 | } 113 | if let noseCrest = faceLandmarks.noseCrest { 114 | drawFeature(noseCrest, color: UIColor.green.cgColor) 115 | } 116 | 117 | if let medianLine = faceLandmarks.medianLine { 118 | drawFeature(medianLine, color: UIColor.gray.cgColor) 119 | } 120 | 121 | if let outerLips = faceLandmarks.outerLips { 122 | drawFeature(outerLips, color: UIColor.red.cgColor, close: true) 123 | } 124 | if let innerLips = faceLandmarks.innerLips { 125 | drawFeature(innerLips, color: UIColor.red.cgColor, close: true) 126 | } 127 | 128 | if let leftEyebrow = faceLandmarks.leftEyebrow { 129 | drawFeature(leftEyebrow, color: UIColor.blue.cgColor) 130 | } 131 | if let rightEyebrow = faceLandmarks.rightEyebrow { 132 | drawFeature(rightEyebrow, color: UIColor.blue.cgColor) 133 | } 134 | 135 | let coloredImg : UIImage = UIGraphicsGetImageFromCurrentImageContext()! 136 | UIGraphicsEndImageContext() 137 | return coloredImg 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /DetectFaceLandmarks.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4E40775D1F129F1F00D570AB /* UIImage+Scale.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E40775C1F129ED900D570AB /* UIImage+Scale.swift */; }; 11 | 4E871D991F129B7D00062C1C /* FaceLandmarksDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E871D981F129B7D00062C1C /* FaceLandmarksDetector.swift */; }; 12 | 4EDCF9501EFAA5E300571ED9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDCF94F1EFAA5E300571ED9 /* AppDelegate.swift */; }; 13 | 4EDCF9521EFAA5E300571ED9 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDCF9511EFAA5E300571ED9 /* ViewController.swift */; }; 14 | 4EDCF9551EFAA5E300571ED9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4EDCF9531EFAA5E300571ED9 /* Main.storyboard */; }; 15 | 4EDCF9571EFAA5E300571ED9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4EDCF9561EFAA5E300571ED9 /* Assets.xcassets */; }; 16 | 4EDCF95A1EFAA5E300571ED9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4EDCF9581EFAA5E300571ED9 /* LaunchScreen.storyboard */; }; 17 | 4EDCF9651EFAA5E300571ED9 /* DetectFaceLandmarksTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDCF9641EFAA5E300571ED9 /* DetectFaceLandmarksTests.swift */; }; 18 | 4EDCF9701EFAA5E300571ED9 /* DetectFaceLandmarksUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDCF96F1EFAA5E300571ED9 /* DetectFaceLandmarksUITests.swift */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 4EDCF9611EFAA5E300571ED9 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = 4EDCF9441EFAA5E200571ED9 /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = 4EDCF94B1EFAA5E300571ED9; 27 | remoteInfo = DetectFaceLandmarks; 28 | }; 29 | 4EDCF96C1EFAA5E300571ED9 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = 4EDCF9441EFAA5E200571ED9 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = 4EDCF94B1EFAA5E300571ED9; 34 | remoteInfo = DetectFaceLandmarks; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 4E40775C1F129ED900D570AB /* UIImage+Scale.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Scale.swift"; sourceTree = ""; }; 40 | 4E871D981F129B7D00062C1C /* FaceLandmarksDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaceLandmarksDetector.swift; sourceTree = ""; }; 41 | 4EDCF94C1EFAA5E300571ED9 /* DetectFaceLandmarks.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DetectFaceLandmarks.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 4EDCF94F1EFAA5E300571ED9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 43 | 4EDCF9511EFAA5E300571ED9 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 44 | 4EDCF9541EFAA5E300571ED9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 45 | 4EDCF9561EFAA5E300571ED9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 46 | 4EDCF9591EFAA5E300571ED9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 47 | 4EDCF95B1EFAA5E300571ED9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 48 | 4EDCF9601EFAA5E300571ED9 /* DetectFaceLandmarksTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DetectFaceLandmarksTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | 4EDCF9641EFAA5E300571ED9 /* DetectFaceLandmarksTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetectFaceLandmarksTests.swift; sourceTree = ""; }; 50 | 4EDCF9661EFAA5E300571ED9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | 4EDCF96B1EFAA5E300571ED9 /* DetectFaceLandmarksUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DetectFaceLandmarksUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 4EDCF96F1EFAA5E300571ED9 /* DetectFaceLandmarksUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetectFaceLandmarksUITests.swift; sourceTree = ""; }; 53 | 4EDCF9711EFAA5E300571ED9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | /* End PBXFileReference section */ 55 | 56 | /* Begin PBXFrameworksBuildPhase section */ 57 | 4EDCF9491EFAA5E300571ED9 /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | 4EDCF95D1EFAA5E300571ED9 /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | 4EDCF9681EFAA5E300571ED9 /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | /* End PBXFrameworksBuildPhase section */ 79 | 80 | /* Begin PBXGroup section */ 81 | 4EDCF9431EFAA5E200571ED9 = { 82 | isa = PBXGroup; 83 | children = ( 84 | 4EDCF94E1EFAA5E300571ED9 /* DetectFaceLandmarks */, 85 | 4EDCF9631EFAA5E300571ED9 /* DetectFaceLandmarksTests */, 86 | 4EDCF96E1EFAA5E300571ED9 /* DetectFaceLandmarksUITests */, 87 | 4EDCF94D1EFAA5E300571ED9 /* Products */, 88 | ); 89 | sourceTree = ""; 90 | }; 91 | 4EDCF94D1EFAA5E300571ED9 /* Products */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 4EDCF94C1EFAA5E300571ED9 /* DetectFaceLandmarks.app */, 95 | 4EDCF9601EFAA5E300571ED9 /* DetectFaceLandmarksTests.xctest */, 96 | 4EDCF96B1EFAA5E300571ED9 /* DetectFaceLandmarksUITests.xctest */, 97 | ); 98 | name = Products; 99 | sourceTree = ""; 100 | }; 101 | 4EDCF94E1EFAA5E300571ED9 /* DetectFaceLandmarks */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 4EDCF94F1EFAA5E300571ED9 /* AppDelegate.swift */, 105 | 4EDCF9511EFAA5E300571ED9 /* ViewController.swift */, 106 | 4E871D981F129B7D00062C1C /* FaceLandmarksDetector.swift */, 107 | 4E40775C1F129ED900D570AB /* UIImage+Scale.swift */, 108 | 4EDCF9531EFAA5E300571ED9 /* Main.storyboard */, 109 | 4EDCF9561EFAA5E300571ED9 /* Assets.xcassets */, 110 | 4EDCF9581EFAA5E300571ED9 /* LaunchScreen.storyboard */, 111 | 4EDCF95B1EFAA5E300571ED9 /* Info.plist */, 112 | ); 113 | path = DetectFaceLandmarks; 114 | sourceTree = ""; 115 | }; 116 | 4EDCF9631EFAA5E300571ED9 /* DetectFaceLandmarksTests */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 4EDCF9641EFAA5E300571ED9 /* DetectFaceLandmarksTests.swift */, 120 | 4EDCF9661EFAA5E300571ED9 /* Info.plist */, 121 | ); 122 | path = DetectFaceLandmarksTests; 123 | sourceTree = ""; 124 | }; 125 | 4EDCF96E1EFAA5E300571ED9 /* DetectFaceLandmarksUITests */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 4EDCF96F1EFAA5E300571ED9 /* DetectFaceLandmarksUITests.swift */, 129 | 4EDCF9711EFAA5E300571ED9 /* Info.plist */, 130 | ); 131 | path = DetectFaceLandmarksUITests; 132 | sourceTree = ""; 133 | }; 134 | /* End PBXGroup section */ 135 | 136 | /* Begin PBXNativeTarget section */ 137 | 4EDCF94B1EFAA5E300571ED9 /* DetectFaceLandmarks */ = { 138 | isa = PBXNativeTarget; 139 | buildConfigurationList = 4EDCF9741EFAA5E300571ED9 /* Build configuration list for PBXNativeTarget "DetectFaceLandmarks" */; 140 | buildPhases = ( 141 | 4EDCF9481EFAA5E300571ED9 /* Sources */, 142 | 4EDCF9491EFAA5E300571ED9 /* Frameworks */, 143 | 4EDCF94A1EFAA5E300571ED9 /* Resources */, 144 | ); 145 | buildRules = ( 146 | ); 147 | dependencies = ( 148 | ); 149 | name = DetectFaceLandmarks; 150 | productName = DetectFaceLandmarks; 151 | productReference = 4EDCF94C1EFAA5E300571ED9 /* DetectFaceLandmarks.app */; 152 | productType = "com.apple.product-type.application"; 153 | }; 154 | 4EDCF95F1EFAA5E300571ED9 /* DetectFaceLandmarksTests */ = { 155 | isa = PBXNativeTarget; 156 | buildConfigurationList = 4EDCF9771EFAA5E300571ED9 /* Build configuration list for PBXNativeTarget "DetectFaceLandmarksTests" */; 157 | buildPhases = ( 158 | 4EDCF95C1EFAA5E300571ED9 /* Sources */, 159 | 4EDCF95D1EFAA5E300571ED9 /* Frameworks */, 160 | 4EDCF95E1EFAA5E300571ED9 /* Resources */, 161 | ); 162 | buildRules = ( 163 | ); 164 | dependencies = ( 165 | 4EDCF9621EFAA5E300571ED9 /* PBXTargetDependency */, 166 | ); 167 | name = DetectFaceLandmarksTests; 168 | productName = DetectFaceLandmarksTests; 169 | productReference = 4EDCF9601EFAA5E300571ED9 /* DetectFaceLandmarksTests.xctest */; 170 | productType = "com.apple.product-type.bundle.unit-test"; 171 | }; 172 | 4EDCF96A1EFAA5E300571ED9 /* DetectFaceLandmarksUITests */ = { 173 | isa = PBXNativeTarget; 174 | buildConfigurationList = 4EDCF97A1EFAA5E300571ED9 /* Build configuration list for PBXNativeTarget "DetectFaceLandmarksUITests" */; 175 | buildPhases = ( 176 | 4EDCF9671EFAA5E300571ED9 /* Sources */, 177 | 4EDCF9681EFAA5E300571ED9 /* Frameworks */, 178 | 4EDCF9691EFAA5E300571ED9 /* Resources */, 179 | ); 180 | buildRules = ( 181 | ); 182 | dependencies = ( 183 | 4EDCF96D1EFAA5E300571ED9 /* PBXTargetDependency */, 184 | ); 185 | name = DetectFaceLandmarksUITests; 186 | productName = DetectFaceLandmarksUITests; 187 | productReference = 4EDCF96B1EFAA5E300571ED9 /* DetectFaceLandmarksUITests.xctest */; 188 | productType = "com.apple.product-type.bundle.ui-testing"; 189 | }; 190 | /* End PBXNativeTarget section */ 191 | 192 | /* Begin PBXProject section */ 193 | 4EDCF9441EFAA5E200571ED9 /* Project object */ = { 194 | isa = PBXProject; 195 | attributes = { 196 | LastSwiftUpdateCheck = 0900; 197 | LastUpgradeCheck = 0900; 198 | ORGANIZATIONNAME = mathieu; 199 | TargetAttributes = { 200 | 4EDCF94B1EFAA5E300571ED9 = { 201 | CreatedOnToolsVersion = 9.0; 202 | }; 203 | 4EDCF95F1EFAA5E300571ED9 = { 204 | CreatedOnToolsVersion = 9.0; 205 | TestTargetID = 4EDCF94B1EFAA5E300571ED9; 206 | }; 207 | 4EDCF96A1EFAA5E300571ED9 = { 208 | CreatedOnToolsVersion = 9.0; 209 | TestTargetID = 4EDCF94B1EFAA5E300571ED9; 210 | }; 211 | }; 212 | }; 213 | buildConfigurationList = 4EDCF9471EFAA5E200571ED9 /* Build configuration list for PBXProject "DetectFaceLandmarks" */; 214 | compatibilityVersion = "Xcode 8.0"; 215 | developmentRegion = en; 216 | hasScannedForEncodings = 0; 217 | knownRegions = ( 218 | en, 219 | Base, 220 | ); 221 | mainGroup = 4EDCF9431EFAA5E200571ED9; 222 | productRefGroup = 4EDCF94D1EFAA5E300571ED9 /* Products */; 223 | projectDirPath = ""; 224 | projectRoot = ""; 225 | targets = ( 226 | 4EDCF94B1EFAA5E300571ED9 /* DetectFaceLandmarks */, 227 | 4EDCF95F1EFAA5E300571ED9 /* DetectFaceLandmarksTests */, 228 | 4EDCF96A1EFAA5E300571ED9 /* DetectFaceLandmarksUITests */, 229 | ); 230 | }; 231 | /* End PBXProject section */ 232 | 233 | /* Begin PBXResourcesBuildPhase section */ 234 | 4EDCF94A1EFAA5E300571ED9 /* Resources */ = { 235 | isa = PBXResourcesBuildPhase; 236 | buildActionMask = 2147483647; 237 | files = ( 238 | 4EDCF95A1EFAA5E300571ED9 /* LaunchScreen.storyboard in Resources */, 239 | 4EDCF9571EFAA5E300571ED9 /* Assets.xcassets in Resources */, 240 | 4EDCF9551EFAA5E300571ED9 /* Main.storyboard in Resources */, 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | 4EDCF95E1EFAA5E300571ED9 /* Resources */ = { 245 | isa = PBXResourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | 4EDCF9691EFAA5E300571ED9 /* Resources */ = { 252 | isa = PBXResourcesBuildPhase; 253 | buildActionMask = 2147483647; 254 | files = ( 255 | ); 256 | runOnlyForDeploymentPostprocessing = 0; 257 | }; 258 | /* End PBXResourcesBuildPhase section */ 259 | 260 | /* Begin PBXSourcesBuildPhase section */ 261 | 4EDCF9481EFAA5E300571ED9 /* Sources */ = { 262 | isa = PBXSourcesBuildPhase; 263 | buildActionMask = 2147483647; 264 | files = ( 265 | 4EDCF9521EFAA5E300571ED9 /* ViewController.swift in Sources */, 266 | 4E871D991F129B7D00062C1C /* FaceLandmarksDetector.swift in Sources */, 267 | 4E40775D1F129F1F00D570AB /* UIImage+Scale.swift in Sources */, 268 | 4EDCF9501EFAA5E300571ED9 /* AppDelegate.swift in Sources */, 269 | ); 270 | runOnlyForDeploymentPostprocessing = 0; 271 | }; 272 | 4EDCF95C1EFAA5E300571ED9 /* Sources */ = { 273 | isa = PBXSourcesBuildPhase; 274 | buildActionMask = 2147483647; 275 | files = ( 276 | 4EDCF9651EFAA5E300571ED9 /* DetectFaceLandmarksTests.swift in Sources */, 277 | ); 278 | runOnlyForDeploymentPostprocessing = 0; 279 | }; 280 | 4EDCF9671EFAA5E300571ED9 /* Sources */ = { 281 | isa = PBXSourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 4EDCF9701EFAA5E300571ED9 /* DetectFaceLandmarksUITests.swift in Sources */, 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | }; 288 | /* End PBXSourcesBuildPhase section */ 289 | 290 | /* Begin PBXTargetDependency section */ 291 | 4EDCF9621EFAA5E300571ED9 /* PBXTargetDependency */ = { 292 | isa = PBXTargetDependency; 293 | target = 4EDCF94B1EFAA5E300571ED9 /* DetectFaceLandmarks */; 294 | targetProxy = 4EDCF9611EFAA5E300571ED9 /* PBXContainerItemProxy */; 295 | }; 296 | 4EDCF96D1EFAA5E300571ED9 /* PBXTargetDependency */ = { 297 | isa = PBXTargetDependency; 298 | target = 4EDCF94B1EFAA5E300571ED9 /* DetectFaceLandmarks */; 299 | targetProxy = 4EDCF96C1EFAA5E300571ED9 /* PBXContainerItemProxy */; 300 | }; 301 | /* End PBXTargetDependency section */ 302 | 303 | /* Begin PBXVariantGroup section */ 304 | 4EDCF9531EFAA5E300571ED9 /* Main.storyboard */ = { 305 | isa = PBXVariantGroup; 306 | children = ( 307 | 4EDCF9541EFAA5E300571ED9 /* Base */, 308 | ); 309 | name = Main.storyboard; 310 | sourceTree = ""; 311 | }; 312 | 4EDCF9581EFAA5E300571ED9 /* LaunchScreen.storyboard */ = { 313 | isa = PBXVariantGroup; 314 | children = ( 315 | 4EDCF9591EFAA5E300571ED9 /* Base */, 316 | ); 317 | name = LaunchScreen.storyboard; 318 | sourceTree = ""; 319 | }; 320 | /* End PBXVariantGroup section */ 321 | 322 | /* Begin XCBuildConfiguration section */ 323 | 4EDCF9721EFAA5E300571ED9 /* Debug */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | ALWAYS_SEARCH_USER_PATHS = NO; 327 | CLANG_ANALYZER_NONNULL = YES; 328 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 329 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 330 | CLANG_CXX_LIBRARY = "libc++"; 331 | CLANG_ENABLE_MODULES = YES; 332 | CLANG_ENABLE_OBJC_ARC = YES; 333 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 334 | CLANG_WARN_BOOL_CONVERSION = YES; 335 | CLANG_WARN_COMMA = YES; 336 | CLANG_WARN_CONSTANT_CONVERSION = YES; 337 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 338 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 339 | CLANG_WARN_EMPTY_BODY = YES; 340 | CLANG_WARN_ENUM_CONVERSION = YES; 341 | CLANG_WARN_INFINITE_RECURSION = YES; 342 | CLANG_WARN_INT_CONVERSION = YES; 343 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 344 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 345 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 346 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 347 | CLANG_WARN_STRICT_PROTOTYPES = YES; 348 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 349 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 350 | CLANG_WARN_UNREACHABLE_CODE = YES; 351 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 352 | CODE_SIGN_IDENTITY = "iPhone Developer"; 353 | COPY_PHASE_STRIP = NO; 354 | DEBUG_INFORMATION_FORMAT = dwarf; 355 | ENABLE_STRICT_OBJC_MSGSEND = YES; 356 | ENABLE_TESTABILITY = YES; 357 | GCC_C_LANGUAGE_STANDARD = gnu11; 358 | GCC_DYNAMIC_NO_PIC = NO; 359 | GCC_NO_COMMON_BLOCKS = YES; 360 | GCC_OPTIMIZATION_LEVEL = 0; 361 | GCC_PREPROCESSOR_DEFINITIONS = ( 362 | "DEBUG=1", 363 | "$(inherited)", 364 | ); 365 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 366 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 367 | GCC_WARN_UNDECLARED_SELECTOR = YES; 368 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 369 | GCC_WARN_UNUSED_FUNCTION = YES; 370 | GCC_WARN_UNUSED_VARIABLE = YES; 371 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 372 | MTL_ENABLE_DEBUG_INFO = YES; 373 | ONLY_ACTIVE_ARCH = YES; 374 | SDKROOT = iphoneos; 375 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 376 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 377 | }; 378 | name = Debug; 379 | }; 380 | 4EDCF9731EFAA5E300571ED9 /* Release */ = { 381 | isa = XCBuildConfiguration; 382 | buildSettings = { 383 | ALWAYS_SEARCH_USER_PATHS = NO; 384 | CLANG_ANALYZER_NONNULL = YES; 385 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 386 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 387 | CLANG_CXX_LIBRARY = "libc++"; 388 | CLANG_ENABLE_MODULES = YES; 389 | CLANG_ENABLE_OBJC_ARC = YES; 390 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 391 | CLANG_WARN_BOOL_CONVERSION = YES; 392 | CLANG_WARN_COMMA = YES; 393 | CLANG_WARN_CONSTANT_CONVERSION = YES; 394 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 395 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 396 | CLANG_WARN_EMPTY_BODY = YES; 397 | CLANG_WARN_ENUM_CONVERSION = YES; 398 | CLANG_WARN_INFINITE_RECURSION = YES; 399 | CLANG_WARN_INT_CONVERSION = YES; 400 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 401 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 402 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 403 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 404 | CLANG_WARN_STRICT_PROTOTYPES = YES; 405 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 406 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 407 | CLANG_WARN_UNREACHABLE_CODE = YES; 408 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 409 | CODE_SIGN_IDENTITY = "iPhone Developer"; 410 | COPY_PHASE_STRIP = NO; 411 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 412 | ENABLE_NS_ASSERTIONS = NO; 413 | ENABLE_STRICT_OBJC_MSGSEND = YES; 414 | GCC_C_LANGUAGE_STANDARD = gnu11; 415 | GCC_NO_COMMON_BLOCKS = YES; 416 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 417 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 418 | GCC_WARN_UNDECLARED_SELECTOR = YES; 419 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 420 | GCC_WARN_UNUSED_FUNCTION = YES; 421 | GCC_WARN_UNUSED_VARIABLE = YES; 422 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 423 | MTL_ENABLE_DEBUG_INFO = NO; 424 | SDKROOT = iphoneos; 425 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 426 | VALIDATE_PRODUCT = YES; 427 | }; 428 | name = Release; 429 | }; 430 | 4EDCF9751EFAA5E300571ED9 /* Debug */ = { 431 | isa = XCBuildConfiguration; 432 | buildSettings = { 433 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 434 | DEVELOPMENT_TEAM = 3ZXVY9JDCR; 435 | INFOPLIST_FILE = DetectFaceLandmarks/Info.plist; 436 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 437 | PRODUCT_BUNDLE_IDENTIFIER = com.mathieubolard.DetectFaceLandmarks; 438 | PRODUCT_NAME = "$(TARGET_NAME)"; 439 | SWIFT_VERSION = 4.0; 440 | TARGETED_DEVICE_FAMILY = "1,2"; 441 | }; 442 | name = Debug; 443 | }; 444 | 4EDCF9761EFAA5E300571ED9 /* Release */ = { 445 | isa = XCBuildConfiguration; 446 | buildSettings = { 447 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 448 | DEVELOPMENT_TEAM = 3ZXVY9JDCR; 449 | INFOPLIST_FILE = DetectFaceLandmarks/Info.plist; 450 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 451 | PRODUCT_BUNDLE_IDENTIFIER = com.mathieubolard.DetectFaceLandmarks; 452 | PRODUCT_NAME = "$(TARGET_NAME)"; 453 | SWIFT_VERSION = 4.0; 454 | TARGETED_DEVICE_FAMILY = "1,2"; 455 | }; 456 | name = Release; 457 | }; 458 | 4EDCF9781EFAA5E300571ED9 /* Debug */ = { 459 | isa = XCBuildConfiguration; 460 | buildSettings = { 461 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 462 | BUNDLE_LOADER = "$(TEST_HOST)"; 463 | DEVELOPMENT_TEAM = 3ZXVY9JDCR; 464 | INFOPLIST_FILE = DetectFaceLandmarksTests/Info.plist; 465 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 466 | PRODUCT_BUNDLE_IDENTIFIER = com.mathieubolard.DetectFaceLandmarksTests; 467 | PRODUCT_NAME = "$(TARGET_NAME)"; 468 | SWIFT_VERSION = 4.0; 469 | TARGETED_DEVICE_FAMILY = "1,2"; 470 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DetectFaceLandmarks.app/DetectFaceLandmarks"; 471 | }; 472 | name = Debug; 473 | }; 474 | 4EDCF9791EFAA5E300571ED9 /* Release */ = { 475 | isa = XCBuildConfiguration; 476 | buildSettings = { 477 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 478 | BUNDLE_LOADER = "$(TEST_HOST)"; 479 | DEVELOPMENT_TEAM = 3ZXVY9JDCR; 480 | INFOPLIST_FILE = DetectFaceLandmarksTests/Info.plist; 481 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 482 | PRODUCT_BUNDLE_IDENTIFIER = com.mathieubolard.DetectFaceLandmarksTests; 483 | PRODUCT_NAME = "$(TARGET_NAME)"; 484 | SWIFT_VERSION = 4.0; 485 | TARGETED_DEVICE_FAMILY = "1,2"; 486 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DetectFaceLandmarks.app/DetectFaceLandmarks"; 487 | }; 488 | name = Release; 489 | }; 490 | 4EDCF97B1EFAA5E300571ED9 /* Debug */ = { 491 | isa = XCBuildConfiguration; 492 | buildSettings = { 493 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 494 | DEVELOPMENT_TEAM = 3ZXVY9JDCR; 495 | INFOPLIST_FILE = DetectFaceLandmarksUITests/Info.plist; 496 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 497 | PRODUCT_BUNDLE_IDENTIFIER = com.mathieubolard.DetectFaceLandmarksUITests; 498 | PRODUCT_NAME = "$(TARGET_NAME)"; 499 | SWIFT_VERSION = 4.0; 500 | TARGETED_DEVICE_FAMILY = "1,2"; 501 | TEST_TARGET_NAME = DetectFaceLandmarks; 502 | }; 503 | name = Debug; 504 | }; 505 | 4EDCF97C1EFAA5E300571ED9 /* Release */ = { 506 | isa = XCBuildConfiguration; 507 | buildSettings = { 508 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 509 | DEVELOPMENT_TEAM = 3ZXVY9JDCR; 510 | INFOPLIST_FILE = DetectFaceLandmarksUITests/Info.plist; 511 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 512 | PRODUCT_BUNDLE_IDENTIFIER = com.mathieubolard.DetectFaceLandmarksUITests; 513 | PRODUCT_NAME = "$(TARGET_NAME)"; 514 | SWIFT_VERSION = 4.0; 515 | TARGETED_DEVICE_FAMILY = "1,2"; 516 | TEST_TARGET_NAME = DetectFaceLandmarks; 517 | }; 518 | name = Release; 519 | }; 520 | /* End XCBuildConfiguration section */ 521 | 522 | /* Begin XCConfigurationList section */ 523 | 4EDCF9471EFAA5E200571ED9 /* Build configuration list for PBXProject "DetectFaceLandmarks" */ = { 524 | isa = XCConfigurationList; 525 | buildConfigurations = ( 526 | 4EDCF9721EFAA5E300571ED9 /* Debug */, 527 | 4EDCF9731EFAA5E300571ED9 /* Release */, 528 | ); 529 | defaultConfigurationIsVisible = 0; 530 | defaultConfigurationName = Release; 531 | }; 532 | 4EDCF9741EFAA5E300571ED9 /* Build configuration list for PBXNativeTarget "DetectFaceLandmarks" */ = { 533 | isa = XCConfigurationList; 534 | buildConfigurations = ( 535 | 4EDCF9751EFAA5E300571ED9 /* Debug */, 536 | 4EDCF9761EFAA5E300571ED9 /* Release */, 537 | ); 538 | defaultConfigurationIsVisible = 0; 539 | defaultConfigurationName = Release; 540 | }; 541 | 4EDCF9771EFAA5E300571ED9 /* Build configuration list for PBXNativeTarget "DetectFaceLandmarksTests" */ = { 542 | isa = XCConfigurationList; 543 | buildConfigurations = ( 544 | 4EDCF9781EFAA5E300571ED9 /* Debug */, 545 | 4EDCF9791EFAA5E300571ED9 /* Release */, 546 | ); 547 | defaultConfigurationIsVisible = 0; 548 | defaultConfigurationName = Release; 549 | }; 550 | 4EDCF97A1EFAA5E300571ED9 /* Build configuration list for PBXNativeTarget "DetectFaceLandmarksUITests" */ = { 551 | isa = XCConfigurationList; 552 | buildConfigurations = ( 553 | 4EDCF97B1EFAA5E300571ED9 /* Debug */, 554 | 4EDCF97C1EFAA5E300571ED9 /* Release */, 555 | ); 556 | defaultConfigurationIsVisible = 0; 557 | defaultConfigurationName = Release; 558 | }; 559 | /* End XCConfigurationList section */ 560 | }; 561 | rootObject = 4EDCF9441EFAA5E200571ED9 /* Project object */; 562 | } 563 | --------------------------------------------------------------------------------