Skip to content

Commit

Permalink
feat: add no light challenge implementation (#127)
Browse files Browse the repository at this point in the history
* feat: add no light challenge implementation

* update package.swift for CI build

* Fix unit tests

* Address review comments
  • Loading branch information
thisisabhash authored Apr 18, 2024
1 parent 2909c12 commit cc166eb
Show file tree
Hide file tree
Showing 21 changed files with 301 additions and 113 deletions.
2 changes: 0 additions & 2 deletions HostApp/HostApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,6 @@
Base,
);
mainGroup = 9070FF97285112B4009867D5;
packageReferences = (
);
productRefGroup = 9070FFA1285112B4009867D5 /* Products */;
projectDirPath = "";
projectRoot = "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/aws-amplify/amplify-swift",
"state" : {
"revision" : "7d9640a5b7416f02f03463972dc2c6d87b295545",
"version" : "2.27.2"
"branch" : "feat/no-light-support",
"revision" : "7c1fa2f7a766208f5af69ca8dce5fd02e6de4db6"
}
},
{
Expand All @@ -18,15 +18,6 @@
"version" : "1.1.1"
}
},
{
"identity" : "aws-appsync-realtime-client-ios",
"kind" : "remoteSourceControl",
"location" : "https://github.com/aws-amplify/aws-appsync-realtime-client-ios.git",
"state" : {
"revision" : "a08684c5004e2049c29f57a5938beae9695a1ef7",
"version" : "3.1.2"
}
},
{
"identity" : "aws-crt-swift",
"kind" : "remoteSourceControl",
Expand Down Expand Up @@ -63,15 +54,6 @@
"version" : "0.13.2"
}
},
{
"identity" : "starscream",
"kind" : "remoteSourceControl",
"location" : "https://github.com/daltoniam/Starscream",
"state" : {
"revision" : "df8d82047f6654d8e4b655d1b1525c64e1059d21",
"version" : "4.0.4"
}
},
{
"identity" : "swift-log",
"kind" : "remoteSourceControl",
Expand Down
3 changes: 2 additions & 1 deletion HostApp/HostApp/Views/ExampleLivenessView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ struct ExampleLivenessView: View {
case .liveness:
FaceLivenessDetectorView(
sessionID: viewModel.sessionID,
region: "us-east-1",
// TODO: Change before merging to main
region: "us-west-2",
isPresented: Binding(
get: { viewModel.presentationState == .liveness },
set: { _ in }
Expand Down
22 changes: 2 additions & 20 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/aws-amplify/amplify-swift",
"state" : {
"revision" : "7d9640a5b7416f02f03463972dc2c6d87b295545",
"version" : "2.27.2"
"branch" : "feat/no-light-support",
"revision" : "7c1fa2f7a766208f5af69ca8dce5fd02e6de4db6"
}
},
{
Expand All @@ -18,15 +18,6 @@
"version" : "1.1.1"
}
},
{
"identity" : "aws-appsync-realtime-client-ios",
"kind" : "remoteSourceControl",
"location" : "https://github.com/aws-amplify/aws-appsync-realtime-client-ios.git",
"state" : {
"revision" : "a08684c5004e2049c29f57a5938beae9695a1ef7",
"version" : "3.1.2"
}
},
{
"identity" : "aws-crt-swift",
"kind" : "remoteSourceControl",
Expand Down Expand Up @@ -63,15 +54,6 @@
"version" : "0.13.2"
}
},
{
"identity" : "starscream",
"kind" : "remoteSourceControl",
"location" : "https://github.com/daltoniam/Starscream",
"state" : {
"revision" : "df8d82047f6654d8e4b655d1b1525c64e1059d21",
"version" : "4.0.4"
}
},
{
"identity" : "swift-log",
"kind" : "remoteSourceControl",
Expand Down
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ let package = Package(
targets: ["FaceLiveness"]),
],
dependencies: [
.package(url: "https://github.com/aws-amplify/amplify-swift", exact: "2.27.2")
// TODO: Change this before merge to main
.package(url: "https://github.com/aws-amplify/amplify-swift", branch: "feat/no-light-support")
],
targets: [
.target(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

import Foundation
@_spi(PredictionsFaceLiveness) import AWSPredictionsPlugin

struct DetectedFace {
var boundingBox: CGRect
Expand All @@ -19,7 +20,8 @@ struct DetectedFace {

let confidence: Float

func boundingBoxFromLandmarks(ovalRect: CGRect) -> CGRect {
func boundingBoxFromLandmarks(ovalRect: CGRect,
ovalMatchChallenge: FaceLivenessSession.OvalMatchChallenge) -> CGRect {
let alpha = 2.0
let gamma = 1.8
let ow = (alpha * pupilDistance + gamma * faceHeight) / 2
Expand All @@ -34,7 +36,7 @@ struct DetectedFace {
}

let faceWidth = ow
let faceHeight = 1.618 * faceWidth
let faceHeight = ovalMatchChallenge.oval.heightWidthRatio * faceWidth
let faceBoxBottom = boundingBox.maxY
let faceBoxTop = faceBoxBottom - faceHeight
let faceBoxLeft = min(cx - ow / 2, rightEar.x)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Accelerate
import CoreGraphics
import CoreImage
import VideoToolbox
@_spi(PredictionsFaceLiveness) import AWSPredictionsPlugin

enum FaceDetectorShortRange {}

Expand All @@ -33,11 +34,16 @@ extension FaceDetectorShortRange {
)
}

weak var faceDetectionSessionConfiguration: FaceDetectionSessionConfigurationWrapper?
weak var detectionResultHandler: FaceDetectionResultHandler?

func setResultHandler(detectionResultHandler: FaceDetectionResultHandler) {
self.detectionResultHandler = detectionResultHandler
}

func setFaceDetectionSessionConfigurationWrapper(configuration: FaceDetectionSessionConfigurationWrapper) {
self.faceDetectionSessionConfiguration = configuration
}

func detectFaces(from buffer: CVPixelBuffer) {
let faces = prediction(for: buffer)
Expand Down Expand Up @@ -105,10 +111,17 @@ extension FaceDetectorShortRange {
count: confidenceScoresCapacity
)
)

let blazeFaceDetectionThreshold: Float
if let sessionConfiguration = faceDetectionSessionConfiguration?.sessionConfiguration {
blazeFaceDetectionThreshold = Float(sessionConfiguration.ovalMatchChallenge.faceDetectionThreshold)
} else {
blazeFaceDetectionThreshold = confidenceScoreThreshold
}

var passingConfidenceScoresIndices = confidenceScores
.enumerated()
.filter { $0.element >= confidenceScoreThreshold }
.filter { $0.element >= blazeFaceDetectionThreshold}
.sorted(by: {
$0.element > $1.element
})
Expand Down
5 changes: 5 additions & 0 deletions Sources/FaceLiveness/FaceDetection/FaceDetector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

import AVFoundation
@_spi(PredictionsFaceLiveness) import AWSPredictionsPlugin

protocol FaceDetector {
func detectFaces(from buffer: CVPixelBuffer)
Expand All @@ -16,6 +17,10 @@ protocol FaceDetectionResultHandler: AnyObject {
func process(newResult: FaceDetectionResult)
}

protocol FaceDetectionSessionConfigurationWrapper: AnyObject {
var sessionConfiguration: FaceLivenessSession.SessionConfiguration? { get }
}

enum FaceDetectionResult {
case noFace
case singleFace(DetectedFace)
Expand Down
46 changes: 31 additions & 15 deletions Sources/FaceLiveness/Views/GetReadyPage/GetReadyPageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,49 @@
//

import SwiftUI
@_spi(PredictionsFaceLiveness) import AWSPredictionsPlugin

struct GetReadyPageView: View {
let beginCheckButtonDisabled: Bool
let onBegin: () -> Void

let challenge: Challenge

init(
onBegin: @escaping () -> Void,
beginCheckButtonDisabled: Bool = false
beginCheckButtonDisabled: Bool = false,
challenge: Challenge
) {
self.onBegin = onBegin
self.beginCheckButtonDisabled = beginCheckButtonDisabled
self.challenge = challenge
}

var body: some View {
VStack {
ZStack {
CameraPreviewView()
VStack {
WarningBox(
titleText: LocalizedStrings.get_ready_photosensitivity_title,
bodyText: LocalizedStrings.get_ready_photosensitivity_description,
popoverContent: { photosensitivityWarningPopoverContent }
)
.accessibilityElement(children: .combine)
Text(LocalizedStrings.preview_center_your_face_text)
.font(.title)
.multilineTextAlignment(.center)
Spacer()
}.padding()
switch self.challenge.type {
case .faceMovementChallenge:
VStack {
Text(LocalizedStrings.preview_center_your_face_text)
.font(.title)
.multilineTextAlignment(.center)
Spacer()
}.padding()
case . faceMovementAndLightChallenge:
VStack {
WarningBox(
titleText: LocalizedStrings.get_ready_photosensitivity_title,
bodyText: LocalizedStrings.get_ready_photosensitivity_description,
popoverContent: { photosensitivityWarningPopoverContent }
)
.accessibilityElement(children: .combine)
Text(LocalizedStrings.preview_center_your_face_text)
.font(.title)
.multilineTextAlignment(.center)
Spacer()
}.padding()
}
}
beginCheckButton
}
Expand Down Expand Up @@ -72,6 +86,8 @@ struct GetReadyPageView: View {

struct GetReadyPageView_Previews: PreviewProvider {
static var previews: some View {
GetReadyPageView(onBegin: {})
GetReadyPageView(onBegin: {},
challenge: .init(version: "2.0.0",
type: .faceMovementAndLightChallenge))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import SwiftUI
import Combine
@_spi(PredictionsFaceLiveness) import AWSPredictionsPlugin

struct InstructionContainerView: View {
@ObservedObject var viewModel: FaceLivenessDetectionViewModel
Expand Down Expand Up @@ -97,13 +98,29 @@ struct InstructionContainerView: View {
argument: LocalizedStrings.challenge_verifying
)
}
case .faceMatched:
case .completedNoLightCheck:
InstructionView(
text: LocalizedStrings.challenge_instruction_hold_still,
backgroundColor: .livenessPrimaryBackground,
textColor: .livenessPrimaryLabel,
font: .title
text: LocalizedStrings.challenge_verifying,
backgroundColor: .livenessBackground
)
.onAppear {
UIAccessibility.post(
notification: .announcement,
argument: LocalizedStrings.challenge_verifying
)
}
case .faceMatched:
if let challenge = viewModel.challenge,
case .faceMovementAndLightChallenge = challenge.type {
InstructionView(
text: LocalizedStrings.challenge_instruction_hold_still,
backgroundColor: .livenessPrimaryBackground,
textColor: .livenessPrimaryLabel,
font: .title
)
} else {
EmptyView()
}
default:
EmptyView()
}
Expand Down
Loading

0 comments on commit cc166eb

Please sign in to comment.