Skip to content

Commit

Permalink
Handle images layout, refactor view model to make it less complex.
Browse files Browse the repository at this point in the history
  • Loading branch information
Cesar Vargas Casaseca committed May 3, 2020
1 parent 11bbd9c commit 01cb8c8
Show file tree
Hide file tree
Showing 21 changed files with 283 additions and 65 deletions.
12 changes: 12 additions & 0 deletions Warhol/SampleApp/Assets.xcassets/leftEye.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Untitled.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions Warhol/SampleApp/Assets.xcassets/nose.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "pngwave (3).png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions Warhol/SampleApp/Assets.xcassets/rightEye.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "rightEye.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 16 additions & 5 deletions Warhol/SampleApp/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15510"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16086"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
Expand All @@ -17,25 +17,36 @@
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uDo-iR-BPQ">
<rect key="frame" x="16" y="60" width="343" height="30"/>
<state key="normal" title="Open Camera"/>
<state key="normal" title="Open Camera With Client Drawing"/>
<connections>
<action selector="openCameraButtonWasPressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="d01-2r-kda"/>
<action selector="openCameraWithClientDrawingButtonWasPressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="d01-2r-kda"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LVo-vQ-Ima">
<rect key="frame" x="16" y="104" width="343" height="30"/>
<state key="normal" title="Open Camera With Images on Features"/>
<connections>
<action selector="openCameraWithImageFeatures:" destination="BYZ-38-t0r" eventType="touchUpInside" id="vlp-Ta-9Ja"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Lif-Qj-t4h">
<rect key="frame" x="16" y="148" width="343" height="30"/>
<state key="normal" title="Open Image"/>
<connections>
<action selector="openImageButtonWasPressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="LMk-oy-MoV"/>
<action selector="openImageButtonWasPressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="nOM-6a-Hys"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="LVo-vQ-Ima" firstAttribute="width" secondItem="uDo-iR-BPQ" secondAttribute="width" id="5VQ-kT-uDa"/>
<constraint firstItem="Lif-Qj-t4h" firstAttribute="trailing" secondItem="LVo-vQ-Ima" secondAttribute="trailing" id="AAt-Le-BXB"/>
<constraint firstItem="Lif-Qj-t4h" firstAttribute="top" secondItem="LVo-vQ-Ima" secondAttribute="bottom" constant="14" id="N9F-FT-O76"/>
<constraint firstItem="Lif-Qj-t4h" firstAttribute="leading" secondItem="LVo-vQ-Ima" secondAttribute="leading" id="Qpm-ms-l9g"/>
<constraint firstItem="LVo-vQ-Ima" firstAttribute="trailing" secondItem="uDo-iR-BPQ" secondAttribute="trailing" id="c1L-K9-dti"/>
<constraint firstItem="uDo-iR-BPQ" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="d7p-9t-Nq5"/>
<constraint firstItem="LVo-vQ-Ima" firstAttribute="leading" secondItem="uDo-iR-BPQ" secondAttribute="leading" id="gZK-Vx-gKp"/>
<constraint firstItem="Lif-Qj-t4h" firstAttribute="height" secondItem="LVo-vQ-Ima" secondAttribute="height" id="kt5-uA-jxc"/>
<constraint firstItem="LVo-vQ-Ima" firstAttribute="height" secondItem="uDo-iR-BPQ" secondAttribute="height" id="mZI-LG-rSx"/>
<constraint firstItem="LVo-vQ-Ima" firstAttribute="top" secondItem="uDo-iR-BPQ" secondAttribute="bottom" constant="14" id="myE-ab-Hf9"/>
<constraint firstItem="uDo-iR-BPQ" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" constant="60" id="paf-yI-Iyn"/>
Expand Down
15 changes: 2 additions & 13 deletions Warhol/SampleApp/View/CameraFrontView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,10 @@ final class FaceView: UIView, CameraFrontView {
context.strokePath()
UIColor.white.setStroke()

draw(landmark: viewModel.leftEye, closePath: true, in: context)
draw(landmark: viewModel.rightEye, closePath: true, in: context)

draw(landmark: viewModel.leftEyebrow, closePath: false, in: context)
draw(landmark: viewModel.rightEyebrow, closePath: false, in: context)

draw(landmark: viewModel.nose, closePath: false, in: context)

draw(landmark: viewModel.outerLips, closePath: true, in: context)
draw(landmark: viewModel.innerLips, closePath: true, in: context)

draw(landmark: viewModel.faceContour, closePath: false, in: context)
Array(viewModel.landmarks.values).forEach {self.draw(landmark: $0, closePath: true, in: context) }
}

