Skip to content

Commit 762c6ae

Browse files
authored
feat: add liveness (#12)
1 parent ab04d3c commit 762c6ae

File tree

80 files changed

+10726
-8
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+10726
-8
lines changed

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
/*.xcodeproj
5+
xcuserdata/
6+
DerivedData/
7+
.swiftpm/config/registries.json
8+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
9+
.netrc
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>FILEHEADER</key>
6+
<string>
7+
// Copyright Amazon.com Inc. or its affiliates.
8+
// All Rights Reserved.
9+
//
10+
// SPDX-License-Identifier: Apache-2.0
11+
//</string>
12+
</dict>
13+
</plist>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>IDEDidComputeMac32BitWarning</key>
6+
<true/>
7+
</dict>
8+
</plist>

Package.resolved

Lines changed: 104 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// swift-tools-version: 5.7
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "AmplifyUILiveness",
8+
defaultLocalization: "en",
9+
platforms: [.iOS(.v14)],
10+
products: [
11+
.library(
12+
name: "FaceLiveness",
13+
targets: ["FaceLiveness"]),
14+
],
15+
dependencies: [
16+
.package(url: "https://github.com/aws-amplify/amplify-swift", from: "2.8.0")
17+
],
18+
targets: [
19+
.target(
20+
name: "FaceLiveness",
21+
dependencies: [
22+
.product(name: "AWSPluginsCore", package: "amplify-swift"),
23+
.product(name: "AWSCognitoAuthPlugin", package: "amplify-swift"),
24+
.product(name: "AWSPredictionsPlugin", package: "amplify-swift")
25+
],
26+
resources: [
27+
.process("Resources/Base.lproj"),
28+
.copy("Resources/face_detection_short_range.mlmodelc")
29+
]
30+
),
31+
.testTarget(
32+
name: "FaceLivenessTests",
33+
dependencies: ["FaceLiveness"]),
34+
]
35+
)

README.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,16 @@
22

33
---
44

5-
# Amplify UI for SwiftUI
5+
# Amplify UI Liveness for SwiftUI
66

7-
[![GitHub](https://img.shields.io/github/license/aws-amplify/amplify-ui-swift)](LICENSE)
7+
[![GitHub](https://img.shields.io/github/license/aws-amplify/amplify-ui-swift-liveness)](LICENSE)
88
[![Discord](https://img.shields.io/discord/308323056592486420?logo=discord)](https://discord.gg/jWVbPfC)
9-
[![Open Bugs](https://img.shields.io/github/issues/aws-amplify/amplify-ui-swift/bug?color=d73a4a&label=bugs)](https://github.com/aws-amplify/amplify-ui-swift/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
10-
[![Feature Requests](https://img.shields.io/github/issues/aws-amplify/amplify-ui-swift/feature-request?color=ff9001&label=feature%20requests)](https://github.com/aws-amplify/amplify-ui-swift/issues?q=is%3Aissue+label%3Afeature-request+is%3Aopen)
9+
[![Open Bugs](https://img.shields.io/github/issues/aws-amplify/amplify-ui-swift-liveness/bug?color=d73a4a&label=bugs)](https://github.com/aws-amplify/amplify-ui-swift-liveness/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
10+
[![Feature Requests](https://img.shields.io/github/issues/aws-amplify/amplify-ui-swift-liveness/feature-request?color=ff9001&label=feature%20requests)](https://github.com/aws-amplify/amplify-ui-swift-liveness/issues?q=is%3Aissue+label%3Afeature-request+is%3Aopen)
1111

12-
Amplify UI for SwiftUI is an open-source UI library with components that are endlessly customizable, accessible, and can integrate into _any_ SwiftUI application. Amplify UI consists of:
12+
Amplify UI Liveness for SwiftUI provides a UI component (FaceLivenessDetector) that helps developers deter fraud and scams during face-based identity checks by validating that users are physically present in front of the camera at that moment, and not an imposter spoofing the user's face.
1313

14-
1. Connected components that simplify complex cloud-connected workflows, like Authenticator.
15-
2. Primitive components that create consistency across Amplify UI and allow you to build complete applications that fit your brand, like Buttons and Badges.
16-
3. Theming capabilities that allow you to customize the appearance of Amplify UI to match your brand.
14+
More information on setting up and using the FaceLivenessDetector is in the [Amplify UI Face Liveness documentation](https://ui.docs.amplify.aws/swift/connected-components/liveness).
1715

1816
## Contributing
1917

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import AVFoundation
9+
import CoreImage
10+
11+
extension CMSampleBuffer {
12+
func rotateRightUpMirrored() -> CVPixelBuffer? {
13+
guard let pixelBuffer = CMSampleBufferGetImageBuffer(self) else {
14+
return nil
15+
}
16+
17+
var cvPixelBufferPtr: CVPixelBuffer?
18+
19+
let error = CVPixelBufferCreate(
20+
kCFAllocatorDefault,
21+
CVPixelBufferGetHeight(pixelBuffer),
22+
CVPixelBufferGetWidth(pixelBuffer),
23+
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
24+
nil,
25+
&cvPixelBufferPtr
26+
)
27+
28+
guard error == kCVReturnSuccess,
29+
let cvPixelBuffer = cvPixelBufferPtr
30+
else {
31+
return nil
32+
}
33+
34+
let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
35+
.oriented(.right)
36+
.oriented(.upMirrored)
37+
38+
let context = CIContext(options: nil)
39+
context.render(ciImage, to: cvPixelBuffer)
40+
return cvPixelBuffer
41+
}
42+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import AVFoundation
9+
10+
final class LivenessAVAssetWriter: AVAssetWriter {
11+
init() {
12+
super.init(contentType: .mpeg4Movie)
13+
outputFileTypeProfile = .mpeg4CMAFCompliant
14+
preferredOutputSegmentInterval = CMTime(
15+
seconds: 1,
16+
preferredTimescale: 1
17+
)
18+
initialSegmentStartTime = CMTime(seconds: 0, preferredTimescale: 240)
19+
}
20+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import AVFoundation
9+
10+
fileprivate let videoWidth = 480
11+
fileprivate let videoHeight = 640
12+
fileprivate let bitRate = 1_000_000
13+
14+
class LivenessAVAssetWriterInput: AVAssetWriterInput {
15+
init() {
16+
super.init(
17+
mediaType: .video,
18+
outputSettings: [
19+
AVVideoCodecKey: AVVideoCodecType.h264,
20+
AVVideoWidthKey: videoWidth,
21+
AVVideoHeightKey: videoHeight,
22+
AVVideoCompressionPropertiesKey: [AVVideoAverageBitRateKey: bitRate]
23+
],
24+
sourceFormatHint: nil
25+
)
26+
}
27+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// Copyright Amazon.com Inc. or its affiliates.
3+
// All Rights Reserved.
4+
//
5+
// SPDX-License-Identifier: Apache-2.0
6+
//
7+
8+
import AVFoundation
9+
10+
struct LivenessCaptureDevice {
11+
let avCaptureDevice: AVCaptureDevice?
12+
var preset: AVCaptureSession.Preset = .vga640x480
13+
var fps: Double = 30
14+
var exposure: AVCaptureDevice.ExposureMode = .continuousAutoExposure
15+
var whiteBalance: AVCaptureDevice.WhiteBalanceMode = .continuousAutoWhiteBalance
16+
var focus: AVCaptureDevice.FocusMode = .continuousAutoFocus
17+
18+
func configure() throws {
19+
guard let avCaptureDevice else { throw LivenessCaptureSessionError.cameraUnavailable }
20+
try avCaptureDevice.lockForConfiguration()
21+
defer { avCaptureDevice.unlockForConfiguration() }
22+
23+
let fps = CMTimeScale(fps)
24+
let frameDuration = CMTime(value: 1, timescale: fps)
25+
avCaptureDevice.activeVideoMinFrameDuration = frameDuration
26+
avCaptureDevice.activeVideoMaxFrameDuration = frameDuration
27+
if avCaptureDevice.isExposureModeSupported(exposure) {
28+
avCaptureDevice.exposureMode = exposure
29+
}
30+
31+
if avCaptureDevice.isFocusModeSupported(focus) {
32+
avCaptureDevice.focusMode = focus
33+
}
34+
35+
if avCaptureDevice.isWhiteBalanceModeSupported(whiteBalance) {
36+
avCaptureDevice.whiteBalanceMode = whiteBalance
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)