├── .gitignore
├── demo.png
├── Assets
├── More.png
├── album.png
├── More@2x.png
├── More@3x.png
├── album@2x.png
├── album@3x.png
├── photos.jpg
├── trigger.png
├── trigger@2x.png
└── trigger@3x.png
├── FaceApp
├── Assets.xcassets
│ ├── Contents.json
│ ├── sad.imageset
│ │ ├── sad.png
│ │ └── Contents.json
│ ├── face.imageset
│ │ ├── face.jpg
│ │ └── Contents.json
│ ├── fear.imageset
│ │ ├── fear.png
│ │ └── Contents.json
│ ├── more.imageset
│ │ ├── More.png
│ │ ├── More@2x.png
│ │ ├── More@3x.png
│ │ └── Contents.json
│ ├── album.imageset
│ │ ├── album.png
│ │ ├── album@2x.png
│ │ ├── album@3x.png
│ │ └── Contents.json
│ ├── anger.imageset
│ │ ├── anger.png
│ │ └── Contents.json
│ ├── happy.imageset
│ │ ├── happy.png
│ │ └── Contents.json
│ ├── sjtuicon.imageset
│ │ ├── 校标-校徽.png
│ │ ├── 校标-校徽-1.png
│ │ ├── 校标-校徽-2.png
│ │ └── Contents.json
│ ├── disgust.imageset
│ │ ├── disgust.png
│ │ └── Contents.json
│ ├── neutral.imageset
│ │ ├── neutral.png
│ │ └── Contents.json
│ ├── surprise.imageset
│ │ ├── suprise.png
│ │ └── Contents.json
│ ├── trigger.imageset
│ │ ├── trigger.png
│ │ ├── trigger@2x.png
│ │ ├── trigger@3x.png
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── 246x0w-2.jpg
│ │ └── Contents.json
│ └── captcha.imageset
│ │ ├── 1528083146.jpg
│ │ └── Contents.json
├── MyCollectionViewCell.swift
├── Base.lproj
│ ├── Main.storyboard
│ └── LaunchScreen.storyboard
├── Info.plist
├── AppDelegate.swift
├── HorizontalViewController.swift
├── TableViewController.swift
├── PreviewView.swift
├── MainViewController.swift
├── LoginViewController.swift
├── ProcessViewController.swift
└── ViewController.swift
├── FaceApp.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcuserdata
│ └── Arco.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── project.pbxproj
├── Podfile
├── README.md
├── FaceAppTests
├── Info.plist
└── FaceAppTests.swift
└── FaceAppUITests
├── Info.plist
└── FaceAppUITests.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | *.xcodeproj
2 | *.xcworkspace
3 | Pods/
4 | *.lock
5 |
--------------------------------------------------------------------------------
/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/demo.png
--------------------------------------------------------------------------------
/Assets/More.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/Assets/More.png
--------------------------------------------------------------------------------
/Assets/album.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/Assets/album.png
--------------------------------------------------------------------------------
/Assets/More@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/Assets/More@2x.png
--------------------------------------------------------------------------------
/Assets/More@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/Assets/More@3x.png
--------------------------------------------------------------------------------
/Assets/album@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/Assets/album@2x.png
--------------------------------------------------------------------------------
/Assets/album@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/Assets/album@3x.png
--------------------------------------------------------------------------------
/Assets/photos.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/Assets/photos.jpg
--------------------------------------------------------------------------------
/Assets/trigger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/Assets/trigger.png
--------------------------------------------------------------------------------
/Assets/trigger@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/Assets/trigger@2x.png
--------------------------------------------------------------------------------
/Assets/trigger@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/Assets/trigger@3x.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/sad.imageset/sad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/sad.imageset/sad.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/face.imageset/face.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/face.imageset/face.jpg
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/fear.imageset/fear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/fear.imageset/fear.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/more.imageset/More.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/more.imageset/More.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/album.imageset/album.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/album.imageset/album.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/anger.imageset/anger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/anger.imageset/anger.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/happy.imageset/happy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/happy.imageset/happy.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/more.imageset/More@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/more.imageset/More@2x.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/more.imageset/More@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/more.imageset/More@3x.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/album.imageset/album@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/album.imageset/album@2x.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/album.imageset/album@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/album.imageset/album@3x.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/sjtuicon.imageset/校标-校徽.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/sjtuicon.imageset/校标-校徽.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/disgust.imageset/disgust.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/disgust.imageset/disgust.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/neutral.imageset/neutral.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/neutral.imageset/neutral.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/sjtuicon.imageset/校标-校徽-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/sjtuicon.imageset/校标-校徽-1.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/sjtuicon.imageset/校标-校徽-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/sjtuicon.imageset/校标-校徽-2.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/surprise.imageset/suprise.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/surprise.imageset/suprise.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/trigger.imageset/trigger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/trigger.imageset/trigger.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/AppIcon.appiconset/246x0w-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/AppIcon.appiconset/246x0w-2.jpg
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/captcha.imageset/1528083146.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/captcha.imageset/1528083146.jpg
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/trigger.imageset/trigger@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/trigger.imageset/trigger@2x.png
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/trigger.imageset/trigger@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rebornzeroj/FaceApp/HEAD/FaceApp/Assets.xcassets/trigger.imageset/trigger@3x.png
--------------------------------------------------------------------------------
/FaceApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | source 'https://github.com/CocoaPods/Specs.git'
2 | platform :ios, '10.0'
3 | use_frameworks!
4 |
5 | target 'FaceApp' do
6 | pod 'SnapKit', '~> 4.0.0'
7 | pod 'Alamofire', '~> 4.7'
8 | pod 'SwiftyJSON', '~> 4.0'
9 | pod 'Toast-Swift', '~> 3.0.1'
10 | end
11 |
--------------------------------------------------------------------------------
/FaceApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/face.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "face.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/fear.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "fear.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/sad.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "sad.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FaceApp
2 |
3 | Transform your face using Artificial Intelligence.
4 |
5 | 
6 |
7 | ## Pod
8 | 1. SnapKit
9 | 2. Alamofire
10 |
11 | ## Description
12 | I train a [CycleGAN](https://github.com/hardikbansal/CycleGAN) to change face. The GAN model is deployed on a http server. The mobile app upload a pic and get a response for transformed face.
13 |
14 |
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/anger.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "anger.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/happy.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "happy.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/captcha.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "1528083146.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/disgust.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "disgust.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/neutral.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "neutral.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/surprise.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "suprise.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/more.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "More.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "More@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "More@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/album.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "album.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "album@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "album@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/sjtuicon.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "校标-校徽-2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "校标-校徽.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "校标-校徽-1.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/FaceApp/Assets.xcassets/trigger.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "trigger.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "trigger@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "trigger@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/FaceAppTests/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 |
--------------------------------------------------------------------------------
/FaceAppUITests/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 |
--------------------------------------------------------------------------------
/FaceApp.xcodeproj/xcuserdata/Arco.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | FaceApp.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 2D77DC0F209F56670022BAAB
16 |
17 | primary
18 |
19 |
20 | 2D77DC23209F56680022BAAB
21 |
22 | primary
23 |
24 |
25 | 2D77DC2E209F56680022BAAB
26 |
27 | primary
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/FaceAppTests/FaceAppTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FaceAppTests.swift
3 | // FaceAppTests
4 | //
5 | // Created by Arco on 2018/5/6.
6 | // Copyright © 2018 c. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import FaceApp
11 |
12 | class FaceAppTests: 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 |
--------------------------------------------------------------------------------
/FaceAppUITests/FaceAppUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FaceAppUITests.swift
3 | // FaceAppUITests
4 | //
5 | // Created by Arco on 2018/5/6.
6 | // Copyright © 2018 c. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class FaceAppUITests: 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 |
--------------------------------------------------------------------------------
/FaceApp/MyCollectionViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MyCollectionViewCell.swift
3 | // FaceApp
4 | //
5 | // Created by Arco on 2018/5/15.
6 | // Copyright © 2018 c. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SnapKit
11 |
12 | class MyCollectionViewCell: UICollectionViewCell {
13 |
14 | var imageView: UIImageView!
15 | var labelView: UILabel!
16 | override init(frame: CGRect) {
17 | super.init(frame: frame)
18 |
19 | // self.backgroundColor = UIColor.white
20 |
21 | let topView = UIImageView()
22 | let bottomView = UILabel()
23 |
24 | self.imageView = topView
25 | self.labelView = bottomView
26 | self.labelView?.textAlignment = .center
27 |
28 | self.contentView.addSubview(topView)
29 | self.contentView.addSubview(bottomView)
30 |
31 | topView.snp.makeConstraints { (make) -> Void in
32 | make.top.equalTo(self.contentView)
33 | make.centerX.equalTo(self.contentView)
34 | make.width.equalTo(50)
35 | make.height.equalTo(50)
36 | }
37 |
38 | bottomView.snp.makeConstraints { (make) -> Void in
39 | // make.top.equalTo(topView)
40 | make.bottom.equalTo(self.contentView)
41 | make.width.equalTo(self.contentView)
42 | make.height.equalTo(20)
43 | }
44 |
45 |
46 | }
47 |
48 | required init?(coder aDecoder: NSCoder) {
49 | super.init(coder: aDecoder)
50 | fatalError("init(coder:) has not been implemented")
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/FaceApp/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 |
--------------------------------------------------------------------------------
/FaceApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSAppTransportSecurity
6 |
7 | NSAllowsArbitraryLoads
8 |
9 |
10 | CFBundleDevelopmentRegion
11 | $(DEVELOPMENT_LANGUAGE)
12 | CFBundleExecutable
13 | $(EXECUTABLE_NAME)
14 | CFBundleIcons
15 |
16 | CFBundleIcons~ipad
17 |
18 | CFBundleIdentifier
19 | $(PRODUCT_BUNDLE_IDENTIFIER)
20 | CFBundleInfoDictionaryVersion
21 | 6.0
22 | CFBundleName
23 | $(PRODUCT_NAME)
24 | CFBundlePackageType
25 | APPL
26 | CFBundleShortVersionString
27 | 1.0
28 | CFBundleVersion
29 | 1
30 | LSRequiresIPhoneOS
31 |
32 | NSCameraUsageDescription
33 | Camera usage description
34 | NSPhotoLibraryUsageDescription
35 | Photo Library Usage Description
36 | NSPhotoLibraryAddUsageDescription
37 | NSPhotoLibraryAddUsageDescription
38 | UILaunchStoryboardName
39 | LaunchScreen
40 | UIRequiredDeviceCapabilities
41 |
42 | UISupportedInterfaceOrientations
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 | UISupportedInterfaceOrientations~ipad
49 |
50 | UIInterfaceOrientationPortrait
51 | UIInterfaceOrientationPortraitUpsideDown
52 | UIInterfaceOrientationLandscapeLeft
53 | UIInterfaceOrientationLandscapeRight
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/FaceApp/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 | "size" : "60x60",
35 | "idiom" : "iphone",
36 | "filename" : "246x0w-2.jpg",
37 | "scale" : "2x"
38 | },
39 | {
40 | "idiom" : "iphone",
41 | "size" : "60x60",
42 | "scale" : "3x"
43 | },
44 | {
45 | "idiom" : "ipad",
46 | "size" : "20x20",
47 | "scale" : "1x"
48 | },
49 | {
50 | "idiom" : "ipad",
51 | "size" : "20x20",
52 | "scale" : "2x"
53 | },
54 | {
55 | "idiom" : "ipad",
56 | "size" : "29x29",
57 | "scale" : "1x"
58 | },
59 | {
60 | "idiom" : "ipad",
61 | "size" : "29x29",
62 | "scale" : "2x"
63 | },
64 | {
65 | "idiom" : "ipad",
66 | "size" : "40x40",
67 | "scale" : "1x"
68 | },
69 | {
70 | "idiom" : "ipad",
71 | "size" : "40x40",
72 | "scale" : "2x"
73 | },
74 | {
75 | "idiom" : "ipad",
76 | "size" : "76x76",
77 | "scale" : "1x"
78 | },
79 | {
80 | "idiom" : "ipad",
81 | "size" : "76x76",
82 | "scale" : "2x"
83 | },
84 | {
85 | "idiom" : "ipad",
86 | "size" : "83.5x83.5",
87 | "scale" : "2x"
88 | },
89 | {
90 | "idiom" : "ios-marketing",
91 | "size" : "1024x1024",
92 | "scale" : "1x"
93 | }
94 | ],
95 | "info" : {
96 | "version" : 1,
97 | "author" : "xcode"
98 | }
99 | }
--------------------------------------------------------------------------------
/FaceApp/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // FaceApp
4 | //
5 | // Created by Arco on 2018/5/6.
6 | // Copyright © 2018 c. 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 | window = UIWindow(frame: UIScreen.main.bounds)
20 | self.window?.makeKeyAndVisible()
21 | // let mainViewController = MainViewController()
22 | // self.window?.rootViewController = mainViewController
23 | let nav = UINavigationController()
24 | let loginViewController = LoginViewController()
25 | nav.viewControllers = [loginViewController]
26 | // let horizontalViewController = HorizontalViewController()
27 | // nav.viewControllers = [horizontalViewController]
28 | self.window?.rootViewController = nav
29 | window?.makeKeyAndVisible()
30 | return true
31 | }
32 |
33 | func applicationWillResignActive(_ application: UIApplication) {
34 | // 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.
35 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
36 | }
37 |
38 | func applicationDidEnterBackground(_ application: UIApplication) {
39 | // 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.
40 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
41 | }
42 |
43 | func applicationWillEnterForeground(_ application: UIApplication) {
44 | // 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.
45 | }
46 |
47 | func applicationDidBecomeActive(_ application: UIApplication) {
48 | // 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.
49 | }
50 |
51 | func applicationWillTerminate(_ application: UIApplication) {
52 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
53 | }
54 |
55 |
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/FaceApp/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 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/FaceApp/HorizontalViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HorizontalViewController.swift
3 | // FaceApp
4 | //
5 | // Created by Arco on 2018/5/15.
6 | // Copyright © 2018 c. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class HorizontalViewController: UIViewController, UICollectionViewDataSource,UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
12 |
13 | var collectionView: UICollectionView?
14 | var bgColor: [UIColor] = [UIColor.green, UIColor.black, UIColor.yellow]
15 | var images: [UIImage] = [UIImage(named: "neutral")!, UIImage(named: "happy")!, UIImage(named: "surprise")!,
16 | UIImage(named: "sad")!, UIImage(named: "anger")!, UIImage(named: "fear")!, UIImage(named: "disgust")!]
17 | var captions: [String] = ["neutral", "happy", "surprise", "sad", "anger", "fear", "disgust"]
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 |
21 | // Do any additional setup after loading the view.
22 | self.view.backgroundColor = UIColor.white
23 |
24 | let layout = UICollectionViewFlowLayout()
25 | layout.scrollDirection = .horizontal
26 |
27 | let bottomView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
28 | bottomView.backgroundColor = UIColor.clear
29 | self.collectionView = bottomView
30 |
31 | bottomView.collectionViewLayout = layout
32 | bottomView.dataSource = self
33 | bottomView.delegate = self
34 |
35 | bottomView.register(MyCollectionViewCell.self, forCellWithReuseIdentifier: "collectionViewCell")
36 |
37 | self.view.addSubview(bottomView)
38 | bottomView.snp.makeConstraints{ (make) -> Void in
39 | make.bottom.equalTo(self.view)
40 | make.left.equalTo(self.view)
41 | make.right.equalTo(self.view)
42 | make.height.equalTo(80)
43 | }
44 |
45 | }
46 |
47 | override func didReceiveMemoryWarning() {
48 | super.didReceiveMemoryWarning()
49 | // Dispose of any resources that can be recreated.
50 | }
51 |
52 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
53 | return 7
54 | }
55 |
56 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
57 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as! MyCollectionViewCell
58 | // cell.backgroundColor = self.bgColor[indexPath.row]
59 | cell.imageView.image = self.images[indexPath.row]
60 | cell.labelView.text = self.captions[indexPath.row]
61 | return cell
62 | }
63 |
64 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
65 | // return CGSize(width: self.view.frame.size.width * 0.3, height: self.view.frame.size.width * 0.4)
66 | return CGSize(width: 80, height: 80)
67 | }
68 |
69 | /*
70 | // MARK: - Navigation
71 |
72 | // In a storyboard-based application, you will often want to do a little preparation before navigation
73 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
74 | // Get the new view controller using segue.destinationViewController.
75 | // Pass the selected object to the new view controller.
76 | }
77 | */
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/FaceApp/TableViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TableViewController.swift
3 | // FaceApp
4 | //
5 | // Created by Arco on 2018/5/13.
6 | // Copyright © 2018 c. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class TableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
12 |
13 | var myTableView: UITableView = UITableView()
14 | var itemsToLoad: [String] = ["One", "Two", "Three"]
15 | var delegate: Expression?
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 |
20 | // Uncomment the following line to preserve selection between presentations
21 | // self.clearsSelectionOnViewWillAppear = false
22 |
23 | // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
24 | // self.navigationItem.rightBarButtonItem = self.editButtonItem
25 | // self.navigationController?.setNavigationBarHidden(false, animated: true)
26 | myTableView.dataSource = self
27 | myTableView.delegate = self
28 |
29 | myTableView.register(UITableViewCell.self, forCellReuseIdentifier: "myCell")
30 |
31 | self.view.addSubview(myTableView)
32 | myTableView.snp.makeConstraints{ (make) -> Void in
33 | make.top.equalTo(self.view)
34 | make.left.equalTo(self.view)
35 | make.right.equalTo(self.view)
36 | make.bottom.equalTo(self.view)
37 | }
38 |
39 | self.navigationController!.navigationItem.backBarButtonItem?.title = "Done"
40 | }
41 |
42 | override func didReceiveMemoryWarning() {
43 | super.didReceiveMemoryWarning()
44 | // Dispose of any resources that can be recreated.
45 | }
46 |
47 | // MARK: - Table view data source
48 |
49 | func numberOfSections(in tableView: UITableView) -> Int {
50 | // #warning Incomplete implementation, return the number of sections
51 | return 1
52 | }
53 |
54 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
55 | // #warning Incomplete implementation, return the number of rows
56 | return self.itemsToLoad.count
57 | }
58 |
59 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
60 | let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
61 |
62 | cell.textLabel?.text = self.itemsToLoad[indexPath.row]
63 |
64 | // Configure the cell...
65 |
66 | return cell
67 | }
68 |
69 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
70 | delegate?.changeExpression(expression: self.itemsToLoad[indexPath.row])
71 | }
72 |
73 | /*
74 | // Override to support conditional editing of the table view.
75 | override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
76 | // Return false if you do not want the specified item to be editable.
77 | return true
78 | }
79 | */
80 |
81 | /*
82 | // Override to support editing the table view.
83 | override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
84 | if editingStyle == .delete {
85 | // Delete the row from the data source
86 | tableView.deleteRows(at: [indexPath], with: .fade)
87 | } else if editingStyle == .insert {
88 | // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
89 | }
90 | }
91 | */
92 |
93 | /*
94 | // Override to support rearranging the table view.
95 | override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
96 |
97 | }
98 | */
99 |
100 | /*
101 | // Override to support conditional rearranging of the table view.
102 | override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
103 | // Return false if you do not want the item to be re-orderable.
104 | return true
105 | }
106 | */
107 |
108 | /*
109 | // MARK: - Navigation
110 |
111 | // In a storyboard-based application, you will often want to do a little preparation before navigation
112 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
113 | // Get the new view controller using segue.destinationViewController.
114 | // Pass the selected object to the new view controller.
115 | }
116 | */
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/FaceApp/PreviewView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PreviewView.swift
3 | // VisionDetection
4 | //
5 | // Created by Wei Chieh Tseng on 09/06/2017.
6 | // Copyright © 2017 Willjay. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Vision
11 | import AVFoundation
12 |
13 | class PreviewView: UIView {
14 |
15 | private var maskLayer = [CAShapeLayer]()
16 |
17 | // MARK: AV capture properties
18 | var videoPreviewLayer: AVCaptureVideoPreviewLayer {
19 | return layer as! AVCaptureVideoPreviewLayer
20 | }
21 |
22 | var session: AVCaptureSession? {
23 | get {
24 | return videoPreviewLayer.session
25 | }
26 |
27 | set {
28 | videoPreviewLayer.session = newValue
29 | }
30 | }
31 |
32 | override class var layerClass: AnyClass {
33 | return AVCaptureVideoPreviewLayer.self
34 | }
35 |
36 | // Create a new layer drawing the bounding box
37 | private func createLayer(in rect: CGRect) -> CAShapeLayer{
38 |
39 | let mask = CAShapeLayer()
40 | mask.frame = rect
41 | mask.cornerRadius = 10
42 | mask.opacity = 0.75
43 | mask.borderColor = UIColor.yellow.cgColor
44 | mask.borderWidth = 2.0
45 |
46 | maskLayer.append(mask)
47 | layer.insertSublayer(mask, at: 1)
48 |
49 | return mask
50 | }
51 |
52 | func drawFaceboundingBox(face : VNFaceObservation) {
53 |
54 | let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -frame.height)
55 |
56 | let translate = CGAffineTransform.identity.scaledBy(x: frame.width, y: frame.height)
57 |
58 | // The coordinates are normalized to the dimensions of the processed image, with the origin at the image's lower-left corner.
59 | let facebounds = face.boundingBox.applying(translate).applying(transform)
60 |
61 | _ = createLayer(in: facebounds)
62 | }
63 |
64 | func drawFaceWithLandmarks(face: VNFaceObservation) {
65 |
66 | let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -frame.height)
67 |
68 | let translate = CGAffineTransform.identity.scaledBy(x: frame.width, y: frame.height)
69 |
70 | // The coordinates are normalized to the dimensions of the processed image, with the origin at the image's lower-left corner.
71 | let facebounds = face.boundingBox.applying(translate).applying(transform)
72 |
73 | // Draw the bounding rect
74 | let faceLayer = createLayer(in: facebounds)
75 |
76 | // Draw the landmarks
77 | drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.nose)!, isClosed:false)
78 | drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.noseCrest)!, isClosed:false)
79 | drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.medianLine)!, isClosed:false)
80 | drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.leftEye)!)
81 | drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.leftPupil)!)
82 | drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.leftEyebrow)!, isClosed:false)
83 | drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.rightEye)!)
84 | drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.rightPupil)!)
85 | drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.rightEye)!)
86 | drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.rightEyebrow)!, isClosed:false)
87 | drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.innerLips)!)
88 | drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.outerLips)!)
89 | drawLandmarks(on: faceLayer, faceLandmarkRegion: (face.landmarks?.faceContour)!, isClosed: false)
90 | }
91 |
92 |
93 | func drawLandmarks(on targetLayer: CALayer, faceLandmarkRegion: VNFaceLandmarkRegion2D, isClosed: Bool = true) {
94 | let rect: CGRect = targetLayer.frame
95 | var points: [CGPoint] = []
96 |
97 | for i in 0.. CALayer {
115 | let linePath = UIBezierPath()
116 | linePath.move(to: landmarkPoints.first!)
117 |
118 | for point in landmarkPoints.dropFirst() {
119 | linePath.addLine(to: point)
120 | }
121 |
122 | if isClosed {
123 | linePath.addLine(to: landmarkPoints.first!)
124 | }
125 |
126 | let lineLayer = CAShapeLayer()
127 | lineLayer.path = linePath.cgPath
128 | lineLayer.fillColor = nil
129 | lineLayer.opacity = 1.0
130 | lineLayer.strokeColor = UIColor.green.cgColor
131 | lineLayer.lineWidth = 0.02
132 |
133 | return lineLayer
134 | }
135 |
136 | func removeMask() {
137 | for mask in maskLayer {
138 | mask.removeFromSuperlayer()
139 | }
140 | maskLayer.removeAll()
141 | }
142 |
143 | }
144 |
--------------------------------------------------------------------------------
/FaceApp/MainViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainViewController.swift
3 | // FaceApp
4 | //
5 | // Created by Arco on 2018/5/7.
6 | // Copyright © 2018 c. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import AVFoundation
11 | import SnapKit
12 |
13 | class MainViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
14 |
15 | var shutterBtn: UIButton?
16 | var faceImg: UIImage?
17 | var captureSession: AVCaptureSession?
18 | var videoPreviewLayer: AVCaptureVideoPreviewLayer?
19 | var capturePhotoOutput: AVCapturePhotoOutput?
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 | // Do any additional setup after loading the view.
24 |
25 | self.navigationController?.setNavigationBarHidden(true, animated: false)
26 | let previewView = UIView()
27 | self.view.addSubview(previewView)
28 |
29 | let blurEffect = UIBlurEffect(style: .extraLight)
30 | let controlPanel = UIVisualEffectView(effect: blurEffect)
31 | self.view.addSubview(controlPanel)
32 |
33 | let btnImage = UIImage(named: "trigger")
34 | let triggerButton = UIButton()
35 | self.shutterBtn = triggerButton
36 | triggerButton.setImage(btnImage, for: .normal)
37 | triggerButton.contentMode = .scaleToFill
38 |
39 | controlPanel.addSubview(triggerButton)
40 |
41 | let albumImage = UIImage(named: "album")
42 | let albumBtn = UIButton()
43 | albumBtn.setImage(albumImage, for: .normal)
44 | albumBtn.contentMode = .scaleToFill
45 |
46 | controlPanel.addSubview(albumBtn)
47 |
48 | previewView.snp.makeConstraints{ (make) -> Void in
49 | make.top.equalTo(self.view)
50 | make.width.equalTo(self.view)
51 | make.height.equalTo(self.view)
52 | }
53 |
54 | controlPanel.snp.makeConstraints{ (make) -> Void in
55 | make.bottom.equalTo(self.view)
56 | make.width.equalTo(self.view)
57 | make.height.equalTo(self.view).multipliedBy(0.13)
58 | }
59 |
60 | triggerButton.snp.makeConstraints{ (make) -> Void in
61 | make.width.equalTo(controlPanel.snp.height).multipliedBy(0.9)
62 | make.height.equalTo(triggerButton.snp.width)
63 | make.center.equalTo(controlPanel)
64 | }
65 |
66 | triggerButton.addTarget(self, action: #selector(shutter), for: .touchUpInside)
67 |
68 | albumBtn.snp.makeConstraints { (make) -> Void in
69 | make.centerY.equalTo(triggerButton)
70 | make.left.equalTo(controlPanel).offset(10)
71 | make.width.equalTo(controlPanel.snp.height).multipliedBy(0.9)
72 | make.height.equalTo(albumBtn.snp.width)
73 | }
74 |
75 | albumBtn.addTarget(self, action: #selector(checkPhotos), for: .touchUpInside)
76 |
77 | let captureDevice = AVCaptureDevice.default(for: AVMediaType.video)
78 | do {
79 | let input = try AVCaptureDeviceInput(device: captureDevice!)
80 | captureSession = AVCaptureSession()
81 | captureSession?.addInput(input)
82 | videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
83 | videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
84 | videoPreviewLayer?.frame = view.layer.bounds
85 | previewView.layer.addSublayer(videoPreviewLayer!)
86 | captureSession?.startRunning()
87 |
88 | // Get an instance of ACCapturePhotoOutput class
89 | capturePhotoOutput = AVCapturePhotoOutput()
90 | capturePhotoOutput?.isHighResolutionCaptureEnabled = true
91 | // Set the output on the capture session
92 | captureSession?.addOutput(capturePhotoOutput!)
93 | } catch {
94 | print(error)
95 | }
96 |
97 |
98 | }
99 |
100 | override func viewDidAppear(_ animated: Bool) {
101 | self.shutterBtn?.isEnabled = true
102 | }
103 | @objc func shutter(){
104 | // Make sure capturePhotoOutput is valid
105 | self.shutterBtn?.isEnabled = false
106 | guard let capturePhotoOutput = self.capturePhotoOutput else { return }
107 | // Get an instance of AVCapturePhotoSettings class
108 | let photoSettings = AVCapturePhotoSettings()
109 | // Set photo settings for our need
110 | photoSettings.isAutoStillImageStabilizationEnabled = true
111 | photoSettings.isHighResolutionPhotoEnabled = true
112 | photoSettings.flashMode = .auto
113 | // Call capturePhoto method by passing our photo settings and a
114 | // delegate implementing AVCapturePhotoCaptureDelegate
115 | capturePhotoOutput.capturePhoto(with: photoSettings, delegate: self)
116 |
117 | }
118 |
119 | @objc func checkPhotos(){
120 | print("albumbtn clicked")
121 | let imagePickerController = UIImagePickerController()
122 | imagePickerController.delegate = self
123 | imagePickerController.sourceType = UIImagePickerControllerSourceType.savedPhotosAlbum
124 | imagePickerController.allowsEditing = true
125 | self.present(imagePickerController, animated: true, completion: nil)
126 |
127 | }
128 |
129 | func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
130 | if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
131 | let processViewController = ProcessViewController()
132 | processViewController.faceImg = pickedImage
133 | self.navigationController?.pushViewController(processViewController, animated: true)
134 | }
135 |
136 | dismiss(animated: true, completion: nil)
137 |
138 | }
139 |
140 | override func didReceiveMemoryWarning() {
141 | super.didReceiveMemoryWarning()
142 | // Dispose of any resources that can be recreated.
143 | }
144 |
145 | /*
146 | // MARK: - Navigation
147 |
148 | // In a storyboard-based application, you will often want to do a little preparation before navigation
149 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
150 | // Get the new view controller using segue.destinationViewController.
151 | // Pass the selected object to the new view controller.
152 | }
153 | */
154 |
155 | }
156 |
157 | extension MainViewController: AVCapturePhotoCaptureDelegate {
158 | func photoOutput(_ captureOutput: AVCapturePhotoOutput,
159 | didFinishProcessingPhoto photoSampleBuffer: CMSampleBuffer?,
160 | previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?,
161 | resolvedSettings: AVCaptureResolvedPhotoSettings,
162 | bracketSettings: AVCaptureBracketedStillImageSettings?,
163 | error: Error?) {
164 | // get captured image
165 | // Make sure we get some photo sample buffer
166 | guard error == nil, let photoSampleBuffer = photoSampleBuffer else {
167 | print("Error capturing photo: \(String(describing: error))")
168 | return
169 | }
170 | // Convert photo same buffer to a jpeg image data by using // AVCapturePhotoOutput
171 | guard let imageData =
172 | AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSampleBuffer, previewPhotoSampleBuffer: previewPhotoSampleBuffer) else {
173 | return
174 | }
175 | // Initialise a UIImage with our image data
176 | let capturedImage = UIImage.init(data: imageData , scale: 1.0)
177 | self.faceImg = capturedImage
178 | let processViewController = ProcessViewController()
179 | processViewController.faceImg = capturedImage
180 | self.navigationController?.pushViewController(processViewController, animated: true)
181 | // if let image = capturedImage {
182 | // // Save our captured image to photos album
183 | // UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
184 | // }
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/FaceApp/LoginViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // FaceApp
4 | //
5 | // Created by Arco on 2018/5/6.
6 | // Copyright © 2018 c. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SnapKit
11 | import Alamofire
12 | import Toast_Swift
13 |
14 | let brightBlue = UIColor(red: 0, green: 118/255, blue: 1, alpha: 0.5)
15 | let brightGray = UIColor(red: 155/255, green: 155/255, blue: 155/255, alpha: 1)
16 |
17 | class LoginViewController: UIViewController {
18 |
19 | weak var logoView: UIImageView!
20 | weak var usernameTextView: UITextField!
21 | weak var passwordTextView: UITextField!
22 | weak var captchaTextView: UITextField!
23 | weak var captchaImageView: UIImageView!
24 | weak var maskView: UIVisualEffectView!
25 | var base_name: String!
26 | // let host = "http://218.193.183.249:8888"
27 | // let host = "http://192.168.3.191:8000"
28 | // let host = "http://192.168.1.105:8000"
29 | let host = "http://192.168.3.21:8000"
30 | var username: String? {
31 | get {
32 | return usernameTextView.text?.trim()
33 | }
34 | }
35 | var password: String? {
36 | get {
37 | return passwordTextView.text?.trim()
38 | }
39 | }
40 |
41 | var captcha: String? {
42 | get {
43 | return captchaTextView.text?.trim()
44 | }
45 | }
46 |
47 | override func viewDidLoad() {
48 | super.viewDidLoad()
49 | // Do any additional setup after loading the view, typically from a nib.
50 | NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
51 | NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
52 |
53 | let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard (_:)))
54 | self.view.addGestureRecognizer(tapGesture)
55 |
56 | let loadingBlurEffect = UIBlurEffect(style: .extraLight)
57 | let maskView = UIVisualEffectView(effect: loadingBlurEffect)
58 | self.maskView = maskView
59 |
60 | self.view.backgroundColor = UIColor.white
61 | self.navigationController?.setNavigationBarHidden(true, animated: false)
62 | let mainImageView = UIImageView(image: UIImage(named: "sjtuicon"))
63 | self.logoView = mainImageView
64 |
65 | let usernameTextField = UITextField()
66 | usernameTextField.borderStyle = .roundedRect
67 | usernameTextField.layer.borderWidth = 1
68 | usernameTextField.layer.cornerRadius = 5
69 | usernameTextField.layer.borderColor = brightGray.cgColor
70 | usernameTextField.attributedPlaceholder = NSAttributedString(string: "用户名")
71 |
72 | let passwordTextField = UITextField()
73 | passwordTextField.isSecureTextEntry = true
74 | passwordTextField.borderStyle = .roundedRect
75 | passwordTextField.layer.borderWidth = 1
76 | passwordTextField.layer.cornerRadius = 5
77 | passwordTextField.layer.borderColor = brightGray.cgColor
78 | passwordTextField.attributedPlaceholder = NSAttributedString(string: "密码")
79 |
80 | let submitButton = UIButton()
81 | submitButton.setAttributedTitle(NSAttributedString(string: "提交"), for: .normal)
82 | submitButton.layer.borderWidth = 1
83 | submitButton.layer.cornerRadius = 5
84 | submitButton.layer.borderColor = brightGray.cgColor
85 |
86 | self.usernameTextView = usernameTextField
87 | self.passwordTextView = passwordTextField
88 |
89 | self.view.addSubview(mainImageView)
90 | self.view.addSubview(usernameTextField)
91 | self.view.addSubview(passwordTextField)
92 | self.view.addSubview(submitButton)
93 |
94 | let captchaView = UIView()
95 | self.view.addSubview(captchaView)
96 |
97 | let captchaTextField = UITextField()
98 | captchaTextField.borderStyle = .roundedRect
99 | captchaTextField.layer.borderWidth = 1
100 | captchaTextField.layer.cornerRadius = 5
101 | captchaTextField.layer.borderColor = brightGray.cgColor
102 | captchaTextField.attributedPlaceholder = NSAttributedString(string: "验证码")
103 |
104 | let captchaImageView = UIImageView(image: UIImage(named: "captcha"))
105 |
106 | self.captchaTextView = captchaTextField
107 | self.captchaImageView = captchaImageView
108 |
109 | captchaView.addSubview(captchaTextField)
110 | captchaView.addSubview(captchaImageView)
111 |
112 | mainImageView.snp.makeConstraints{ (make) -> Void in
113 | make.centerX.equalTo(self.view)
114 | make.top.equalTo(self.view).offset(72)
115 | make.width.equalTo(self.view).multipliedBy(0.5)
116 | make.height.equalTo(mainImageView.snp.width)
117 | }
118 |
119 | usernameTextField.snp.makeConstraints{ (make) -> Void in
120 | make.centerX.equalTo(self.view)
121 | make.top.equalTo(mainImageView.snp.bottom).offset(51)
122 | make.width.equalTo(self.view).multipliedBy(0.8)
123 | make.height.equalTo(44)
124 | }
125 |
126 | passwordTextField.snp.makeConstraints{ (make) -> Void in
127 | make.centerX.equalTo(self.view)
128 | make.top.equalTo(usernameTextField.snp.bottom).offset(20)
129 | make.width.equalTo(self.view).multipliedBy(0.8)
130 | make.height.equalTo(44)
131 | }
132 |
133 | captchaView.snp.makeConstraints { (make) -> Void in
134 | make.centerX.equalTo(self.view)
135 | make.width.equalTo(self.view).multipliedBy(0.8)
136 | make.top.equalTo(passwordTextField.snp.bottom).offset(20)
137 | make.height.equalTo(44)
138 | }
139 |
140 | captchaTextField.snp.makeConstraints { (make) -> Void in
141 | make.top.equalTo(captchaView)
142 | make.bottom.equalTo(captchaView)
143 | make.left.equalTo(captchaView)
144 | make.right.equalTo(captchaImageView.snp.left).offset(-10)
145 | }
146 |
147 | captchaImageView.snp.makeConstraints { (make) -> Void in
148 | make.top.equalTo(captchaView)
149 | make.bottom.equalTo(captchaView)
150 | // make.left.equalTo(captchaTextField.snp.right)
151 | make.right.equalTo(captchaView)
152 | make.width.lessThanOrEqualTo(captchaView).multipliedBy(0.5)
153 | }
154 |
155 | submitButton.snp.makeConstraints{ (make) -> Void in
156 | make.centerX.equalTo(self.view)
157 | make.top.equalTo(captchaView.snp.bottom).offset(31)
158 | make.width.equalTo(120)
159 | make.height.equalTo(44)
160 | }
161 |
162 | submitButton.addTarget(self, action: #selector(loginToSjtu), for: .touchUpInside)
163 |
164 | self.view.addSubview(self.maskView)
165 | self.maskView.snp.makeConstraints { (make) in
166 | make.width.equalTo(self.view)
167 | make.height.equalTo(self.view)
168 | }
169 |
170 | self.maskView.isHidden = true
171 | }
172 |
173 | override func viewDidAppear(_ animated: Bool) {
174 | getCaptcha()
175 | }
176 |
177 | @objc func dismissKeyboard (_ sender: UITapGestureRecognizer) {
178 | self.view.endEditing(true)
179 | }
180 |
181 | @objc func keyboardWillShow(notification: NSNotification) {
182 | if self.view.frame.origin.y == 0{
183 | self.view.frame.origin.y -= self.logoView.frame.height + 72
184 | }
185 | }
186 |
187 | @objc func keyboardWillHide(notification: NSNotification) {
188 | if self.view.frame.origin.y != 0{
189 | self.view.frame.origin.y += self.logoView.frame.height + 72
190 | }
191 | }
192 |
193 | @objc func loginToSjtu(sender: UIButton){
194 | login()
195 | }
196 |
197 | func getCaptcha(){
198 | Alamofire.request("\(self.host)/demo/captcha").responseString { response in
199 | if let base_name = response.result.value {
200 | self.base_name = base_name
201 | self.getCaptchaFile(base_name)
202 | }
203 | }
204 | }
205 |
206 | func getCaptchaFile(_ base_name: String){
207 | Alamofire.request("\(self.host)/demo/captcha_file?base_name=\(base_name)").responseData { response in
208 | if let data = response.result.value {
209 | let captcha = UIImage(data: data)
210 | self.captchaImageView.image = captcha
211 | }
212 | }
213 | }
214 |
215 | func login(){
216 | if self.username == "admin" {
217 | let viewController = ViewController()
218 | self.navigationController?.pushViewController(viewController, animated: true)
219 | }
220 | guard self.username != "" else{
221 | self.view.makeToast("请输入您的jAccount帐号")
222 | print("miss username")
223 | return
224 | }
225 | guard self.password != "" else{
226 | self.view.makeToast("请输入您的密码")
227 | print("miss password")
228 | return
229 | }
230 | guard self.captcha != "" else{
231 | self.view.makeToast("请输入验证码")
232 | print("miss captcha")
233 | return
234 | }
235 | guard self.base_name != "" else{
236 | print("miss base_name")
237 | return
238 | }
239 | let parameters: Parameters = [
240 | "username": self.username!,
241 | "password": self.password!,
242 | "base_name": self.base_name!,
243 | "captcha": self.captcha!
244 | ]
245 |
246 | UIView.transition(with: self.maskView, duration: 1, options: .transitionCrossDissolve, animations: { self.maskView.isHidden = false }, completion: nil)
247 | Alamofire.request("\(self.host)/demo/login", method: .post, parameters: parameters).responseString { response in
248 | if let status = response.result.value{
249 | if status == "success"{
250 | print("welcome, \(self.username!)")
251 | let viewController = ViewController()
252 | self.navigationController?.pushViewController(viewController, animated: true)
253 | } else {
254 | self.view.makeToast("请正确填写你的用户名、密码和验证码,注意:密码是区分大小写的")
255 | print("wrong username or password")
256 | self.getCaptcha()
257 | }
258 | UIView.transition(with: self.maskView, duration: 1, options: .transitionCrossDissolve, animations: { self.maskView.isHidden = true }, completion: nil)
259 | }
260 | }
261 | }
262 |
263 | func uploadImg(img: UIImage){
264 |
265 | let orgiFace = UIImagePNGRepresentation(img)!
266 | Alamofire.upload(orgiFace, to: "http://127.0.0.1:8000/demo/upload/").responseData { response in
267 | if let data = response.result.value {
268 | let newFace = UIImage(data: data)
269 | self.logoView.image = newFace
270 | }
271 | }
272 | }
273 |
274 | override func didReceiveMemoryWarning() {
275 | super.didReceiveMemoryWarning()
276 | // Dispose of any resources that can be recreated.
277 | }
278 |
279 |
280 | }
281 |
282 | extension String
283 | {
284 | func trim() -> String
285 | {
286 | return self.trimmingCharacters(in: NSCharacterSet.whitespaces)
287 | }
288 | }
289 |
290 |
--------------------------------------------------------------------------------
/FaceApp/ProcessViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProcessViewController.swift
3 | // FaceApp
4 | //
5 | // Created by Arco on 2018/5/13.
6 | // Copyright © 2018 c. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Alamofire
11 | import SwiftyJSON
12 | import Toast_Swift
13 |
14 | protocol Expression
15 | {
16 | func changeExpression(expression: String)
17 | }
18 |
19 | class ProcessViewController: UIViewController, UIScrollViewDelegate, UICollectionViewDataSource,UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, Expression {
20 |
21 | let api_key = "NeSGlSk3KBueWp-K6Cr18I_-MiuZ4l2u"
22 | let api_secret = "j22uKoQLiIw-5sazuu0lzCkYdNQEn_OQ"
23 |
24 | var scaleRect: CGRect?
25 | var faceImg: UIImage!
26 | var cropFace: UIImage!
27 | var needHide: Bool!
28 | var expression: String?
29 | weak var faceView: UIImageView!
30 | weak var scrollView: UIScrollView!
31 | var collectionView: UICollectionView?
32 | var expressionImages: [String:UIImage] = [:]
33 | var confidences: [String: String] = [:]
34 | var images: [UIImage] = [UIImage(named: "neutral")!, UIImage(named: "happy")!, UIImage(named: "surprise")!,
35 | UIImage(named: "sad")!, UIImage(named: "anger")!]
36 | var captions: [String] = ["neutral", "happy", "surprise", "sad", "anger"]
37 | var maskView: UIVisualEffectView!
38 | var loadingFlag = false
39 | // let host = "http://218.193.183.249:8888"
40 | // let host = "http://192.168.3.191:8000"
41 | // let host = "http://192.168.1.105:8000"
42 | let host = "http://192.168.3.21:8000"
43 |
44 | override func viewDidLoad() {
45 | super.viewDidLoad()
46 |
47 | // Do any additional setup after loading the view.
48 | self.needHide = true
49 |
50 | let loadingBlurEffect = UIBlurEffect(style: .extraLight)
51 | self.maskView = UIVisualEffectView(effect: loadingBlurEffect)
52 |
53 | self.navigationController?.setNavigationBarHidden(false, animated: false)
54 | self.view.backgroundColor = UIColor.white
55 |
56 | if let scale = self.scaleRect {
57 | self.faceImg = self.faceImg.crop(rect: scale)
58 | self.faceImg = self.faceImg.imageWithImage(scaledToSize: CGSize(width: 128, height: 128))
59 | self.faceImg = self.faceImg.fixOrientation()
60 | }
61 | else {
62 | self.faceImg = self.faceImg.imageWithImage(scaledToSize: CGSize(width: 128, height: 128))
63 | self.cropFace = self.faceImg
64 | }
65 | self.expressionImages["neutral"] = self.faceImg
66 |
67 | let imageView = UIImageView(image: faceImg)
68 | imageView.contentMode = .scaleAspectFit
69 | self.faceView = imageView
70 |
71 | let mainView = UIScrollView()
72 | mainView.delegate = self
73 |
74 | self.scrollView = mainView
75 | mainView.minimumZoomScale = 0.5;
76 | mainView.maximumZoomScale = 4.0;
77 | mainView.showsHorizontalScrollIndicator = false
78 | mainView.showsVerticalScrollIndicator = false
79 |
80 | mainView.addSubview(imageView)
81 | self.view.addSubview(mainView)
82 |
83 | let layout = UICollectionViewFlowLayout()
84 | layout.scrollDirection = .horizontal
85 |
86 | let selectionPanel = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
87 | selectionPanel.showsVerticalScrollIndicator = false
88 | selectionPanel.showsVerticalScrollIndicator = false
89 |
90 | self.collectionView = selectionPanel
91 |
92 | selectionPanel.dataSource = self
93 | selectionPanel.delegate = self
94 |
95 | selectionPanel.register(MyCollectionViewCell.self, forCellWithReuseIdentifier: "collectionViewCell")
96 |
97 | self.view.addSubview(selectionPanel)
98 |
99 | mainView.snp.makeConstraints { (make) -> Void in
100 | make.width.equalTo(self.view)
101 | make.height.equalTo(self.view)
102 | make.center.equalTo(self.view)
103 | }
104 |
105 | let imageAspect = self.faceImg.size.height / self.faceImg.size.width
106 |
107 | imageView.snp.makeConstraints { (make) -> Void in
108 | make.width.equalToSuperview().multipliedBy(1.0)
109 | make.height.equalTo(imageView.snp.width).multipliedBy(imageAspect)
110 | }
111 |
112 | selectionPanel.snp.makeConstraints{ (make) -> Void in
113 | make.bottom.equalTo(self.view)
114 | make.left.equalTo(self.view)
115 | make.right.equalTo(self.view)
116 | make.height.equalTo(100)
117 | }
118 |
119 | let selectBtn = UIBarButtonItem(title: "Save", style: .plain, target: self, action: #selector(saveAction))
120 | // let selectBtn = UIBarButtonItem(image: UIImage(named: "more"), style: .plain, target: self, action: #selector(moreAction))
121 |
122 | self.navigationItem.rightBarButtonItem = selectBtn
123 |
124 | selectionPanel.backgroundColor = UIColor.clear
125 |
126 | let blurEffect = UIBlurEffect(style: .extraLight)
127 | let blurEffectView = UIVisualEffectView(effect: blurEffect)
128 | blurEffectView.frame = selectionPanel.frame
129 | selectionPanel.backgroundView = blurEffectView
130 |
131 | mainView.isScrollEnabled = true
132 |
133 | self.view.addSubview(self.maskView)
134 | maskView.snp.makeConstraints { (make) in
135 | make.width.equalTo(self.view)
136 | make.height.equalTo(self.view)
137 | }
138 |
139 | self.maskView.isHidden = true
140 |
141 | }
142 |
143 | func scrollViewDidZoom(_ scrollView: UIScrollView) {
144 | let imageViewSize = self.faceView.frame.size
145 | let scrollViewSize = scrollView.bounds.size
146 | let verticalInset = imageViewSize.height < scrollViewSize.height ? (scrollViewSize.height - imageViewSize.height) / 2 : 5
147 | let horizontalInset = imageViewSize.width < scrollViewSize.width ? (scrollViewSize.width - imageViewSize.width) / 2 : 5
148 | scrollView.contentInset = UIEdgeInsets(top: verticalInset, left: horizontalInset, bottom: verticalInset, right: horizontalInset)
149 | }
150 |
151 | func viewForZooming(in scrollView: UIScrollView) -> UIView? {
152 | return self.faceView
153 | }
154 |
155 | override func didReceiveMemoryWarning() {
156 | super.didReceiveMemoryWarning()
157 | // Dispose of any resources that can be recreated.
158 | }
159 |
160 | func compareFace(faceOrg: UIImage, faceChanged: UIImage, expression: String) {
161 | let imageDataOrg = UIImagePNGRepresentation(faceOrg)!
162 | let encodeStringOrg = imageDataOrg.base64EncodedString(options: Data.Base64EncodingOptions.lineLength64Characters)
163 | let imageDataChanged = UIImagePNGRepresentation(faceChanged)!
164 | let encodeStringChanged = imageDataChanged.base64EncodedString(options: Data.Base64EncodingOptions.lineLength64Characters)
165 | let parameters: Parameters = [
166 | "api_key": api_key,
167 | "api_secret": api_secret,
168 | "image_base64_1": encodeStringOrg,
169 | "image_base64_2": encodeStringChanged
170 | ]
171 | Alamofire.request("https://api-cn.faceplusplus.com/facepp/v3/compare", method: .post, parameters: parameters)
172 | // .responseString { response in
173 | // print(response.result.value)
174 | // }
175 | .responseJSON { response in
176 | if response.data != nil {
177 | do {
178 | let json = try JSON(data: response.data!)
179 | let rawConfidence = json["confidence"].rawString()
180 | if let confidence = rawConfidence{
181 | print(confidence)
182 | // self.confidences[expression] = confidence
183 | // self.view.makeToast("confidence: \(confidence)")
184 | // self.navigationItem.title = confidence
185 | }
186 | }
187 | catch {
188 | print("json praser error")
189 |
190 | }
191 | }
192 | }
193 | }
194 |
195 | @objc func saveAction(){
196 | if let image = self.faceView.image {
197 | // Save our captured image to photos album
198 | UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
199 | print("Saved")
200 | // self.view.makeToast("Saved")
201 | }
202 | }
203 |
204 | override func viewWillDisappear(_ animated: Bool) {
205 | if self.needHide {
206 | self.navigationController?.setNavigationBarHidden(true, animated: true)
207 | }
208 | }
209 |
210 | override func viewWillAppear(_ animated: Bool) {
211 | print("\(expression ?? "no expression") is selected")
212 | }
213 |
214 | func changeExpression(expression: String) {
215 | self.expression = expression
216 | }
217 |
218 | func cropImg(img: UIImage){
219 | let orgiFace = UIImagePNGRepresentation(img)!
220 | Alamofire.upload(orgiFace, to: self.host + "/demo/crop").responseData
221 | { response in
222 | if let data = response.result.value {
223 | let newFace = UIImage(data: data)
224 | self.cropFace = newFace
225 | }
226 | }
227 | }
228 |
229 | func uploadImg(img: UIImage, expression: String){
230 | self.navigationItem.title = expression
231 | self.loadingFlag = true
232 | UIView.transition(with: self.maskView, duration: 1, options: .transitionCrossDissolve, animations: { self.maskView.isHidden = false }, completion: nil)
233 | // self.maskView.isHidden = false
234 | if self.cropFace == nil {
235 | let oldFace = UIImagePNGRepresentation(img)!
236 | Alamofire.upload(oldFace, to: self.host + "/demo/crop_ios").responseData
237 | { response in
238 | if let data = response.result.value {
239 | let newFace = UIImage(data: data)
240 | self.cropFace = newFace
241 | let orgiFace = UIImagePNGRepresentation(self.cropFace)!
242 | Alamofire.upload(orgiFace, to: self.host + "/demo/\(expression)").responseData { response in
243 | if let data = response.result.value {
244 | let newFace = UIImage(data: data)
245 | self.expressionImages[expression] = newFace
246 | self.faceView!.image = newFace
247 | self.loadingFlag = false
248 | // self.maskView.isHidden = true
249 | self.compareFace(faceOrg: self.faceImg, faceChanged: newFace!, expression: expression)
250 | UIView.transition(with: self.maskView, duration: 1, options: .transitionCrossDissolve, animations: { self.maskView.isHidden = true }, completion: nil)
251 | }
252 | }
253 |
254 | }
255 | }
256 | }
257 | else {
258 | let orgiFace = UIImagePNGRepresentation(self.cropFace)!
259 | Alamofire.upload(orgiFace, to: self.host + "/demo/\(expression)").responseData { response in
260 | if let data = response.result.value {
261 | let newFace = UIImage(data: data)
262 | self.expressionImages[expression] = newFace
263 | self.faceView!.image = newFace
264 | self.loadingFlag = false
265 | // self.maskView.isHidden = true
266 | self.compareFace(faceOrg: self.faceImg, faceChanged: newFace!, expression: expression)
267 | UIView.transition(with: self.maskView, duration: 1, options: .transitionCrossDissolve, animations: { self.maskView.isHidden = true }, completion: nil)
268 | }
269 | }
270 | }
271 |
272 | }
273 |
274 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
275 | return self.captions.count
276 | }
277 |
278 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
279 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as! MyCollectionViewCell
280 | // cell.backgroundColor = self.bgColor[indexPath.row]
281 | cell.imageView.image = self.images[indexPath.row]
282 | cell.labelView.text = self.captions[indexPath.row]
283 | return cell
284 | }
285 |
286 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
287 | // return CGSize(width: self.view.frame.size.width * 0.3, height: self.view.frame.size.width * 0.4)
288 | return CGSize(width: 80, height: 75)
289 | }
290 |
291 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
292 | if self.loadingFlag {
293 | return
294 | }
295 | let expression = self.captions[indexPath.row]
296 | if let newFaceImg = self.expressionImages[expression] {
297 | if self.navigationItem.title == expression{
298 | uploadImg(img: self.faceImg, expression: expression)
299 | }
300 | self.faceView.image = newFaceImg
301 | self.navigationItem.title = expression
302 | // self.navigationItem.title = self.confidences[expression]
303 | } else {
304 | uploadImg(img: self.faceImg, expression: expression)
305 | }
306 | }
307 |
308 | /*
309 | // MARK: - Navigation
310 |
311 | // In a storyboard-based application, you will often want to do a little preparation before navigation
312 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
313 | // Get the new view controller using segue.destinationViewController.
314 | // Pass the selected object to the new view controller.
315 | }
316 | */
317 |
318 | }
319 |
320 | extension UIImage {
321 | func fixOrientation() -> UIImage {
322 | if self.imageOrientation == UIImageOrientation.up {
323 | return self
324 | }
325 | UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
326 | self.draw(in: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height))
327 | if let normalizedImage: UIImage = UIGraphicsGetImageFromCurrentImageContext() {
328 | UIGraphicsEndImageContext()
329 | return normalizedImage
330 | } else {
331 | return self
332 | }
333 | }
334 |
335 | func imageWithImage(scaledToSize newSize:CGSize) -> UIImage{
336 | UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0);
337 | self.draw(in: CGRect(origin: CGPoint.zero, size: CGSize(width: newSize.width, height: newSize.height)))
338 | let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
339 | UIGraphicsEndImageContext()
340 | return newImage
341 | }
342 | }
343 |
--------------------------------------------------------------------------------
/FaceApp/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // VisionDetection
4 | //
5 | // Created by Wei Chieh Tseng on 09/06/2017.
6 | // Copyright © 2017 Willjay. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import AVFoundation
11 | import Vision
12 | import SnapKit
13 |
14 | class ViewController: UIViewController {
15 |
16 | // VNRequest: Either Retangles or Landmarks
17 | var faceDetectionRequest: VNRequest!
18 | var previewView: PreviewView!
19 |
20 | var shutterBtn: UIButton?
21 | var capturePhotoOutput: AVCapturePhotoOutput?
22 |
23 | var detectedFace = false
24 | var firstFace: VNFaceObservation?
25 |
26 | override func viewDidLoad() {
27 | super.viewDidLoad()
28 |
29 | self.navigationController?.setNavigationBarHidden(true, animated: false)
30 |
31 | let cameraView = PreviewView()
32 | previewView = cameraView
33 | self.view.addSubview(cameraView)
34 |
35 | cameraView.snp.makeConstraints { (make) in
36 | make.width.equalToSuperview()
37 | make.height.equalToSuperview()
38 | }
39 |
40 | let blurEffect = UIBlurEffect(style: .extraLight)
41 | let controlPanel = UIVisualEffectView(effect: blurEffect)
42 | // let controlPanel = UIView()
43 | self.view.addSubview(controlPanel)
44 |
45 | controlPanel.snp.makeConstraints{ (make) -> Void in
46 | make.bottom.equalTo(self.view)
47 | make.width.equalTo(self.view)
48 | make.height.equalTo(self.view).multipliedBy(0.13)
49 | }
50 |
51 | let btnImage = UIImage(named: "trigger")
52 | let triggerButton = UIButton()
53 | self.shutterBtn = triggerButton
54 | triggerButton.setImage(btnImage, for: .normal)
55 | triggerButton.contentMode = .scaleToFill
56 |
57 | triggerButton.addTarget(self, action: #selector(shutter), for: .touchUpInside)
58 | controlPanel.contentView.addSubview(triggerButton)
59 |
60 | triggerButton.snp.makeConstraints{ (make) -> Void in
61 | make.width.equalTo(controlPanel.snp.height).multipliedBy(0.9)
62 | make.height.equalTo(triggerButton.snp.width)
63 | make.center.equalTo(controlPanel)
64 | }
65 |
66 | let albumImage = UIImage(named: "album")
67 | let albumBtn = UIButton()
68 | albumBtn.setImage(albumImage, for: .normal)
69 | albumBtn.contentMode = .scaleToFill
70 |
71 | albumBtn.addTarget(self, action: #selector(checkPhotos), for: .touchUpInside)
72 | controlPanel.contentView.addSubview(albumBtn)
73 |
74 | albumBtn.snp.makeConstraints { (make) -> Void in
75 | make.centerY.equalTo(triggerButton)
76 | make.left.equalTo(controlPanel).offset(10)
77 | make.width.equalTo(controlPanel.snp.height).multipliedBy(0.9)
78 | make.height.equalTo(albumBtn.snp.width)
79 | }
80 |
81 | // Get an instance of ACCapturePhotoOutput class
82 | capturePhotoOutput = AVCapturePhotoOutput()
83 | capturePhotoOutput?.isHighResolutionCaptureEnabled = true
84 |
85 | // Set the output on the capture session
86 | session.addOutput(capturePhotoOutput!)
87 |
88 | // Set up the video preview view.
89 | previewView.session = session
90 |
91 | // Set up Vision Request
92 | // faceDetectionRequest = VNDetectFaceRectanglesRequest(completionHandler: self.handleFaces) // Default
93 | faceDetectionRequest = VNDetectFaceLandmarksRequest(completionHandler: self.handleFaceLandmarks) // Default
94 | setupVision()
95 |
96 | /*
97 | Check video authorization status. Video access is required and audio
98 | access is optional. If audio access is denied, audio is not recorded
99 | during movie recording.
100 | */
101 | switch AVCaptureDevice.authorizationStatus(for: AVMediaType.video){
102 | case .authorized:
103 | // The user has previously granted access to the camera.
104 | break
105 |
106 | case .notDetermined:
107 | /*
108 | The user has not yet been presented with the option to grant
109 | video access. We suspend the session queue to delay session
110 | setup until the access request has completed.
111 | */
112 | sessionQueue.suspend()
113 | AVCaptureDevice.requestAccess(for: AVMediaType.video, completionHandler: { [unowned self] granted in
114 | if !granted {
115 | self.setupResult = .notAuthorized
116 | }
117 | self.sessionQueue.resume()
118 | })
119 |
120 |
121 | default:
122 | // The user has previously denied access.
123 | setupResult = .notAuthorized
124 | }
125 |
126 | /*
127 | Setup the capture session.
128 | In general it is not safe to mutate an AVCaptureSession or any of its
129 | inputs, outputs, or connections from multiple threads at the same time.
130 |
131 | Why not do all of this on the main queue?
132 | Because AVCaptureSession.startRunning() is a blocking call which can
133 | take a long time. We dispatch session setup to the sessionQueue so
134 | that the main queue isn't blocked, which keeps the UI responsive.
135 | */
136 |
137 | sessionQueue.async { [unowned self] in
138 | self.configureSession()
139 | }
140 |
141 | }
142 |
143 | @objc func shutter(){
144 | // Make sure capturePhotoOutput is valid
145 | if self.detectedFace == false {
146 | return
147 | }
148 | self.shutterBtn?.isEnabled = false
149 | guard let capturePhotoOutput = self.capturePhotoOutput else { return }
150 | // Get an instance of AVCapturePhotoSettings class
151 | let photoSettings = AVCapturePhotoSettings()
152 | // Set photo settings for our need
153 | photoSettings.isAutoStillImageStabilizationEnabled = true
154 | photoSettings.isHighResolutionPhotoEnabled = true
155 | photoSettings.flashMode = .auto
156 | // Call capturePhoto method by passing our photo settings and a
157 | // delegate implementing AVCapturePhotoCaptureDelegate
158 | capturePhotoOutput.capturePhoto(with: photoSettings, delegate: self)
159 |
160 | }
161 |
162 | @objc func checkPhotos(){
163 | print("albumbtn clicked")
164 | let imagePickerController = UIImagePickerController()
165 | imagePickerController.delegate = self
166 | imagePickerController.sourceType = UIImagePickerControllerSourceType.savedPhotosAlbum
167 | imagePickerController.allowsEditing = true
168 | self.present(imagePickerController, animated: true, completion: nil)
169 |
170 | }
171 |
172 | override func viewWillAppear(_ animated: Bool) {
173 | super.viewWillAppear(animated)
174 |
175 | self.shutterBtn?.isEnabled = true
176 |
177 | sessionQueue.async { [unowned self] in
178 | switch self.setupResult {
179 | case .success:
180 | // Only setup observers and start the session running if setup succeeded.
181 | self.addObservers()
182 | self.session.startRunning()
183 | self.isSessionRunning = self.session.isRunning
184 |
185 | case .notAuthorized:
186 | DispatchQueue.main.async { [unowned self] in
187 | let message = NSLocalizedString("AVCamBarcode doesn't have permission to use the camera, please change privacy settings", comment: "Alert message when the user has denied access to the camera")
188 | let alertController = UIAlertController(title: "AppleFaceDetection", message: message, preferredStyle: .alert)
189 | alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .cancel, handler: nil))
190 | alertController.addAction(UIAlertAction(title: NSLocalizedString("Settings", comment: "Alert button to open Settings"), style: .`default`, handler: { action in
191 | UIApplication.shared.open(URL(string: UIApplicationOpenSettingsURLString)!, options: [:], completionHandler: nil)
192 | }))
193 |
194 | self.present(alertController, animated: true, completion: nil)
195 | }
196 |
197 | case .configurationFailed:
198 | DispatchQueue.main.async { [unowned self] in
199 | let message = NSLocalizedString("Unable to capture media", comment: "Alert message when something goes wrong during capture session configuration")
200 | let alertController = UIAlertController(title: "AppleFaceDetection", message: message, preferredStyle: .alert)
201 | alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .cancel, handler: nil))
202 |
203 | self.present(alertController, animated: true, completion: nil)
204 | }
205 | }
206 | }
207 | }
208 |
209 | override func viewWillDisappear(_ animated: Bool) {
210 | sessionQueue.async { [unowned self] in
211 | if self.setupResult == .success {
212 | self.session.stopRunning()
213 | self.isSessionRunning = self.session.isRunning
214 | self.removeObservers()
215 | }
216 | }
217 |
218 | super.viewWillDisappear(animated)
219 | }
220 |
221 |
222 | override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
223 | super.viewWillTransition(to: size, with: coordinator)
224 |
225 | if let videoPreviewLayerConnection = previewView.videoPreviewLayer.connection {
226 | let deviceOrientation = UIDevice.current.orientation
227 | guard let newVideoOrientation = deviceOrientation.videoOrientation, deviceOrientation.isPortrait || deviceOrientation.isLandscape else {
228 | return
229 | }
230 |
231 | videoPreviewLayerConnection.videoOrientation = newVideoOrientation
232 |
233 | }
234 | }
235 |
236 | @objc func UpdateDetectionType(_ sender: UISegmentedControl) {
237 | // use segmentedControl to switch over VNRequest
238 | faceDetectionRequest = sender.selectedSegmentIndex == 0 ? VNDetectFaceRectanglesRequest(completionHandler: handleFaces) : VNDetectFaceLandmarksRequest(completionHandler: handleFaceLandmarks)
239 |
240 | setupVision()
241 | }
242 |
243 |
244 | // MARK: Session Management
245 |
246 | private enum SessionSetupResult {
247 | case success
248 | case notAuthorized
249 | case configurationFailed
250 | }
251 |
252 | private var devicePosition: AVCaptureDevice.Position = .back
253 |
254 | private let session = AVCaptureSession()
255 | private var isSessionRunning = false
256 |
257 | private let sessionQueue = DispatchQueue(label: "session queue", attributes: [], target: nil) // Communicate with the session and other session objects on this queue.
258 |
259 | private var setupResult: SessionSetupResult = .success
260 |
261 | private var videoDeviceInput: AVCaptureDeviceInput!
262 |
263 | private var videoDataOutput: AVCaptureVideoDataOutput!
264 | private var videoDataOutputQueue = DispatchQueue(label: "VideoDataOutputQueue")
265 |
266 | private var requests = [VNRequest]()
267 |
268 | private func configureSession() {
269 | if self.setupResult != .success {
270 | return
271 | }
272 |
273 | session.beginConfiguration()
274 | session.sessionPreset = .high
275 |
276 | // Add video input.
277 | do {
278 | var defaultVideoDevice: AVCaptureDevice?
279 |
280 | // Choose the back dual camera if available, otherwise default to a wide angle camera.
281 | if let dualCameraDevice = AVCaptureDevice.default(.builtInDualCamera, for: AVMediaType.video, position: .back) {
282 | defaultVideoDevice = dualCameraDevice
283 | }
284 |
285 | else if let backCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .back) {
286 | defaultVideoDevice = backCameraDevice
287 | }
288 |
289 | else if let frontCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: AVMediaType.video, position: .front) {
290 | defaultVideoDevice = frontCameraDevice
291 | }
292 |
293 | let videoDeviceInput = try AVCaptureDeviceInput(device: defaultVideoDevice!)
294 |
295 | if session.canAddInput(videoDeviceInput) {
296 | session.addInput(videoDeviceInput)
297 | self.videoDeviceInput = videoDeviceInput
298 | DispatchQueue.main.async {
299 | /*
300 | Why are we dispatching this to the main queue?
301 | Because AVCaptureVideoPreviewLayer is the backing layer for PreviewView and UIView
302 | can only be manipulated on the main thread.
303 | Note: As an exception to the above rule, it is not necessary to serialize video orientation changes
304 | on the AVCaptureVideoPreviewLayer’s connection with other session manipulation.
305 |
306 | Use the status bar orientation as the initial video orientation. Subsequent orientation changes are
307 | handled by CameraViewController.viewWillTransition(to:with:).
308 | */
309 | let statusBarOrientation = UIApplication.shared.statusBarOrientation
310 | var initialVideoOrientation: AVCaptureVideoOrientation = .portrait
311 | if statusBarOrientation != .unknown {
312 | if let videoOrientation = statusBarOrientation.videoOrientation {
313 | initialVideoOrientation = videoOrientation
314 | }
315 | }
316 | self.previewView.videoPreviewLayer.connection!.videoOrientation = initialVideoOrientation
317 | }
318 | }
319 |
320 | else {
321 | print("Could not add video device input to the session")
322 | setupResult = .configurationFailed
323 | session.commitConfiguration()
324 | return
325 | }
326 |
327 | }
328 | catch {
329 | print("Could not create video device input: \(error)")
330 | setupResult = .configurationFailed
331 | session.commitConfiguration()
332 | return
333 | }
334 |
335 | // add output
336 | videoDataOutput = AVCaptureVideoDataOutput()
337 | videoDataOutput.videoSettings = [(kCVPixelBufferPixelFormatTypeKey as String): Int(kCVPixelFormatType_32BGRA)]
338 |
339 |
340 | if session.canAddOutput(videoDataOutput) {
341 | videoDataOutput.alwaysDiscardsLateVideoFrames = true
342 | videoDataOutput.setSampleBufferDelegate(self, queue: videoDataOutputQueue)
343 | session.addOutput(videoDataOutput)
344 | }
345 | else {
346 | print("Could not add metadata output to the session")
347 | setupResult = .configurationFailed
348 | session.commitConfiguration()
349 | return
350 | }
351 |
352 | session.commitConfiguration()
353 |
354 | }
355 |
356 | private func availableSessionPresets() -> [String] {
357 | let allSessionPresets = [AVCaptureSession.Preset.photo,
358 | AVCaptureSession.Preset.low,
359 | AVCaptureSession.Preset.medium,
360 | AVCaptureSession.Preset.high,
361 | AVCaptureSession.Preset.cif352x288,
362 | AVCaptureSession.Preset.vga640x480,
363 | AVCaptureSession.Preset.hd1280x720,
364 | AVCaptureSession.Preset.iFrame960x540,
365 | AVCaptureSession.Preset.iFrame1280x720,
366 | AVCaptureSession.Preset.hd1920x1080,
367 | AVCaptureSession.Preset.hd4K3840x2160]
368 |
369 | var availableSessionPresets = [String]()
370 | for sessionPreset in allSessionPresets {
371 | if session.canSetSessionPreset(sessionPreset) {
372 | availableSessionPresets.append(sessionPreset.rawValue)
373 | }
374 | }
375 |
376 | return availableSessionPresets
377 | }
378 |
379 | func exifOrientationFromDeviceOrientation() -> UInt32 {
380 | enum DeviceOrientation: UInt32 {
381 | case top0ColLeft = 1
382 | case top0ColRight = 2
383 | case bottom0ColRight = 3
384 | case bottom0ColLeft = 4
385 | case left0ColTop = 5
386 | case right0ColTop = 6
387 | case right0ColBottom = 7
388 | case left0ColBottom = 8
389 | }
390 | var exifOrientation: DeviceOrientation
391 |
392 | switch UIDevice.current.orientation {
393 | case .portraitUpsideDown:
394 | exifOrientation = .left0ColBottom
395 | case .landscapeLeft:
396 | exifOrientation = devicePosition == .front ? .bottom0ColRight : .top0ColLeft
397 | case .landscapeRight:
398 | exifOrientation = devicePosition == .front ? .top0ColLeft : .bottom0ColRight
399 | default:
400 | exifOrientation = .right0ColTop
401 | }
402 | return exifOrientation.rawValue
403 | }
404 |
405 |
406 | }
407 |
408 | extension ViewController {
409 | private func addObservers() {
410 | /*
411 | Observe the previewView's regionOfInterest to update the AVCaptureMetadataOutput's
412 | rectOfInterest when the user finishes resizing the region of interest.
413 | */
414 | NotificationCenter.default.addObserver(self, selector: #selector(sessionRuntimeError), name: Notification.Name("AVCaptureSessionRuntimeErrorNotification"), object: session)
415 |
416 | /*
417 | A session can only run when the app is full screen. It will be interrupted
418 | in a multi-app layout, introduced in iOS 9, see also the documentation of
419 | AVCaptureSessionInterruptionReason. Add observers to handle these session
420 | interruptions and show a preview is paused message. See the documentation
421 | of AVCaptureSessionWasInterruptedNotification for other interruption reasons.
422 | */
423 | NotificationCenter.default.addObserver(self, selector: #selector(sessionWasInterrupted), name: Notification.Name("AVCaptureSessionWasInterruptedNotification"), object: session)
424 | NotificationCenter.default.addObserver(self, selector: #selector(sessionInterruptionEnded), name: Notification.Name("AVCaptureSessionInterruptionEndedNotification"), object: session)
425 | }
426 |
427 | private func removeObservers() {
428 | NotificationCenter.default.removeObserver(self)
429 | }
430 |
431 | @objc func sessionRuntimeError(_ notification: Notification) {
432 | guard let errorValue = notification.userInfo?[AVCaptureSessionErrorKey] as? NSError else { return }
433 |
434 | let error = AVError(_nsError: errorValue)
435 | print("Capture session runtime error: \(error)")
436 |
437 | /*
438 | Automatically try to restart the session running if media services were
439 | reset and the last start running succeeded. Otherwise, enable the user
440 | to try to resume the session running.
441 | */
442 | if error.code == .mediaServicesWereReset {
443 | sessionQueue.async { [unowned self] in
444 | if self.isSessionRunning {
445 | self.session.startRunning()
446 | self.isSessionRunning = self.session.isRunning
447 | }
448 | }
449 | }
450 | }
451 |
452 | @objc func sessionWasInterrupted(_ notification: Notification) {
453 | /*
454 | In some scenarios we want to enable the user to resume the session running.
455 | For example, if music playback is initiated via control center while
456 | using AVCamBarcode, then the user can let AVCamBarcode resume
457 | the session running, which will stop music playback. Note that stopping
458 | music playback in control center will not automatically resume the session
459 | running. Also note that it is not always possible to resume, see `resumeInterruptedSession(_:)`.
460 | */
461 | if let userInfoValue = notification.userInfo?[AVCaptureSessionInterruptionReasonKey] as AnyObject?, let reasonIntegerValue = userInfoValue.integerValue, let reason = AVCaptureSession.InterruptionReason(rawValue: reasonIntegerValue) {
462 | print("Capture session was interrupted with reason \(reason)")
463 | }
464 | }
465 |
466 | @objc func sessionInterruptionEnded(_ notification: Notification) {
467 | print("Capture session interruption ended")
468 | }
469 | }
470 |
471 | extension ViewController {
472 | func setupVision() {
473 | self.requests = [faceDetectionRequest]
474 | }
475 |
476 | func handleFaces(request: VNRequest, error: Error?) {
477 | DispatchQueue.main.async {
478 | //perform all the UI updates on the main queue
479 | guard let results = request.results as? [VNFaceObservation] else { return }
480 | self.previewView.removeMask()
481 | if results.count > 0 {
482 | self.detectedFace = true
483 | self.firstFace = results[0]
484 | }
485 | else {
486 | self.detectedFace = false
487 | }
488 | for face in results {
489 | self.previewView.drawFaceboundingBox(face: face)
490 | }
491 | }
492 | }
493 |
494 | func handleFaceLandmarks(request: VNRequest, error: Error?) {
495 | DispatchQueue.main.async {
496 | //perform all the UI updates on the main queue
497 | guard let results = request.results as? [VNFaceObservation] else { return }
498 | self.previewView.removeMask()
499 | if results.count > 0 {
500 | self.detectedFace = true
501 | self.firstFace = results[0]
502 | }
503 | else {
504 | self.detectedFace = false
505 | }
506 | for face in results {
507 | self.previewView.drawFaceWithLandmarks(face: face)
508 | }
509 | }
510 | }
511 |
512 |
513 | }
514 |
515 | extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate{
516 | // MARK: - AVCaptureVideoDataOutputSampleBufferDelegate
517 | func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
518 | guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer),
519 | let exifOrientation = CGImagePropertyOrientation(rawValue: exifOrientationFromDeviceOrientation()) else { return }
520 | var requestOptions: [VNImageOption : Any] = [:]
521 |
522 | if let cameraIntrinsicData = CMGetAttachment(sampleBuffer, kCMSampleBufferAttachmentKey_CameraIntrinsicMatrix, nil) {
523 | requestOptions = [.cameraIntrinsics : cameraIntrinsicData]
524 | }
525 |
526 | let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, orientation: exifOrientation, options: requestOptions)
527 |
528 | do {
529 | try imageRequestHandler.perform(requests)
530 | }
531 |
532 | catch {
533 | print(error)
534 | }
535 |
536 | }
537 |
538 | }
539 |
540 | extension ViewController: AVCapturePhotoCaptureDelegate {
541 | func photoOutput(_ captureOutput: AVCapturePhotoOutput,
542 | didFinishProcessingPhoto photoSampleBuffer: CMSampleBuffer?,
543 | previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?,
544 | resolvedSettings: AVCaptureResolvedPhotoSettings,
545 | bracketSettings: AVCaptureBracketedStillImageSettings?,
546 | error: Error?) {
547 | // get captured image
548 | // Make sure we get some photo sample buffer
549 | guard error == nil, let photoSampleBuffer = photoSampleBuffer else {
550 | print("Error capturing photo: \(String(describing: error))")
551 | return
552 | }
553 | // Convert photo same buffer to a jpeg image data by using // AVCapturePhotoOutput
554 | guard let imageData =
555 | AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSampleBuffer, previewPhotoSampleBuffer: previewPhotoSampleBuffer) else {
556 | return
557 | }
558 | // Initialise a UIImage with our image data
559 | let capturedImage = UIImage.init(data: imageData , scale: 1.0)
560 |
561 | let processViewController = ProcessViewController()
562 | processViewController.faceImg = capturedImage
563 |
564 | if let face = self.firstFace {
565 | let oldbounds = face.boundingBox
566 | let transform = CGAffineTransform(scaleX: 1, y: -1).translatedBy(x: 0, y: -(capturedImage?.size.height)!)
567 | let translate = CGAffineTransform.identity.scaledBy(x: (capturedImage?.size.width)!, y: (capturedImage?.size.height)!)
568 | let facebounds = face.boundingBox.applying(translate).applying(transform)
569 |
570 | processViewController.scaleRect = facebounds
571 | }
572 | self.navigationController?.pushViewController(processViewController, animated: true)
573 | // if let image = capturedImage {
574 | // // Save our captured image to photos album
575 | // UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
576 | // }
577 | }
578 | }
579 |
580 | extension ViewController: UINavigationControllerDelegate, UIImagePickerControllerDelegate {
581 | func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
582 | if let pickedImage = info[UIImagePickerControllerEditedImage] as? UIImage {
583 | let processViewController = ProcessViewController()
584 | processViewController.faceImg = pickedImage
585 | self.navigationController?.pushViewController(processViewController, animated: true)
586 | }
587 |
588 | dismiss(animated: true, completion: nil)
589 | }
590 | }
591 |
592 | extension UIDeviceOrientation {
593 | var videoOrientation: AVCaptureVideoOrientation? {
594 | switch self {
595 | case .portrait: return .portrait
596 | case .portraitUpsideDown: return .portraitUpsideDown
597 | case .landscapeLeft: return .landscapeRight
598 | case .landscapeRight: return .landscapeLeft
599 | default: return nil
600 | }
601 | }
602 | }
603 |
604 | extension UIInterfaceOrientation {
605 | var videoOrientation: AVCaptureVideoOrientation? {
606 | switch self {
607 | case .portrait: return .portrait
608 | case .portraitUpsideDown: return .portraitUpsideDown
609 | case .landscapeLeft: return .landscapeLeft
610 | case .landscapeRight: return .landscapeRight
611 | default: return nil
612 | }
613 | }
614 | }
615 |
616 | extension UIImage {
617 | func crop( rect: CGRect) -> UIImage {
618 | let imageWidth = self.size.width
619 | // let imageHeight = self.size.height
620 |
621 | var rect = rect
622 | var x = rect.origin.x * self.scale
623 | var y = rect.origin.y * self.scale
624 | var width = rect.size.width * self.scale
625 | var height = rect.size.height * self.scale
626 |
627 | var offsetX: CGFloat = 0
628 | var offsetY: CGFloat = 0
629 | let scaleOffsetX: CGFloat = 0.2
630 | let scaleOffsetY: CGFloat = 0.2
631 |
632 | if x - width * scaleOffsetX > 0 {
633 | offsetX = width * scaleOffsetX
634 | x = x - offsetX
635 | } else {
636 | offsetX = 0
637 | x = 0
638 | }
639 | if y - height * scaleOffsetY > 0 {
640 | offsetY = height * scaleOffsetY
641 | y = y - offsetY
642 | } else {
643 | offsetY = 0
644 | x = 0
645 | }
646 |
647 | width += offsetX * 2
648 | height += offsetY * 2
649 |
650 | let tempY = y
651 | y = imageWidth - (x + width)
652 | x = tempY
653 |
654 | rect.origin.x = x
655 | rect.origin.y = y
656 | rect.size.width = height
657 | rect.size.height = width
658 | // rect.size.width = width
659 | // rect.size.height = width
660 |
661 |
662 | let imageRef = self.cgImage!.cropping(to: rect)
663 | let image = UIImage(cgImage: imageRef!, scale: self.scale, orientation: self.imageOrientation)
664 | return image
665 | }
666 | }
667 |
--------------------------------------------------------------------------------
/FaceApp.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 2D334CC020AFE0C300EF7AB5 /* PreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D334CBE20AFE0C200EF7AB5 /* PreviewView.swift */; };
11 | 2D334CC120AFE0C300EF7AB5 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D334CBF20AFE0C300EF7AB5 /* ViewController.swift */; };
12 | 2D5590F220A96FF500AA2209 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5590F120A96FF500AA2209 /* Assets.xcassets */; };
13 | 2D59248B20AA9A1A00AB941D /* HorizontalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D59248A20AA9A1A00AB941D /* HorizontalViewController.swift */; };
14 | 2D59248D20AAA25F00AB941D /* MyCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D59248C20AAA25F00AB941D /* MyCollectionViewCell.swift */; };
15 | 2D60423120A842D40078C039 /* ProcessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D60423020A842D40078C039 /* ProcessViewController.swift */; };
16 | 2D60423520A86EE70078C039 /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D60423420A86EE70078C039 /* TableViewController.swift */; };
17 | 2D77DC14209F56670022BAAB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D77DC13209F56670022BAAB /* AppDelegate.swift */; };
18 | 2D77DC16209F56670022BAAB /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D77DC15209F56670022BAAB /* LoginViewController.swift */; };
19 | 2D77DC19209F56670022BAAB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2D77DC17209F56670022BAAB /* Main.storyboard */; };
20 | 2D77DC1E209F56680022BAAB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2D77DC1C209F56680022BAAB /* LaunchScreen.storyboard */; };
21 | 2D77DC29209F56680022BAAB /* FaceAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D77DC28209F56680022BAAB /* FaceAppTests.swift */; };
22 | 2D77DC34209F56680022BAAB /* FaceAppUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D77DC33209F56680022BAAB /* FaceAppUITests.swift */; };
23 | 2D77DC46209F61060022BAAB /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D77DC45209F61060022BAAB /* MainViewController.swift */; };
24 | 8BE044749F4F3EDD6923F5ED /* Pods_FaceApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 412112E8EFF4697B1C640E36 /* Pods_FaceApp.framework */; };
25 | /* End PBXBuildFile section */
26 |
27 | /* Begin PBXContainerItemProxy section */
28 | 2D77DC25209F56680022BAAB /* PBXContainerItemProxy */ = {
29 | isa = PBXContainerItemProxy;
30 | containerPortal = 2D77DC08209F56670022BAAB /* Project object */;
31 | proxyType = 1;
32 | remoteGlobalIDString = 2D77DC0F209F56670022BAAB;
33 | remoteInfo = FaceApp;
34 | };
35 | 2D77DC30209F56680022BAAB /* PBXContainerItemProxy */ = {
36 | isa = PBXContainerItemProxy;
37 | containerPortal = 2D77DC08209F56670022BAAB /* Project object */;
38 | proxyType = 1;
39 | remoteGlobalIDString = 2D77DC0F209F56670022BAAB;
40 | remoteInfo = FaceApp;
41 | };
42 | /* End PBXContainerItemProxy section */
43 |
44 | /* Begin PBXFileReference section */
45 | 1532E256725320EADEF5CF9A /* Pods-FaceApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FaceApp.debug.xcconfig"; path = "Pods/Target Support Files/Pods-FaceApp/Pods-FaceApp.debug.xcconfig"; sourceTree = ""; };
46 | 2D334CBE20AFE0C200EF7AB5 /* PreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewView.swift; sourceTree = ""; };
47 | 2D334CBF20AFE0C300EF7AB5 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
48 | 2D5590F120A96FF500AA2209 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
49 | 2D59248A20AA9A1A00AB941D /* HorizontalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HorizontalViewController.swift; sourceTree = ""; };
50 | 2D59248C20AAA25F00AB941D /* MyCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCollectionViewCell.swift; sourceTree = ""; };
51 | 2D60423020A842D40078C039 /* ProcessViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessViewController.swift; sourceTree = ""; };
52 | 2D60423420A86EE70078C039 /* TableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; };
53 | 2D77DC10209F56670022BAAB /* FaceApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FaceApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
54 | 2D77DC13209F56670022BAAB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
55 | 2D77DC15209F56670022BAAB /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; };
56 | 2D77DC18209F56670022BAAB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
57 | 2D77DC1D209F56680022BAAB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
58 | 2D77DC1F209F56680022BAAB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
59 | 2D77DC24209F56680022BAAB /* FaceAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FaceAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
60 | 2D77DC28209F56680022BAAB /* FaceAppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaceAppTests.swift; sourceTree = ""; };
61 | 2D77DC2A209F56680022BAAB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
62 | 2D77DC2F209F56680022BAAB /* FaceAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FaceAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
63 | 2D77DC33209F56680022BAAB /* FaceAppUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaceAppUITests.swift; sourceTree = ""; };
64 | 2D77DC35209F56680022BAAB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
65 | 2D77DC45209F61060022BAAB /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; };
66 | 412112E8EFF4697B1C640E36 /* Pods_FaceApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FaceApp.framework; sourceTree = BUILT_PRODUCTS_DIR; };
67 | BCA8B798D961322553D4C940 /* Pods-FaceApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FaceApp.release.xcconfig"; path = "Pods/Target Support Files/Pods-FaceApp/Pods-FaceApp.release.xcconfig"; sourceTree = ""; };
68 | /* End PBXFileReference section */
69 |
70 | /* Begin PBXFrameworksBuildPhase section */
71 | 2D77DC0D209F56670022BAAB /* Frameworks */ = {
72 | isa = PBXFrameworksBuildPhase;
73 | buildActionMask = 2147483647;
74 | files = (
75 | 8BE044749F4F3EDD6923F5ED /* Pods_FaceApp.framework in Frameworks */,
76 | );
77 | runOnlyForDeploymentPostprocessing = 0;
78 | };
79 | 2D77DC21209F56680022BAAB /* Frameworks */ = {
80 | isa = PBXFrameworksBuildPhase;
81 | buildActionMask = 2147483647;
82 | files = (
83 | );
84 | runOnlyForDeploymentPostprocessing = 0;
85 | };
86 | 2D77DC2C209F56680022BAAB /* Frameworks */ = {
87 | isa = PBXFrameworksBuildPhase;
88 | buildActionMask = 2147483647;
89 | files = (
90 | );
91 | runOnlyForDeploymentPostprocessing = 0;
92 | };
93 | /* End PBXFrameworksBuildPhase section */
94 |
95 | /* Begin PBXGroup section */
96 | 2D77DC07209F56670022BAAB = {
97 | isa = PBXGroup;
98 | children = (
99 | 2D77DC12209F56670022BAAB /* FaceApp */,
100 | 2D77DC27209F56680022BAAB /* FaceAppTests */,
101 | 2D77DC32209F56680022BAAB /* FaceAppUITests */,
102 | 2D77DC11209F56670022BAAB /* Products */,
103 | D841439D0BFFE56230927713 /* Pods */,
104 | 3E824493AE7089A50E12F62F /* Frameworks */,
105 | );
106 | sourceTree = "";
107 | };
108 | 2D77DC11209F56670022BAAB /* Products */ = {
109 | isa = PBXGroup;
110 | children = (
111 | 2D77DC10209F56670022BAAB /* FaceApp.app */,
112 | 2D77DC24209F56680022BAAB /* FaceAppTests.xctest */,
113 | 2D77DC2F209F56680022BAAB /* FaceAppUITests.xctest */,
114 | );
115 | name = Products;
116 | sourceTree = "";
117 | };
118 | 2D77DC12209F56670022BAAB /* FaceApp */ = {
119 | isa = PBXGroup;
120 | children = (
121 | 2D77DC13209F56670022BAAB /* AppDelegate.swift */,
122 | 2D77DC15209F56670022BAAB /* LoginViewController.swift */,
123 | 2D77DC45209F61060022BAAB /* MainViewController.swift */,
124 | 2D334CBE20AFE0C200EF7AB5 /* PreviewView.swift */,
125 | 2D334CBF20AFE0C300EF7AB5 /* ViewController.swift */,
126 | 2D60423020A842D40078C039 /* ProcessViewController.swift */,
127 | 2D77DC17209F56670022BAAB /* Main.storyboard */,
128 | 2D77DC1C209F56680022BAAB /* LaunchScreen.storyboard */,
129 | 2D5590F120A96FF500AA2209 /* Assets.xcassets */,
130 | 2D77DC1F209F56680022BAAB /* Info.plist */,
131 | 2D60423420A86EE70078C039 /* TableViewController.swift */,
132 | 2D59248A20AA9A1A00AB941D /* HorizontalViewController.swift */,
133 | 2D59248C20AAA25F00AB941D /* MyCollectionViewCell.swift */,
134 | );
135 | path = FaceApp;
136 | sourceTree = "";
137 | };
138 | 2D77DC27209F56680022BAAB /* FaceAppTests */ = {
139 | isa = PBXGroup;
140 | children = (
141 | 2D77DC28209F56680022BAAB /* FaceAppTests.swift */,
142 | 2D77DC2A209F56680022BAAB /* Info.plist */,
143 | );
144 | path = FaceAppTests;
145 | sourceTree = "";
146 | };
147 | 2D77DC32209F56680022BAAB /* FaceAppUITests */ = {
148 | isa = PBXGroup;
149 | children = (
150 | 2D77DC33209F56680022BAAB /* FaceAppUITests.swift */,
151 | 2D77DC35209F56680022BAAB /* Info.plist */,
152 | );
153 | path = FaceAppUITests;
154 | sourceTree = "";
155 | };
156 | 3E824493AE7089A50E12F62F /* Frameworks */ = {
157 | isa = PBXGroup;
158 | children = (
159 | 412112E8EFF4697B1C640E36 /* Pods_FaceApp.framework */,
160 | );
161 | name = Frameworks;
162 | sourceTree = "";
163 | };
164 | D841439D0BFFE56230927713 /* Pods */ = {
165 | isa = PBXGroup;
166 | children = (
167 | 1532E256725320EADEF5CF9A /* Pods-FaceApp.debug.xcconfig */,
168 | BCA8B798D961322553D4C940 /* Pods-FaceApp.release.xcconfig */,
169 | );
170 | name = Pods;
171 | sourceTree = "";
172 | };
173 | /* End PBXGroup section */
174 |
175 | /* Begin PBXNativeTarget section */
176 | 2D77DC0F209F56670022BAAB /* FaceApp */ = {
177 | isa = PBXNativeTarget;
178 | buildConfigurationList = 2D77DC38209F56680022BAAB /* Build configuration list for PBXNativeTarget "FaceApp" */;
179 | buildPhases = (
180 | 99794F7F2FD9FB6C8E9FB5A7 /* [CP] Check Pods Manifest.lock */,
181 | 2D77DC0C209F56670022BAAB /* Sources */,
182 | 2D77DC0D209F56670022BAAB /* Frameworks */,
183 | 2D77DC0E209F56670022BAAB /* Resources */,
184 | C522967D07D1AB147F4AD02E /* [CP] Embed Pods Frameworks */,
185 | );
186 | buildRules = (
187 | );
188 | dependencies = (
189 | );
190 | name = FaceApp;
191 | productName = FaceApp;
192 | productReference = 2D77DC10209F56670022BAAB /* FaceApp.app */;
193 | productType = "com.apple.product-type.application";
194 | };
195 | 2D77DC23209F56680022BAAB /* FaceAppTests */ = {
196 | isa = PBXNativeTarget;
197 | buildConfigurationList = 2D77DC3B209F56680022BAAB /* Build configuration list for PBXNativeTarget "FaceAppTests" */;
198 | buildPhases = (
199 | 2D77DC20209F56680022BAAB /* Sources */,
200 | 2D77DC21209F56680022BAAB /* Frameworks */,
201 | 2D77DC22209F56680022BAAB /* Resources */,
202 | );
203 | buildRules = (
204 | );
205 | dependencies = (
206 | 2D77DC26209F56680022BAAB /* PBXTargetDependency */,
207 | );
208 | name = FaceAppTests;
209 | productName = FaceAppTests;
210 | productReference = 2D77DC24209F56680022BAAB /* FaceAppTests.xctest */;
211 | productType = "com.apple.product-type.bundle.unit-test";
212 | };
213 | 2D77DC2E209F56680022BAAB /* FaceAppUITests */ = {
214 | isa = PBXNativeTarget;
215 | buildConfigurationList = 2D77DC3E209F56680022BAAB /* Build configuration list for PBXNativeTarget "FaceAppUITests" */;
216 | buildPhases = (
217 | 2D77DC2B209F56680022BAAB /* Sources */,
218 | 2D77DC2C209F56680022BAAB /* Frameworks */,
219 | 2D77DC2D209F56680022BAAB /* Resources */,
220 | );
221 | buildRules = (
222 | );
223 | dependencies = (
224 | 2D77DC31209F56680022BAAB /* PBXTargetDependency */,
225 | );
226 | name = FaceAppUITests;
227 | productName = FaceAppUITests;
228 | productReference = 2D77DC2F209F56680022BAAB /* FaceAppUITests.xctest */;
229 | productType = "com.apple.product-type.bundle.ui-testing";
230 | };
231 | /* End PBXNativeTarget section */
232 |
233 | /* Begin PBXProject section */
234 | 2D77DC08209F56670022BAAB /* Project object */ = {
235 | isa = PBXProject;
236 | attributes = {
237 | LastSwiftUpdateCheck = 0930;
238 | LastUpgradeCheck = 0930;
239 | ORGANIZATIONNAME = c;
240 | TargetAttributes = {
241 | 2D77DC0F209F56670022BAAB = {
242 | CreatedOnToolsVersion = 9.3;
243 | };
244 | 2D77DC23209F56680022BAAB = {
245 | CreatedOnToolsVersion = 9.3;
246 | TestTargetID = 2D77DC0F209F56670022BAAB;
247 | };
248 | 2D77DC2E209F56680022BAAB = {
249 | CreatedOnToolsVersion = 9.3;
250 | TestTargetID = 2D77DC0F209F56670022BAAB;
251 | };
252 | };
253 | };
254 | buildConfigurationList = 2D77DC0B209F56670022BAAB /* Build configuration list for PBXProject "FaceApp" */;
255 | compatibilityVersion = "Xcode 9.3";
256 | developmentRegion = en;
257 | hasScannedForEncodings = 0;
258 | knownRegions = (
259 | en,
260 | Base,
261 | );
262 | mainGroup = 2D77DC07209F56670022BAAB;
263 | productRefGroup = 2D77DC11209F56670022BAAB /* Products */;
264 | projectDirPath = "";
265 | projectRoot = "";
266 | targets = (
267 | 2D77DC0F209F56670022BAAB /* FaceApp */,
268 | 2D77DC23209F56680022BAAB /* FaceAppTests */,
269 | 2D77DC2E209F56680022BAAB /* FaceAppUITests */,
270 | );
271 | };
272 | /* End PBXProject section */
273 |
274 | /* Begin PBXResourcesBuildPhase section */
275 | 2D77DC0E209F56670022BAAB /* Resources */ = {
276 | isa = PBXResourcesBuildPhase;
277 | buildActionMask = 2147483647;
278 | files = (
279 | 2D77DC1E209F56680022BAAB /* LaunchScreen.storyboard in Resources */,
280 | 2D5590F220A96FF500AA2209 /* Assets.xcassets in Resources */,
281 | 2D77DC19209F56670022BAAB /* Main.storyboard in Resources */,
282 | );
283 | runOnlyForDeploymentPostprocessing = 0;
284 | };
285 | 2D77DC22209F56680022BAAB /* Resources */ = {
286 | isa = PBXResourcesBuildPhase;
287 | buildActionMask = 2147483647;
288 | files = (
289 | );
290 | runOnlyForDeploymentPostprocessing = 0;
291 | };
292 | 2D77DC2D209F56680022BAAB /* Resources */ = {
293 | isa = PBXResourcesBuildPhase;
294 | buildActionMask = 2147483647;
295 | files = (
296 | );
297 | runOnlyForDeploymentPostprocessing = 0;
298 | };
299 | /* End PBXResourcesBuildPhase section */
300 |
301 | /* Begin PBXShellScriptBuildPhase section */
302 | 99794F7F2FD9FB6C8E9FB5A7 /* [CP] Check Pods Manifest.lock */ = {
303 | isa = PBXShellScriptBuildPhase;
304 | buildActionMask = 2147483647;
305 | files = (
306 | );
307 | inputPaths = (
308 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
309 | "${PODS_ROOT}/Manifest.lock",
310 | );
311 | name = "[CP] Check Pods Manifest.lock";
312 | outputPaths = (
313 | "$(DERIVED_FILE_DIR)/Pods-FaceApp-checkManifestLockResult.txt",
314 | );
315 | runOnlyForDeploymentPostprocessing = 0;
316 | shellPath = /bin/sh;
317 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
318 | showEnvVarsInLog = 0;
319 | };
320 | C522967D07D1AB147F4AD02E /* [CP] Embed Pods Frameworks */ = {
321 | isa = PBXShellScriptBuildPhase;
322 | buildActionMask = 2147483647;
323 | files = (
324 | );
325 | inputPaths = (
326 | "${SRCROOT}/Pods/Target Support Files/Pods-FaceApp/Pods-FaceApp-frameworks.sh",
327 | "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
328 | "${BUILT_PRODUCTS_DIR}/SnapKit/SnapKit.framework",
329 | "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework",
330 | "${BUILT_PRODUCTS_DIR}/Toast-Swift/Toast_Swift.framework",
331 | );
332 | name = "[CP] Embed Pods Frameworks";
333 | outputPaths = (
334 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
335 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SnapKit.framework",
336 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework",
337 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Toast_Swift.framework",
338 | );
339 | runOnlyForDeploymentPostprocessing = 0;
340 | shellPath = /bin/sh;
341 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FaceApp/Pods-FaceApp-frameworks.sh\"\n";
342 | showEnvVarsInLog = 0;
343 | };
344 | /* End PBXShellScriptBuildPhase section */
345 |
346 | /* Begin PBXSourcesBuildPhase section */
347 | 2D77DC0C209F56670022BAAB /* Sources */ = {
348 | isa = PBXSourcesBuildPhase;
349 | buildActionMask = 2147483647;
350 | files = (
351 | 2D60423120A842D40078C039 /* ProcessViewController.swift in Sources */,
352 | 2D334CC020AFE0C300EF7AB5 /* PreviewView.swift in Sources */,
353 | 2D59248D20AAA25F00AB941D /* MyCollectionViewCell.swift in Sources */,
354 | 2D334CC120AFE0C300EF7AB5 /* ViewController.swift in Sources */,
355 | 2D77DC46209F61060022BAAB /* MainViewController.swift in Sources */,
356 | 2D77DC16209F56670022BAAB /* LoginViewController.swift in Sources */,
357 | 2D60423520A86EE70078C039 /* TableViewController.swift in Sources */,
358 | 2D59248B20AA9A1A00AB941D /* HorizontalViewController.swift in Sources */,
359 | 2D77DC14209F56670022BAAB /* AppDelegate.swift in Sources */,
360 | );
361 | runOnlyForDeploymentPostprocessing = 0;
362 | };
363 | 2D77DC20209F56680022BAAB /* Sources */ = {
364 | isa = PBXSourcesBuildPhase;
365 | buildActionMask = 2147483647;
366 | files = (
367 | 2D77DC29209F56680022BAAB /* FaceAppTests.swift in Sources */,
368 | );
369 | runOnlyForDeploymentPostprocessing = 0;
370 | };
371 | 2D77DC2B209F56680022BAAB /* Sources */ = {
372 | isa = PBXSourcesBuildPhase;
373 | buildActionMask = 2147483647;
374 | files = (
375 | 2D77DC34209F56680022BAAB /* FaceAppUITests.swift in Sources */,
376 | );
377 | runOnlyForDeploymentPostprocessing = 0;
378 | };
379 | /* End PBXSourcesBuildPhase section */
380 |
381 | /* Begin PBXTargetDependency section */
382 | 2D77DC26209F56680022BAAB /* PBXTargetDependency */ = {
383 | isa = PBXTargetDependency;
384 | target = 2D77DC0F209F56670022BAAB /* FaceApp */;
385 | targetProxy = 2D77DC25209F56680022BAAB /* PBXContainerItemProxy */;
386 | };
387 | 2D77DC31209F56680022BAAB /* PBXTargetDependency */ = {
388 | isa = PBXTargetDependency;
389 | target = 2D77DC0F209F56670022BAAB /* FaceApp */;
390 | targetProxy = 2D77DC30209F56680022BAAB /* PBXContainerItemProxy */;
391 | };
392 | /* End PBXTargetDependency section */
393 |
394 | /* Begin PBXVariantGroup section */
395 | 2D77DC17209F56670022BAAB /* Main.storyboard */ = {
396 | isa = PBXVariantGroup;
397 | children = (
398 | 2D77DC18209F56670022BAAB /* Base */,
399 | );
400 | name = Main.storyboard;
401 | sourceTree = "";
402 | };
403 | 2D77DC1C209F56680022BAAB /* LaunchScreen.storyboard */ = {
404 | isa = PBXVariantGroup;
405 | children = (
406 | 2D77DC1D209F56680022BAAB /* Base */,
407 | );
408 | name = LaunchScreen.storyboard;
409 | sourceTree = "";
410 | };
411 | /* End PBXVariantGroup section */
412 |
413 | /* Begin XCBuildConfiguration section */
414 | 2D77DC36209F56680022BAAB /* Debug */ = {
415 | isa = XCBuildConfiguration;
416 | buildSettings = {
417 | ALWAYS_SEARCH_USER_PATHS = NO;
418 | CLANG_ANALYZER_NONNULL = YES;
419 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
420 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
421 | CLANG_CXX_LIBRARY = "libc++";
422 | CLANG_ENABLE_MODULES = YES;
423 | CLANG_ENABLE_OBJC_ARC = YES;
424 | CLANG_ENABLE_OBJC_WEAK = YES;
425 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
426 | CLANG_WARN_BOOL_CONVERSION = YES;
427 | CLANG_WARN_COMMA = YES;
428 | CLANG_WARN_CONSTANT_CONVERSION = YES;
429 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
430 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
431 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
432 | CLANG_WARN_EMPTY_BODY = YES;
433 | CLANG_WARN_ENUM_CONVERSION = YES;
434 | CLANG_WARN_INFINITE_RECURSION = YES;
435 | CLANG_WARN_INT_CONVERSION = YES;
436 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
437 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
438 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
439 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
440 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
441 | CLANG_WARN_STRICT_PROTOTYPES = YES;
442 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
443 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
444 | CLANG_WARN_UNREACHABLE_CODE = YES;
445 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
446 | CODE_SIGN_IDENTITY = "iPhone Developer";
447 | COPY_PHASE_STRIP = NO;
448 | DEBUG_INFORMATION_FORMAT = dwarf;
449 | ENABLE_STRICT_OBJC_MSGSEND = YES;
450 | ENABLE_TESTABILITY = YES;
451 | GCC_C_LANGUAGE_STANDARD = gnu11;
452 | GCC_DYNAMIC_NO_PIC = NO;
453 | GCC_NO_COMMON_BLOCKS = YES;
454 | GCC_OPTIMIZATION_LEVEL = 0;
455 | GCC_PREPROCESSOR_DEFINITIONS = (
456 | "DEBUG=1",
457 | "$(inherited)",
458 | );
459 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
460 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
461 | GCC_WARN_UNDECLARED_SELECTOR = YES;
462 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
463 | GCC_WARN_UNUSED_FUNCTION = YES;
464 | GCC_WARN_UNUSED_VARIABLE = YES;
465 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
466 | MTL_ENABLE_DEBUG_INFO = YES;
467 | ONLY_ACTIVE_ARCH = YES;
468 | SDKROOT = iphoneos;
469 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
470 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
471 | };
472 | name = Debug;
473 | };
474 | 2D77DC37209F56680022BAAB /* Release */ = {
475 | isa = XCBuildConfiguration;
476 | buildSettings = {
477 | ALWAYS_SEARCH_USER_PATHS = NO;
478 | CLANG_ANALYZER_NONNULL = YES;
479 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
480 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
481 | CLANG_CXX_LIBRARY = "libc++";
482 | CLANG_ENABLE_MODULES = YES;
483 | CLANG_ENABLE_OBJC_ARC = YES;
484 | CLANG_ENABLE_OBJC_WEAK = YES;
485 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
486 | CLANG_WARN_BOOL_CONVERSION = YES;
487 | CLANG_WARN_COMMA = YES;
488 | CLANG_WARN_CONSTANT_CONVERSION = YES;
489 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
490 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
491 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
492 | CLANG_WARN_EMPTY_BODY = YES;
493 | CLANG_WARN_ENUM_CONVERSION = YES;
494 | CLANG_WARN_INFINITE_RECURSION = YES;
495 | CLANG_WARN_INT_CONVERSION = YES;
496 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
497 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
498 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
499 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
500 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
501 | CLANG_WARN_STRICT_PROTOTYPES = YES;
502 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
503 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
504 | CLANG_WARN_UNREACHABLE_CODE = YES;
505 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
506 | CODE_SIGN_IDENTITY = "iPhone Developer";
507 | COPY_PHASE_STRIP = NO;
508 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
509 | ENABLE_NS_ASSERTIONS = NO;
510 | ENABLE_STRICT_OBJC_MSGSEND = YES;
511 | GCC_C_LANGUAGE_STANDARD = gnu11;
512 | GCC_NO_COMMON_BLOCKS = YES;
513 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
514 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
515 | GCC_WARN_UNDECLARED_SELECTOR = YES;
516 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
517 | GCC_WARN_UNUSED_FUNCTION = YES;
518 | GCC_WARN_UNUSED_VARIABLE = YES;
519 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
520 | MTL_ENABLE_DEBUG_INFO = NO;
521 | SDKROOT = iphoneos;
522 | SWIFT_COMPILATION_MODE = wholemodule;
523 | SWIFT_OPTIMIZATION_LEVEL = "-O";
524 | VALIDATE_PRODUCT = YES;
525 | };
526 | name = Release;
527 | };
528 | 2D77DC39209F56680022BAAB /* Debug */ = {
529 | isa = XCBuildConfiguration;
530 | baseConfigurationReference = 1532E256725320EADEF5CF9A /* Pods-FaceApp.debug.xcconfig */;
531 | buildSettings = {
532 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
533 | CODE_SIGN_STYLE = Automatic;
534 | DEVELOPMENT_TEAM = GY384L655Q;
535 | INFOPLIST_FILE = FaceApp/Info.plist;
536 | IPHONEOS_DEPLOYMENT_TARGET = 11.3;
537 | LD_RUNPATH_SEARCH_PATHS = (
538 | "$(inherited)",
539 | "@executable_path/Frameworks",
540 | );
541 | PRODUCT_BUNDLE_IDENTIFIER = c.arcobaleno.FaceApp;
542 | PRODUCT_NAME = "$(TARGET_NAME)";
543 | SWIFT_VERSION = 4.0;
544 | TARGETED_DEVICE_FAMILY = "1,2";
545 | };
546 | name = Debug;
547 | };
548 | 2D77DC3A209F56680022BAAB /* Release */ = {
549 | isa = XCBuildConfiguration;
550 | baseConfigurationReference = BCA8B798D961322553D4C940 /* Pods-FaceApp.release.xcconfig */;
551 | buildSettings = {
552 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
553 | CODE_SIGN_STYLE = Automatic;
554 | DEVELOPMENT_TEAM = GY384L655Q;
555 | INFOPLIST_FILE = FaceApp/Info.plist;
556 | IPHONEOS_DEPLOYMENT_TARGET = 11.3;
557 | LD_RUNPATH_SEARCH_PATHS = (
558 | "$(inherited)",
559 | "@executable_path/Frameworks",
560 | );
561 | PRODUCT_BUNDLE_IDENTIFIER = c.arcobaleno.FaceApp;
562 | PRODUCT_NAME = "$(TARGET_NAME)";
563 | SWIFT_VERSION = 4.0;
564 | TARGETED_DEVICE_FAMILY = "1,2";
565 | };
566 | name = Release;
567 | };
568 | 2D77DC3C209F56680022BAAB /* Debug */ = {
569 | isa = XCBuildConfiguration;
570 | buildSettings = {
571 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
572 | BUNDLE_LOADER = "$(TEST_HOST)";
573 | CODE_SIGN_STYLE = Automatic;
574 | INFOPLIST_FILE = FaceAppTests/Info.plist;
575 | LD_RUNPATH_SEARCH_PATHS = (
576 | "$(inherited)",
577 | "@executable_path/Frameworks",
578 | "@loader_path/Frameworks",
579 | );
580 | PRODUCT_BUNDLE_IDENTIFIER = c.arcobaleno.FaceAppTests;
581 | PRODUCT_NAME = "$(TARGET_NAME)";
582 | SWIFT_VERSION = 4.0;
583 | TARGETED_DEVICE_FAMILY = "1,2";
584 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FaceApp.app/FaceApp";
585 | };
586 | name = Debug;
587 | };
588 | 2D77DC3D209F56680022BAAB /* Release */ = {
589 | isa = XCBuildConfiguration;
590 | buildSettings = {
591 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
592 | BUNDLE_LOADER = "$(TEST_HOST)";
593 | CODE_SIGN_STYLE = Automatic;
594 | INFOPLIST_FILE = FaceAppTests/Info.plist;
595 | LD_RUNPATH_SEARCH_PATHS = (
596 | "$(inherited)",
597 | "@executable_path/Frameworks",
598 | "@loader_path/Frameworks",
599 | );
600 | PRODUCT_BUNDLE_IDENTIFIER = c.arcobaleno.FaceAppTests;
601 | PRODUCT_NAME = "$(TARGET_NAME)";
602 | SWIFT_VERSION = 4.0;
603 | TARGETED_DEVICE_FAMILY = "1,2";
604 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FaceApp.app/FaceApp";
605 | };
606 | name = Release;
607 | };
608 | 2D77DC3F209F56680022BAAB /* Debug */ = {
609 | isa = XCBuildConfiguration;
610 | buildSettings = {
611 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
612 | CODE_SIGN_STYLE = Automatic;
613 | INFOPLIST_FILE = FaceAppUITests/Info.plist;
614 | LD_RUNPATH_SEARCH_PATHS = (
615 | "$(inherited)",
616 | "@executable_path/Frameworks",
617 | "@loader_path/Frameworks",
618 | );
619 | PRODUCT_BUNDLE_IDENTIFIER = c.arcobaleno.FaceAppUITests;
620 | PRODUCT_NAME = "$(TARGET_NAME)";
621 | SWIFT_VERSION = 4.0;
622 | TARGETED_DEVICE_FAMILY = "1,2";
623 | TEST_TARGET_NAME = FaceApp;
624 | };
625 | name = Debug;
626 | };
627 | 2D77DC40209F56680022BAAB /* Release */ = {
628 | isa = XCBuildConfiguration;
629 | buildSettings = {
630 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
631 | CODE_SIGN_STYLE = Automatic;
632 | INFOPLIST_FILE = FaceAppUITests/Info.plist;
633 | LD_RUNPATH_SEARCH_PATHS = (
634 | "$(inherited)",
635 | "@executable_path/Frameworks",
636 | "@loader_path/Frameworks",
637 | );
638 | PRODUCT_BUNDLE_IDENTIFIER = c.arcobaleno.FaceAppUITests;
639 | PRODUCT_NAME = "$(TARGET_NAME)";
640 | SWIFT_VERSION = 4.0;
641 | TARGETED_DEVICE_FAMILY = "1,2";
642 | TEST_TARGET_NAME = FaceApp;
643 | };
644 | name = Release;
645 | };
646 | /* End XCBuildConfiguration section */
647 |
648 | /* Begin XCConfigurationList section */
649 | 2D77DC0B209F56670022BAAB /* Build configuration list for PBXProject "FaceApp" */ = {
650 | isa = XCConfigurationList;
651 | buildConfigurations = (
652 | 2D77DC36209F56680022BAAB /* Debug */,
653 | 2D77DC37209F56680022BAAB /* Release */,
654 | );
655 | defaultConfigurationIsVisible = 0;
656 | defaultConfigurationName = Release;
657 | };
658 | 2D77DC38209F56680022BAAB /* Build configuration list for PBXNativeTarget "FaceApp" */ = {
659 | isa = XCConfigurationList;
660 | buildConfigurations = (
661 | 2D77DC39209F56680022BAAB /* Debug */,
662 | 2D77DC3A209F56680022BAAB /* Release */,
663 | );
664 | defaultConfigurationIsVisible = 0;
665 | defaultConfigurationName = Release;
666 | };
667 | 2D77DC3B209F56680022BAAB /* Build configuration list for PBXNativeTarget "FaceAppTests" */ = {
668 | isa = XCConfigurationList;
669 | buildConfigurations = (
670 | 2D77DC3C209F56680022BAAB /* Debug */,
671 | 2D77DC3D209F56680022BAAB /* Release */,
672 | );
673 | defaultConfigurationIsVisible = 0;
674 | defaultConfigurationName = Release;
675 | };
676 | 2D77DC3E209F56680022BAAB /* Build configuration list for PBXNativeTarget "FaceAppUITests" */ = {
677 | isa = XCConfigurationList;
678 | buildConfigurations = (
679 | 2D77DC3F209F56680022BAAB /* Debug */,
680 | 2D77DC40209F56680022BAAB /* Release */,
681 | );
682 | defaultConfigurationIsVisible = 0;
683 | defaultConfigurationName = Release;
684 | };
685 | /* End XCConfigurationList section */
686 | };
687 | rootObject = 2D77DC08209F56670022BAAB /* Project object */;
688 | }
689 |
--------------------------------------------------------------------------------