private func draw(landmark: FaceLandmark, closePath: Bool, in context: CGContext) {
private func draw(landmark: FaceLandmarkPerimeter, closePath: Bool, in context: CGContext) {
guard !landmark.isEmpty else {
return
}
Expand Down
14 changes: 2 additions & 12 deletions Warhol/SampleApp/ViewController/ImageViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,10 @@ final class ImageViewController: UIViewController {
context.strokePath()
UIColor.blue.setStroke()

self.draw(landmark: viewModel.leftEye, closePath: true, in: context)
self.draw(landmark: viewModel.rightEye, closePath: true, in: context)

self.draw(landmark: viewModel.leftEyebrow, closePath: false, in: context)
self.draw(landmark: viewModel.rightEyebrow, closePath: false, in: context)

self.draw(landmark: viewModel.nose, closePath: false, in: context)

self.draw(landmark: viewModel.outerLips, closePath: true, in: context)
self.draw(landmark: viewModel.innerLips, closePath: true, in: context)
self.draw(landmark: viewModel.faceContour, closePath: false, in: context)
Array(viewModel.landmarks.values).forEach { self.draw(landmark: $0, closePath: true, in: context) }
}

private func draw(landmark: FaceLandmark, closePath: Bool, in context: CGContext) {
private func draw(landmark: FaceLandmarkPerimeter, closePath: Bool, in context: CGContext) {
guard !landmark.isEmpty else {
return
}
Expand Down
20 changes: 19 additions & 1 deletion Warhol/SampleApp/ViewController/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,30 @@ import UIKit
import Warhol

class ViewController: UIViewController {
@IBAction func openCameraButtonWasPressed(_ sender: Any) {
@IBAction func openCameraWithClientDrawingButtonWasPressed(_ sender: Any) {
let cameraViewController = CameraFaceDetectionViewController()

let faceView = FaceView()
faceView.backgroundColor = .clear
cameraViewController.cameraFrontView = faceView

present(cameraViewController, animated: true, completion: nil)
}

@IBAction func openCameraWithImageFeatures(_ sender: Any) {
let cameraViewController = CameraFaceDetectionViewController()

let leftEye = ImageLayout(image: UIImage(named: "leftEye")!, sizeRatio: SizeRatio(width: 1, height: 4))
let rightEye = ImageLayout(image: UIImage(named: "rightEye")!, sizeRatio: SizeRatio(width: 1, height: 4))
let nose = ImageLayout(image: UIImage(named: "nose")!)

let faceLayout = FaceLayout(landmarkLayouts: [.leftEye: leftEye,
.rightEye: rightEye,
.nose: nose])
cameraViewController.faceLayout = faceLayout

present(cameraViewController, animated: true, completion: nil)

}

@IBAction func openImageButtonWasPressed(_ sender: Any) {
Expand Down
32 changes: 28 additions & 4 deletions Warhol/Warhol.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

/* Begin PBXBuildFile section */
372C82FE24390777005115DB /* CoreGraphicsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372C82FD24390777005115DB /* CoreGraphicsExtensions.swift */; };
373EE90E245C2DDB004E5E6F /* FaceImagesLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373EE90D245C2DDB004E5E6F /* FaceImagesLayout.swift */; };
373EE910245C393D004E5E6F /* FaceLayoutCameraFrontView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373EE90F245C393D004E5E6F /* FaceLayoutCameraFrontView.swift */; };
373EE912245D8287004E5E6F /* CGRect+SizeFactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373EE911245D8287004E5E6F /* CGRect+SizeFactor.swift */; };
373EE914245EDD9F004E5E6F /* FaceLandmarkPerimeter+Rect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373EE913245EDD9F004E5E6F /* FaceLandmarkPerimeter+Rect.swift */; };
377F6FFF242CB14C0043BA3C /* ImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377F6FFE242CB14C0043BA3C /* ImageViewController.swift */; };
377F70132430FF770043BA3C /* FaceDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377F70122430FF770043BA3C /* FaceDetector.swift */; };
377F7026243395060043BA3C /* UIView+Constraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377F7025243395060043BA3C /* UIView+Constraints.swift */; };
Expand Down Expand Up @@ -53,6 +57,10 @@

