Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(predictions): No-Light/FaceMovementOnly challenge support #3622

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ public enum LivenessEventKind {
self.rawValue = rawValue
}

public static let challenge = Self(rawValue: "ServerSessionInformationEvent")
public static let sessionInformation = Self(rawValue: "ServerSessionInformationEvent")
public static let disconnect = Self(rawValue: "DisconnectionEvent")
public static let challenge = Self(rawValue: "ChallengeEvent")
}
case server(Server)

Expand Down Expand Up @@ -60,6 +61,7 @@ extension LivenessEventKind: CustomDebugStringConvertible {
public var debugDescription: String {
switch self {
case .server(.challenge): return ".server(.challenge)"
case .server(.sessionInformation): return ".server(.sessionInformation)"
case .server(.disconnect): return ".server(.disconnect)"
case .client(.initialFaceDetected): return ".client(.initialFaceDetected)"
case .client(.video): return ".client(.video)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ public struct FinalClientEvent {

extension LivenessEvent where T == FinalClientEvent {
@_spi(PredictionsFaceLiveness)
public static func final(event: FinalClientEvent) throws -> Self {

let clientEvent = ClientSessionInformationEvent(
challenge: .init(
faceMovementAndLightChallenge: .init(
public static func final(event: FinalClientEvent,
challenge: Challenge) throws -> Self {
let clientChallengeType: ClientChallenge.ChallengeType
switch challenge.type {
case .faceMovementAndLightChallenge:
clientChallengeType = .faceMovementAndLightChallenge(
challenge: .init(
challengeID: event.initialClientEvent.challengeID,
targetFace: .init(
boundingBox: .init(boundingBox: event.targetFace.initialEvent.boundingBox),
Expand All @@ -46,7 +48,26 @@ extension LivenessEvent where T == FinalClientEvent {
videoEndTimeStamp: Date().epochMilliseconds
)
)
)
case .faceMovementChallenge:
clientChallengeType = .faceMovementChallenge(
challenge: .init(
challengeID: event.initialClientEvent.challengeID,
targetFace: .init(
boundingBox: .init(boundingBox: event.targetFace.initialEvent.boundingBox),
faceDetectedInTargetPositionStartTimestamp: event.targetFace.initialEvent.startTimestamp,
faceDetectedInTargetPositionEndTimestamp: event.targetFace.endTimestamp
),
initialFace: .init(
boundingBox: .init(boundingBox: event.initialClientEvent.initialFaceLocation.boundingBox),
initialFaceDetectedTimeStamp: event.initialClientEvent.initialFaceLocation.startTimestamp
),
videoStartTimestamp: nil,
videoEndTimeStamp: Date().epochMilliseconds
)
)
}

let clientEvent = ClientSessionInformationEvent(challenge: .init(clientChallengeType: clientChallengeType))
let payload = try JSONEncoder().encode(clientEvent)
return .init(
payload: payload,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,20 @@ extension LivenessEvent where T == FreshnessEvent {
public static func freshness(event: FreshnessEvent) throws -> Self {
let clientEvent = ClientSessionInformationEvent(
challenge: .init(
faceMovementAndLightChallenge: .init(
challengeID: event.challengeID,
targetFace: nil,
initialFace: nil,
videoStartTimestamp: nil,
colorDisplayed: .init(
currentColor: .init(rgb: event.color),
sequenceNumber: event.sequenceNumber,
currentColorStartTimeStamp: event.timestamp,
previousColor: .init(rgb: event.previousColor)
),
videoEndTimeStamp: nil
clientChallengeType: .faceMovementAndLightChallenge(
challenge: .init(
challengeID: event.challengeID,
targetFace: nil,
initialFace: nil,
videoStartTimestamp: nil,
colorDisplayed: .init(
currentColor: .init(rgb: event.color),
sequenceNumber: event.sequenceNumber,
currentColorStartTimeStamp: event.timestamp,
previousColor: .init(rgb: event.previousColor)
),
videoEndTimeStamp: nil
)
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,20 @@ public struct InitialClientEvent {

extension LivenessEvent where T == InitialClientEvent {
@_spi(PredictionsFaceLiveness)
public static func initialFaceDetected(event: InitialClientEvent) throws -> Self {
public static func initialFaceDetected(
event: InitialClientEvent,
challenge: Challenge
) throws -> Self {
let initialFace = InitialFace(
boundingBox: .init(boundingBox: event.initialFaceLocation.boundingBox),
initialFaceDetectedTimeStamp: event.initialFaceLocation.startTimestamp
)

let clientSessionInformationEvent = ClientSessionInformationEvent(
challenge: .init(
faceMovementAndLightChallenge: .init(
let clientChallengeType: ClientChallenge.ChallengeType
switch challenge.type {
case .faceMovementAndLightChallenge:
clientChallengeType = .faceMovementAndLightChallenge(
challenge: .init(
challengeID: event.challengeID,
targetFace: nil,
initialFace: initialFace,
Expand All @@ -43,8 +48,21 @@ extension LivenessEvent where T == InitialClientEvent {
videoEndTimeStamp: nil
)
)
case .faceMovementChallenge:
clientChallengeType = .faceMovementChallenge(
challenge: .init(
challengeID: event.challengeID,
targetFace: nil,
initialFace: initialFace,
videoStartTimestamp: event.videoStartTimestamp,
videoEndTimeStamp: nil
)
)
}

let clientSessionInformationEvent = ClientSessionInformationEvent(
challenge: .init(clientChallengeType: clientChallengeType)
)

let payload = try JSONEncoder().encode(clientSessionInformationEvent)
return .init(
payload: payload,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,18 @@
import Foundation

func ovalChallenge(from event: ServerSessionInformationEvent) -> FaceLivenessSession.OvalMatchChallenge {
let challengeConfig = event.sessionInformation.challenge.faceMovementAndLightChallenge.challengeConfig
let ovalParameters = event.sessionInformation.challenge.faceMovementAndLightChallenge.ovalParameters
let challengeConfig: ChallengeConfig
let ovalParameters: OvalParameters

switch event.sessionInformation.challenge.type {
case .faceMovementAndLightChallenge(let challenge):
challengeConfig = challenge.challengeConfig
ovalParameters = challenge.ovalParameters
case .faceMovementChallenge(let challenge):
challengeConfig = challenge.challengeConfig
ovalParameters = challenge.ovalParameters
}

let ovalBoundingBox = FaceLivenessSession.BoundingBox.init(
x: Double(ovalParameters.centerX - ovalParameters.width / 2),
y: Double(ovalParameters.centerY - ovalParameters.height / 2),
Expand Down Expand Up @@ -37,44 +47,46 @@ func ovalChallenge(from event: ServerSessionInformationEvent) -> FaceLivenessSes
)
}

func colorChallenge(from event: ServerSessionInformationEvent) -> FaceLivenessSession.ColorChallenge {
let displayColors = event.sessionInformation.challenge
.faceMovementAndLightChallenge.colorSequences
.map({ color -> FaceLivenessSession.DisplayColor in
func colorChallenge(from event: ServerSessionInformationEvent) -> FaceLivenessSession.ColorChallenge? {
switch event.sessionInformation.challenge.type {
case .faceMovementAndLightChallenge(let challenge):
let displayColors = challenge.colorSequences
.map({ color -> FaceLivenessSession.DisplayColor in

let duration: Double
let shouldScroll: Bool
switch (color.downscrollDuration, color.flatDisplayDuration) {
case (...0, 0...):
duration = Double(color.flatDisplayDuration)
shouldScroll = false
default:
duration = Double(color.downscrollDuration)
shouldScroll = true
}
let duration: Double
let shouldScroll: Bool
switch (color.downscrollDuration, color.flatDisplayDuration) {
case (...0, 0...):
duration = Double(color.flatDisplayDuration)
shouldScroll = false
default:
duration = Double(color.downscrollDuration)
shouldScroll = true
}

precondition(
color.freshnessColor.rgb.count == 3,
"""
Received invalid freshness colors.
Expected 3 values (r, g, b), received: \(color.freshnessColor.rgb.count)
"""
)
precondition(
color.freshnessColor.rgb.count == 3,
"""
Received invalid freshness colors.
Expected 3 values (r, g, b), received: \(color.freshnessColor.rgb.count)
"""
)

return .init(
rgb: .init(
red: Double(color.freshnessColor.rgb[0]) / 255,
green: Double(color.freshnessColor.rgb[1]) / 255,
blue: Double(color.freshnessColor.rgb[2]) / 255,
_values: color.freshnessColor.rgb
),
duration: duration,
shouldScroll: shouldScroll
)
})
return .init(
colors: displayColors
)
return .init(
rgb: .init(
red: Double(color.freshnessColor.rgb[0]) / 255,
green: Double(color.freshnessColor.rgb[1]) / 255,
blue: Double(color.freshnessColor.rgb[2]) / 255,
_values: color.freshnessColor.rgb
),
duration: duration,
shouldScroll: shouldScroll
)
})
return .init(colors: displayColors)
case .faceMovementChallenge:
return nil
}
}

func sessionConfiguration(from event: ServerSessionInformationEvent) -> FaceLivenessSession.SessionConfiguration {
Expand All @@ -83,3 +95,10 @@ func sessionConfiguration(from event: ServerSessionInformationEvent) -> FaceLive
ovalMatchChallenge: ovalChallenge(from: event)
)
}

func challengeType(from event: ChallengeEvent) -> Challenge {
.init(
version: event.version,
type: event.type
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Foundation

extension FaceLivenessSession {
public static let supportedChallenges: [Challenge] = [
Challenge(version: "2.0.0", type: .faceMovementAndLightChallenge),
Challenge(version: "1.0.0", type: .faceMovementChallenge)
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import Foundation
extension FaceLivenessSession {
@_spi(PredictionsFaceLiveness)
public struct SessionConfiguration {
public let colorChallenge: ColorChallenge
public let colorChallenge: ColorChallenge?
public let ovalMatchChallenge: OvalMatchChallenge

public init(colorChallenge: ColorChallenge, ovalMatchChallenge: OvalMatchChallenge) {
public init(
colorChallenge: ColorChallenge? = nil,
ovalMatchChallenge: OvalMatchChallenge
) {
self.colorChallenge = colorChallenge
self.ovalMatchChallenge = ovalMatchChallenge
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ extension AWSPredictionsPlugin {
withID sessionID: String,
credentialsProvider: AWSCredentialsProvider? = nil,
region: String,
options: FaceLivenessSession.Options,
completion: @escaping (Result<Void, FaceLivenessSessionError>) -> Void
) async throws -> FaceLivenessSession {

Expand Down Expand Up @@ -48,7 +47,16 @@ extension AWSPredictionsPlugin {
extension FaceLivenessSession {
@_spi(PredictionsFaceLiveness)
public struct Options {
public init() {}
public let attemptCount: Int
public let preCheckViewEnabled: Bool

public init(
attemptCount: Int,
preCheckViewEnabled: Bool
) {
self.attemptCount = attemptCount
self.preCheckViewEnabled = preCheckViewEnabled
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Foundation

@_spi(PredictionsFaceLiveness)
public struct Challenge: Codable {
public let version: String
public let type: ChallengeType

public init(version: String, type: ChallengeType) {
self.version = version
self.type = type
}

public func queryParameterString() -> String {
return self.type.rawValue + "_" + self.version
}

enum CodingKeys: String, CodingKey {
case version = "Version"
case type = "Type"
}
}

@_spi(PredictionsFaceLiveness)
public enum ChallengeType: String, Codable {
case faceMovementChallenge = "FaceMovementChallenge"
case faceMovementAndLightChallenge = "FaceMovementAndLightChallenge"
}
Loading
Loading