Skip to content

Commit 048a044

Browse files
author
Victor
committed
upgrade liveness
1 parent 017382b commit 048a044

25 files changed

+549
-146
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changelog
22

3+
## 1.4.2 (2025-07-17)
4+
5+
## 1.4.1 (2025-07-01)
6+
7+
## 1.4.0 (2025-06-30)
8+
9+
### Features
10+
11+
- add no-light/facemovementonly challenge and back camera support (#131)
12+
313
## 1.3.5 (2025-03-18)
414

515
## 1.3.4 (2025-01-13)

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ let package = Package(
1313
targets: ["FaceLiveness"]),
1414
],
1515
dependencies: [
16-
.package(url: "https://github.com/aws-amplify/amplify-swift", exact: "2.45.4")
16+
.package(url: "https://github.com/aws-amplify/amplify-swift", exact: "2.49.0")
1717
],
1818
targets: [
1919
.target(

Sources/FaceLiveness/FaceDetection/BlazeFace/DetectedFace.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77

88
import Foundation
9+
@_spi(PredictionsFaceLiveness) import AWSPredictionsPlugin
910

1011
struct DetectedFace {
1112
var boundingBox: CGRect
@@ -19,7 +20,8 @@ struct DetectedFace {
1920

2021
let confidence: Float
2122

22-
func boundingBoxFromLandmarks(ovalRect: CGRect) -> CGRect {
23+
func boundingBoxFromLandmarks(ovalRect: CGRect,
24+
ovalMatchChallenge: FaceLivenessSession.OvalMatchChallenge) -> CGRect {
2325
let alpha = 2.0
2426
let gamma = 1.8
2527
let ow = (alpha * pupilDistance + gamma * faceHeight) / 2
@@ -34,7 +36,7 @@ struct DetectedFace {
3436
}
3537

3638
let faceWidth = ow
37-
let faceHeight = 1.618 * faceWidth
39+
let faceHeight = ovalMatchChallenge.oval.heightWidthRatio * faceWidth
3840
let faceBoxBottom = boundingBox.maxY
3941
let faceBoxTop = faceBoxBottom - faceHeight
4042
let faceBoxLeft = min(cx - ow / 2, rightEar.x)

Sources/FaceLiveness/FaceDetection/BlazeFace/FaceDetectorShortRange+Model.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import Accelerate
1212
import CoreGraphics
1313
import CoreImage
1414
import VideoToolbox
15+
@_spi(PredictionsFaceLiveness) import AWSPredictionsPlugin
1516

1617
enum FaceDetectorShortRange {}
1718

@@ -33,11 +34,16 @@ extension FaceDetectorShortRange {
3334
)
3435
}
3536

37+
weak var faceDetectionSessionConfiguration: FaceDetectionSessionConfigurationWrapper?
3638
weak var detectionResultHandler: FaceDetectionResultHandler?
3739

3840
func setResultHandler(detectionResultHandler: FaceDetectionResultHandler) {
3941
self.detectionResultHandler = detectionResultHandler
4042
}
43+
44+
func setFaceDetectionSessionConfigurationWrapper(configuration: FaceDetectionSessionConfigurationWrapper) {
45+
self.faceDetectionSessionConfiguration = configuration
46+
}
4147

4248
func detectFaces(from buffer: CVPixelBuffer) {
4349
let faces = prediction(for: buffer)
@@ -105,10 +111,22 @@ extension FaceDetectorShortRange {
105111
count: confidenceScoresCapacity
106112
)
107113
)
114+
115+
let blazeFaceDetectionThreshold: Float
116+
if let sessionConfiguration = faceDetectionSessionConfiguration?.sessionConfiguration {
117+
switch sessionConfiguration {
118+
case .faceMovement(let ovalMatchChallenge):
119+
blazeFaceDetectionThreshold = Float(ovalMatchChallenge.faceDetectionThreshold)
120+
case .faceMovementAndLight(_, let ovalMatchChallenge):
121+
blazeFaceDetectionThreshold = Float(ovalMatchChallenge.faceDetectionThreshold)
122+
}
123+
} else {
124+
blazeFaceDetectionThreshold = confidenceScoreThreshold
125+
}
108126

109127
var passingConfidenceScoresIndices = confidenceScores
110128
.enumerated()
111-
.filter { $0.element >= confidenceScoreThreshold }
129+
.filter { $0.element >= blazeFaceDetectionThreshold}
112130
.sorted(by: {
113131
$0.element > $1.element
114132
})

Sources/FaceLiveness/FaceDetection/FaceDetector.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77

88
import AVFoundation
9+
@_spi(PredictionsFaceLiveness) import AWSPredictionsPlugin
910

1011
protocol FaceDetector {
1112
func detectFaces(from buffer: CVPixelBuffer)
@@ -16,6 +17,10 @@ protocol FaceDetectionResultHandler: AnyObject {
1617
func process(newResult: FaceDetectionResult)
1718
}
1819

20+
protocol FaceDetectionSessionConfigurationWrapper: AnyObject {
21+
var sessionConfiguration: FaceLivenessSession.SessionConfiguration? { get }
22+
}
23+
1924
enum FaceDetectionResult {
2025
case noFace
2126
case singleFace(DetectedFace)

Sources/FaceLiveness/Utilities/FinalClientEvent+Init.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,14 @@ extension FinalClientEvent {
1717
faceMatchedEnd: UInt64,
1818
videoEnd: UInt64
1919
) {
20-
let normalizedBoundingBox = sessionConfiguration
21-
.ovalMatchChallenge
20+
let ovalMatchChallenge: FaceLivenessSession.OvalMatchChallenge
21+
switch sessionConfiguration {
22+
case .faceMovement(let challenge):
23+
ovalMatchChallenge = challenge
24+
case .faceMovementAndLight(_, let challenge):
25+
ovalMatchChallenge = challenge
26+
}
27+
let normalizedBoundingBox = ovalMatchChallenge
2228
.oval.boundingBox
2329
.normalize(within: videoSize)
2430

Sources/FaceLiveness/Utilities/UserAgent.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import InternalAmplifyCredentials
1010

1111
struct UserAgentValues {
1212

13-
static let libVersion = "1.3.4"
13+
static let libVersion = "1.4.2"
1414
static let libName = "amplify-ui-swift-face-liveness"
1515

1616
let amplifyVersion: String

Sources/FaceLiveness/Views/CameraPermission/CameraPermissionView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ struct CameraPermissionView: View {
1717
}
1818

1919
var body: some View {
20-
VStack(alignment: .leading) {
20+
VStack(alignment: .center) {
2121
Spacer()
2222
VStack {
2323
Text(LocalizedStrings.camera_permission_change_setting_header)

Sources/FaceLiveness/Views/GetReadyPage/CameraPreviewView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ struct CameraPreviewView: View {
1515

1616
@StateObject var model: CameraPreviewViewModel
1717

18-
init(model: CameraPreviewViewModel = CameraPreviewViewModel()) {
18+
init(model: CameraPreviewViewModel = CameraPreviewViewModel(cameraPosition: .front)) {
1919
self._model = StateObject(wrappedValue: model)
2020
}
2121

Sources/FaceLiveness/Views/GetReadyPage/CameraPreviewViewModel.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@ class CameraPreviewViewModel: NSObject, ObservableObject {
1616
@Published var buffer: CVPixelBuffer?
1717

1818
var previewCaptureSession: LivenessCaptureSession?
19+
let cameraPosition: LivenessCamera
1920

20-
override init() {
21+
init(cameraPosition: LivenessCamera) {
22+
self.cameraPosition = cameraPosition
23+
2124
super.init()
2225
setupSubscriptions()
2326

2427
let avCaptureDevice = AVCaptureDevice.DiscoverySession(
2528
deviceTypes: [.builtInWideAngleCamera],
2629
mediaType: .video,
27-
position: .front
30+
position: cameraPosition == .front ? .front : .back
2831
).devices.first
2932

3033
let outputDelegate = CameraPreviewOutputSampleBufferDelegate { [weak self] buffer in

0 commit comments

Comments
 (0)