/* Begin PBXFileReference section */
372C82FD24390777005115DB /* CoreGraphicsExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreGraphicsExtensions.swift; sourceTree = "<group>"; };
373EE90D245C2DDB004E5E6F /* FaceImagesLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaceImagesLayout.swift; sourceTree = "<group>"; };
373EE90F245C393D004E5E6F /* FaceLayoutCameraFrontView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaceLayoutCameraFrontView.swift; sourceTree = "<group>"; };
373EE911245D8287004E5E6F /* CGRect+SizeFactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGRect+SizeFactor.swift"; sourceTree = "<group>"; };
373EE913245EDD9F004E5E6F /* FaceLandmarkPerimeter+Rect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FaceLandmarkPerimeter+Rect.swift"; sourceTree = "<group>"; };
377F6FFE242CB14C0043BA3C /* ImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewController.swift; sourceTree = "<group>"; };
377F70122430FF770043BA3C /* FaceDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaceDetector.swift; sourceTree = "<group>"; };
377F7025243395060043BA3C /* UIView+Constraints.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Constraints.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -93,6 +101,15 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
373EE915245EDF7B004E5E6F /* FaceImagesLayout */ = {
isa = PBXGroup;
children = (
373EE90F245C393D004E5E6F /* FaceLayoutCameraFrontView.swift */,
373EE90D245C2DDB004E5E6F /* FaceImagesLayout.swift */,
);
path = FaceImagesLayout;
sourceTree = "<group>";
};
377F702724339D680043BA3C /* ViewController */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -132,6 +149,7 @@
37FE0DD324280B2200B95287 /* Warhol */ = {
isa = PBXGroup;
children = (
373EE915245EDF7B004E5E6F /* FaceImagesLayout */,
37FE0DFB2428C0E500B95287 /* Extensions */,
37FE0DD424280B2200B95287 /* Warhol.h */,
37FE0DD524280B2200B95287 /* Info.plist */,
Expand All @@ -149,6 +167,8 @@
372C82FD24390777005115DB /* CoreGraphicsExtensions.swift */,
37FE0DFE2428C50500B95287 /* AVCaptureVideoPreviewLayer+Landmarks.swift */,
377F7025243395060043BA3C /* UIView+Constraints.swift */,
373EE911245D8287004E5E6F /* CGRect+SizeFactor.swift */,
373EE913245EDD9F004E5E6F /* FaceLandmarkPerimeter+Rect.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -323,9 +343,13 @@
37FE0DF724280BF800B95287 /* CameraFaceDetectionViewController.swift in Sources */,
37FE0DFF2428C50500B95287 /* AVCaptureVideoPreviewLayer+Landmarks.swift in Sources */,
377F7026243395060043BA3C /* UIView+Constraints.swift in Sources */,
373EE912245D8287004E5E6F /* CGRect+SizeFactor.swift in Sources */,
37FE0DFA2428BD5900B95287 /* FaceViewModel.swift in Sources */,
373EE90E245C2DDB004E5E6F /* FaceImagesLayout.swift in Sources */,
373EE914245EDD9F004E5E6F /* FaceLandmarkPerimeter+Rect.swift in Sources */,
372C82FE24390777005115DB /* CoreGraphicsExtensions.swift in Sources */,
377F70132430FF770043BA3C /* FaceDetector.swift in Sources */,
373EE910245C393D004E5E6F /* FaceLayoutCameraFrontView.swift in Sources */,
37FE0DF524280BC100B95287 /* Warhol.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -554,14 +578,14 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = EKP9ZV782L;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = SampleApp/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.casaseca.Warhol.SampleApp;
PRODUCT_BUNDLE_IDENTIFIER = de.axelspringer.weltmobil.warhol;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
Expand All @@ -574,14 +598,14 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = EKP9ZV782L;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = SampleApp/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.casaseca.Warhol.SampleApp;
PRODUCT_BUNDLE_IDENTIFIER = de.axelspringer.weltmobil.warhol;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
<key>SampleApp.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
<integer>0</integer>
</dict>
<key>Warhol.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
<integer>1</integer>
</dict>
</dict>
</dict>
Expand Down
11 changes: 11 additions & 0 deletions Warhol/Warhol/CameraFaceDetectionViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ public class CameraFaceDetectionViewController: UIViewController {
}
}

/// Use this property to pass your layout assigning an image to face features
public var faceLayout: FaceLayout? {
willSet {
guard let faceLayout = newValue else {
return
}

cameraFrontView = FaceLayoutCameraFrontView(layout: faceLayout)
}
}

private func addCameraFrontView(_ frontView: UIView) {
view.addSubview(frontView)
view.adjustSubviewToEdges(subView: frontView)
Expand Down
21 changes: 21 additions & 0 deletions Warhol/Warhol/Extensions/CGRect+SizeFactor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// CGRect+SizeFactor.swift
// Warhol
//
// Created by Cesar Vargas on 02.05.20.
// Copyright © 2020 Cesar Vargas. All rights reserved.
//

import Foundation
import CoreGraphics

extension CGRect {
func increaseRect(byPercentageWidth percentageWidth: CGFloat,
byPercentageHeight percentageHeight: CGFloat) -> CGRect {
let startWidth = width
let startHeight = height
let adjustmentWidth = (startWidth * percentageWidth) / 2.0
let adjustmentHeight = (startHeight * percentageHeight) / 2.0
return insetBy(dx: -adjustmentWidth, dy: -adjustmentHeight)
}
}
24 changes: 24 additions & 0 deletions Warhol/Warhol/Extensions/FaceLandmarkPerimeter+Rect.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// FaceLandmarkPerimeter+Rect.swift
// Warhol
//
// Created by Cesar Vargas on 03.05.20.
// Copyright © 2020 Cesar Vargas. All rights reserved.
//

import Foundation
import CoreGraphics

extension FaceLandmarkPerimeter {
func rect() -> CGRect {
let leftMost: CGFloat = self.reduce(CGFloat.greatestFiniteMagnitude) { Swift.min($0, $1.x) }
let upMost = reduce(CGFloat.greatestFiniteMagnitude) { Swift.min($0, $1.y) }
let rightMost = reduce(0) { Swift.max($0, $1.x) }
let downMost = reduce(0) { Swift.max($0, $1.y) }

let originalWidth = rightMost - leftMost
let originalHeight = downMost - upMost

return CGRect(x: leftMost, y: upMost, width: originalWidth, height: originalHeight)
}
}
4 changes: 2 additions & 2 deletions Warhol/Warhol/FaceDetector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class FaceDetector {
return
}

let landmarkMaker: (VNFaceLandmarkRegion2D?) -> FaceLandmark? = { faceLandmarkRegion in
let landmarkMaker: (VNFaceLandmarkRegion2D?) -> FaceLandmarkPerimeter? = { faceLandmarkRegion in
guard let points = faceLandmarkRegion?.normalizedPoints else {
return nil
}
Expand Down Expand Up @@ -74,7 +74,7 @@ class FaceDetector {
height: rectHeight)
}

let landmarkMaker: (VNFaceLandmarkRegion2D?) -> FaceLandmark? = { faceLandmarkRegion in
let landmarkMaker: (VNFaceLandmarkRegion2D?) -> FaceLandmarkPerimeter? = { faceLandmarkRegion in
faceLandmarkRegion?.pointsInImage(imageSize: image.size)
}

Expand Down
Loading

0 comments on commit 01cb8c8

Please sign in to comment.