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

New Smart Selfie Capture #267

Merged
merged 39 commits into from
Dec 13, 2024
Merged

New Smart Selfie Capture #267

merged 39 commits into from
Dec 13, 2024

Conversation

tobitech
Copy link
Contributor

Stories:
https://app.shortcut.com/smileid/story/13453/improve-user-experience-of-new-active-liveness-screens-v10-ios
https://app.shortcut.com/smileid/story/13080/add-active-liveness-smartselfie-v2-ios

Summary

This includes all the tasks for the implementation for new Enhanced Selfie Enrollment and Authentication.

Known Issues

N/A.

Test Instructions

N/A.

Screenshot

N/A

tobitech added 30 commits July 30, 2024 12:02
* implement design for selfie capture camera frame.

* add lottie animation, product cells for selfie enrollment and selfie authentication with strict mode.

* control visibility of smile emblem with showAttribution flag

* make selfie frame border themable.
* implement design for selfie capture camera frame.

* add lottie animation, product cells for selfie enrollment and selfie authentication with strict mode.

* update new selfie view model with existing implementation

* add some image utility functions, import selfie quality model and add selfie quality check to the image buffer from camera feed.

* some code formatting

* more code formatting

* compile coreml model to generate .mlmodelc file.

* automatically generate the model class file.

* remove uncompiled model file.

* disable lint from generated file.

* make a separate class that will handle all the image processing and classification tasks for selfie quality check.

* add some documentation and code formatting

* fix issue with crop to face function.

* Replace the selfie quality model with a new version that takes an image as input.

* remove call to check selfie quality in the v2 view model.

* disable linting for model generated file.

* code formattting.

* remove resources folder and the lottie file.

* code formatting.

* move model image cropping size info to a constant.
# Conflicts:
#	Example/Podfile.lock
…Liveness Check Manager. (#224)

* implement design for selfie capture camera frame.

* add lottie animation, product cells for selfie enrollment and selfie authentication with strict mode.

* update new selfie view model with existing implementation

* add some image utility functions, import selfie quality model and add selfie quality check to the image buffer from camera feed.

* some code formatting

* more code formatting

* compile coreml model to generate .mlmodelc file.

* automatically generate the model class file.

* remove uncompiled model file.

* disable lint from generated file.

* make a separate class that will handle all the image processing and classification tasks for selfie quality check.

* add some documentation and code formatting

* fix issue with crop to face function.

* introduce a new version for facedetector class and new debug design for selfie capture screen. also refactor state management and action handling for the selfie view model v2

* rename preview view to camera view controller

* hook up new face detector class to the new selfie view model.

* integrate the new model that takes an image input and also display the outputs in the debug view in real time.

* remove lottie resources. code formatting.

* Add new debug views and and also use av preview layer to calculate the bounding box

* Fix issue with face bounding box. implement face size and positioning within the frame.

* add instructions view to the selfie capture screen.

* add image capture to the selfie view model.

* Add a view to show captured images: selfie and liveness.

* add active liveness manager class and move updating directive to the view model.

* some clean up

* remove print statements from facedetectorv2

* move face geometry check for active liveness into the liveness manager

* refactor how current task is assigned and how directive for liveness task is displayed

* update criteria for passing the liveness checks

* fix the length of the captured image view

* code formatting

* add some documentation to liveness check manager class

* more docs

* run pod install

* folder structure changes.

* code formatting.

* import UIkit inside Imageclassifier.

* add the ML folder group into the right target.

* some layout changes.

* update the bundle for the generated ml model class.

* move initialisation for the image classifier into the init function so that it's done only once
# Conflicts:
#	Example/Podfile.lock
# Conflicts:
#	Example/Podfile.lock
* implement design for selfie capture camera frame.

* add lottie animation, product cells for selfie enrollment and selfie authentication with strict mode.

* update new selfie view model with existing implementation

* add some image utility functions, import selfie quality model and add selfie quality check to the image buffer from camera feed.

* some code formatting

* more code formatting

* compile coreml model to generate .mlmodelc file.

* automatically generate the model class file.

* remove uncompiled model file.

* disable lint from generated file.

* make a separate class that will handle all the image processing and classification tasks for selfie quality check.

* add some documentation and code formatting

* fix issue with crop to face function.

* introduce a new version for facedetector class and new debug design for selfie capture screen. also refactor state management and action handling for the selfie view model v2

* rename preview view to camera view controller

* hook up new face detector class to the new selfie view model.

* integrate the new model that takes an image input and also display the outputs in the debug view in real time.

* remove lottie resources. code formatting.

* Add new debug views and and also use av preview layer to calculate the bounding box

* Fix issue with face bounding box. implement face size and positioning within the frame.

* add instructions view to the selfie capture screen.

* add image capture to the selfie view model.

* Add a view to show captured images: selfie and liveness.

* add active liveness manager class and move updating directive to the view model.

* some clean up

* remove print statements from facedetectorv2

* move face geometry check for active liveness into the liveness manager

* refactor how current task is assigned and how directive for liveness task is displayed

* update criteria for passing the liveness checks

* fix the length of the captured image view

* code formatting

* add some documentation to liveness check manager class

* more docs

* run pod install

* folder structure changes.

* code formatting.

* import UIkit inside Imageclassifier.

* add some views for new selfie design

* create a folder group for lottie files in the resources folder and add a new lottie file.

* add lottie animation to liveness instructions screen.

* rename selfie capture instructions view to liveness capture

* Add liveness guides view with the 3 progress arcs

* restore lottie view

* import the liveness guide lottie file into resources.

* Remove comments
# Conflicts:
#	Example/Podfile.lock
#	Example/SmileID.xcodeproj/project.pbxproj
#	SmileID.xcodeproj/project.pbxproj
#	Sources/SmileID/Classes/DocumentVerification/Model/DocumentCaptureViewModel.swift
#	Sources/SmileID/Classes/SelfieCapture/SelfieViewModel.swift
# Conflicts:
#	Example/Podfile.lock
* improve code and file structure

* add the liveness guides

* add some config values to liveness guides view.

* change lottie animation frame

* run pod install

* setup animation progress time for the liiveness guide lottie animation.

* refactor the components of liveness guides view.
# Conflicts:
#	Example/Podfile.lock
* improve code and file structure

* add the liveness guides

* add some config values to liveness guides view.

* change lottie animation frame

* run pod install

* setup animation progress time for the liiveness guide lottie animation.

* fix homeview fore each warnings.

* connect face bounds detection to the the indicator.

* add throttling to camera feed.

* use full screen cover to present home screen products.

* control progress arc visibility based on progress.

* add a dummy submit function.

* setup timers.

* add animation to the progress fill arcs.

* refactor the components of liveness guides view.

* setup delay timer and introduce state for the current animation that should be displayed with instructions after delay.

* update arc shape init

* add new lottie files. define a guide animation enum to hold the animation details.

* some refactoring.

* create a validator class for the face observation data.

* refactor buffer image processing and communication between face detector, face validator and selfie view model. some code formatting.

* remove some debug views. some refactoring and improvements.

* make capture instruction strings localizable

* introduce current liveness task into the face validator to set the right capture instruction.

* fix cropping for selfie quality check.

* show or hide the circular ring and the liveness guides based on whether face is within bounds.

* add processing view to view captured images.

* reset animation as user is completing liveness checks

* localise instruction. use appropriate error for face not found.

* refactor image conversion for use in brightness and selfie quality checks.

* replace ObservedObject with StateObject in HomeView to prevent multiple initialising.
# Conflicts:
#	Example/Podfile.lock
#	Example/SmileID/Home/HomeView.swift
#	Sources/SmileID/Classes/SelfieCapture/SelfieViewModel.swift
# Conflicts:
#	Sources/SmileID/Classes/SelfieCapture/SelfieViewModel.swift
* improve code and file structure

* add the liveness guides

* add some config values to liveness guides view.

* change lottie animation frame

* run pod install

* setup animation progress time for the liiveness guide lottie animation.

* fix homeview fore each warnings.

* connect face bounds detection to the the indicator.

* add throttling to camera feed.

* use full screen cover to present home screen products.

* control progress arc visibility based on progress.

* add a dummy submit function.

* setup timers.

* add animation to the progress fill arcs.

* refactor the components of liveness guides view.

* setup delay timer and introduce state for the current animation that should be displayed with instructions after delay.

* update arc shape init

* add new lottie files. define a guide animation enum to hold the animation details.

* some refactoring.

* create a validator class for the face observation data.

* refactor buffer image processing and communication between face detector, face validator and selfie view model. some code formatting.

* remove some debug views. some refactoring and improvements.

* make capture instruction strings localizable

* introduce current liveness task into the face validator to set the right capture instruction.

* fix cropping for selfie quality check.

* show or hide the circular ring and the liveness guides based on whether face is within bounds.

* add processing view to view captured images.

* reset animation as user is completing liveness checks

* inject current liveness task into liveness guide to control which progress is showing. update reset delay timer on main thread. use appropriate error for face detection during cropping.

* processing screen layout. introduce a backport of stateobject.

* run pod install to import missing lottie files.

* present selfie capture flow in navigation view, programmatically navigate to processing view based on capture status. add a circular progress view with a loader image for the selfie processing view.

* refactor view appear setup and reset selfie capture state variables

* import submit method from selfie viewmodel.

* extract submit selfie functionality into a new class to manage the submission processes.

* some refactoring

* move backport and stateobject to helper folder.

* code formatting.

* restore threshold value

* replace ObservedObject with StateObject in HomeView so that it's initialised once.

* use proxy size instead of frame for window size calculation and face layout guide positioning.

* remove stateobject backport

* use a delegate to communicate selfie submission updates to selfie view model.
add the right title to processing screen.

* update processing changes on main thread.

* inject failure reason into the api call for submitting selfie

* rename selfie submission manager

* reset the threshold for timeout for liveness check.

* remove presentation mode variable.

* introduce an environment key to manage dismissing of the selfie capture flow.

* improve error handling of selfie capture.

* localise strings.

* code formatting.

* pod install

* make loader background color themeable.

* run pod install.

* fix missing files and build errors.

* redesign selfie capture screen to use box and oval for camera area and face bounding area

* add a view to preview selfie image, also add an actions view.

* redesign the the progress arcs for active liveness.

* improvements to validating face bounding box. add a frame to selfie preview image.

* remove processing view, add a view state to control visibility of different items based on capture state.

* adjust face size and position evaluation. fix layout for attribution and retry button.

* change active liveness progress colours

* code formatting.

* update faceboundmultiplier constant.

* refactor task timer.

* improve submission handling.
* add test for valid face.

* add more test cases for face validator class.

* refactor out common functionality for reusability in the validator tests.
# Conflicts:
#	Example/Podfile.lock
tobitech and others added 7 commits December 10, 2024 01:04
# Conflicts:
#	Example/Podfile.lock
#	Sources/SmileID/Classes/SmileID.swift
* decouple selfieviewmodel from livenesscheckmanager

* improve object references so to prevent retain cycles.

* write custom encoding function for failure reason, replace forced failure with failure reason enum. append failure reason data to multipart form request body.

* check that submission task is nil before assigning it.

* remove unnecessary comment

* feat: update changelog (#254)

* feat: update changelog

* chore: lint fix

* fix wrong version set for fingerprintjs package and dependency name causing spm not to resolve (#257)

* added autoassign to workflow (#259)

* added autoassign to workflow

* added autoassign to workflow/fix

* pod install

* add beta tag to strict mode products.
add a cancel toolbar button to all product screens.
remove cancel button from liveness instructions screen.

* use a different multiplier for checking face bounds for selfie and liveness capture
hide liveness progress if face is not valid.

* bump build number and add haptic feedback to selfie capture.

* reduce luminance threshold lowerbound

* reduce luminace threshold lower bound

* introduce a function to flip the selfie image for preview during submission.

* reduce head turn thresholds

* remove brightness check from selfie capture v2

* adjust screen brightness for selfie capture screen v2

* Chore(deps): bump rexml from 3.3.6 to 3.3.9 (#249)

Bumps [rexml](https://github.com/ruby/rexml) from 3.3.6 to 3.3.9.
- [Release notes](https://github.com/ruby/rexml/releases)
- [Changelog](https://github.com/ruby/rexml/blob/master/NEWS.md)
- [Commits](ruby/rexml@v3.3.6...v3.3.9)

---
updated-dependencies:
- dependency-name: rexml
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JNdhlovu <[email protected]>
Co-authored-by: Tobi Omotayo <[email protected]>

* bump version number.

* replace selfie quality check with vision face quality in face detector and face validator.

* reset the submission task when there is a response or error.

* improve publisher reference for legacy selfie view model.

* use weak reference in legacy selfie view model to prevent retain cycle.

* introduce a backport of StateObject into sdk helpers

* disable idle timer when capturing selfie and enable it back when dismissed.

* use a backport of stateobject in selfie capture screen.

* bump build number.

* restore brightness check

* check orientation before analyzing camera frames.

* add a did cancel delegate method to selfie result delegate.

* Chore(deps): bump slackapi/slack-github-action (#260)

* add property to set camera name in camera manager. implement fallback for pre-ios 15 devices. add didCancel endpoint to smart selfie result delegate.

* use uniqueID for cameraname. add a delay when capturing random liveness images during timeout.

* import new typeface DMSans and define a model to manage its fonts and styles.

* add face not within frame case to face bounds state.

* remove overlay from face bounding area. introduce new version of animations.

* code formatting.

* correct instructions typo.

* change new smart selfie products name. disable lint checks for backport files. code formatting.

* rename smart selfie v2 to enhanced

* pod install.

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: JNdhlovu <[email protected]>
Co-authored-by: Davina Anthony <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* set the right flag for isEnroll in orchestrated enhanced selfie capture screen. update the location of selfie and livenessimages when submission fails.

* grab last selfie enrollment userid from pasteboard, use new generated user id for selfie authentication

* fix the layout issues with biometric kyc and enhanced kyc country picker.

* bump version number. remove comments.

* add encryption compliance key and value for non exempt encryption
* code formatting.

* revert max pitch threshold

* correct instructions copy

* use device motion to determine device orientation and fall back to unlocked device orientation if device motion is not available.

* code formatting

* update to new lottie animation designs.
…ces (#266)

* Fixed Missing Document Type (#264)

* added missing document type in document jobs

* Update CHANGELOG.md

* add new submission status icons for success and failed states.

* remove setting initial value for user instruction.

* handle multiple faces detected and throw an error

* change order of face validator so that luminance is checked before head position.

* reduce the stroke size of the face in bounds indicator.

* remove done button and hide cancel button if submission was successful.

* use new status icons in status view.

* add a placeholder for api error 2214

* update selfie actions view parameters

* restore camera manager to have accessible initializer.
add camera session size preset for selfie capture image

* code formatting and set line limit of 2 to user instructions title.

* change capture screen layout to accommodate small screen devices.
remove cancel delegate method from selfie result.

* dismiss the selfie capture when submission is successful.

---------

Co-authored-by: Juma Allan <[email protected]>
* added active liveness metadata

* added headpose metadata

* updated camera name metadata

* updated liveness type

* fix the camera info collection.

* code formatting

---------

Co-authored-by: Tobi Omotayo <[email protected]>
@tobitech tobitech self-assigned this Dec 13, 2024
@tobitech tobitech requested a review from a team as a code owner December 13, 2024 15:26
Copy link

github-actions bot commented Dec 13, 2024

Warnings
⚠️ The source files were changed, but the tests remain unmodified. Consider updating or adding to the tests to match the source changes.
⚠️

Sources/SmileID/Classes/SelfieCapture/SelfieViewModel.swift#L229 - TODOs should be resolved (Use mouth deformation as an al...). (todo)

⚠️

Sources/SmileID/Classes/SelfieCapture/View/FaceShapedProgressIndicator.swift#L14 - TODOs should be resolved (Make this fill from bottom to ...). (todo)

⚠️

Sources/SmileID/Classes/SmileID.swift#L159 - Function should have complexity 10 or less: currently complexity equals 11 (cyclomatic_complexity)

⚠️

Sources/SmileID/Classes/SmileID.swift#L186 - TODOs should be resolved (- Fix when Michael changes thi...). (todo)

Generated by 🚫 Danger Swift against 9f24319

@prfectionist
Copy link

prfectionist bot commented Dec 13, 2024

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Memory Management
Several closures capturing self without weak references could lead to retain cycles. Review memory management patterns.

Error Handling
The multipart request data creation has complex error handling paths that should be reviewed for edge cases and proper error propagation.

Thread Safety
Camera session configuration involves multiple queues and async operations. Review thread safety and synchronization.

@prfectionist
Copy link

prfectionist bot commented Dec 13, 2024

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Score
Possible bug
Safe handling of optional values prevents runtime crashes

The force unwrapping of imageBufferValue could crash if the MLFeatureValue creation
fails. Consider using optional binding.

Sources/SmileID/Classes/FaceDetector/SelfieQualityDetector.swift [36]

-self.init(conv2d_193_input: try MLFeatureValue(cgImage: conv2d_193_input, pixelsWide: 120, pixelsHigh: 120, pixelFormatType: kCVPixelFormatType_32ARGB, options: nil).imageBufferValue!)
+guard let buffer = try MLFeatureValue(cgImage: conv2d_193_input, pixelsWide: 120, pixelsHigh: 120, pixelFormatType: kCVPixelFormatType_32ARGB, options: nil).imageBufferValue else {
+    throw NSError(domain: "SelfieQualityDetector", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to create image buffer"])
+}
+self.init(conv2d_193_input: buffer)
Suggestion importance[1-10]: 9

Why: The suggestion eliminates a force unwrap that could cause runtime crashes if MLFeatureValue creation fails. The proposed guard-let pattern with proper error handling is significantly safer.

9
Using running average prevents potential numeric overflow in calculations

The brightness calculation could overflow for large images since it accumulates
values in a Double before dividing. Consider using running average instead.

Sources/SmileID/Classes/FaceDetector/EnhancedFaceDetector.swift [200-205]

 for index in stride(from: 0, to: dataLength, by: bytesPerPixel) {
     let red = dataPointer[index]
     let green = dataPointer[index + 1]
     let blue = dataPointer[index + 2]
-    result += 0.299 * Double(red) + 0.587 * Double(green) + 0.114 * Double(blue)
+    result = result + (0.299 * Double(red) + 0.587 * Double(green) + 0.114 * Double(blue) - result) / Double(index/bytesPerPixel + 1)
 }
Suggestion importance[1-10]: 7

Why: The suggestion prevents potential numeric overflow in brightness calculations for large images, which could lead to incorrect results or crashes. The running average approach is more numerically stable.

7
Possible issue
Use weak self references in closures to prevent retain cycles

Consider using a weak reference to self in the Timer closure to prevent potential
retain cycles.

Sources/SmileID/Classes/SelfieCapture/EnhancedSmartSelfieViewModel.swift [239-249]

 Timer.scheduledTimer(
     timeInterval: 1,
     repeats: true
-) { _ in
+) { [weak self] _ in
+    guard let self = self else { return }
     self.elapsedGuideAnimationDelay += 1
     if self.elapsedGuideAnimationDelay == self.guideAnimationDelayTime {
         self.showGuideAnimation = true
         self.stopGuideAnimationDelayTimer()
     }
 }
Suggestion importance[1-10]: 8

Why: Strong reference to self in timer closure can create retain cycles, leading to memory leaks. Using weak self is a critical Swift best practice for memory management.

8
Early validation of critical dependencies during initialization prevents runtime errors

Consider using a guard statement to early return if selfieQualityModel is nil during
initialization, rather than silently continuing with a nil model that will cause
issues later.

Sources/SmileID/Classes/FaceDetector/EnhancedFaceDetector.swift [39-42]

-override init() {
+override init() throws {
     super.init()
-    selfieQualityModel = createImageClassifier()
+    guard let model = createImageClassifier() else {
+        throw FaceDetectorError.unableToLoadSelfieModel
+    }
+    selfieQualityModel = model
 }
Suggestion importance[1-10]: 8

Why: The suggestion addresses a critical issue where a nil model could cause runtime failures. Making the initializer throwing and validating the model early improves error handling and reliability.

8
Add logging for cases where camera session preset configuration fails

Consider adding error handling for the session preset configuration to handle cases
where the desired preset cannot be set.

Sources/SmileID/Classes/SelfieCapture/SelfieViewModel.swift [85-87]

 if cameraManager.session.canSetSessionPreset(.vga640x480) {
     cameraManager.session.sessionPreset = .vga640x480
+} else {
+    print("Warning: Unable to set camera session preset to VGA resolution")
 }
Suggestion importance[1-10]: 5

Why: The suggestion improves error handling by adding logging for configuration failures, making it easier to diagnose camera setup issues.

5
Best practice
Add guard statement to handle weak self unwrapping to prevent force unwrapping of optional self

Use weak self in the sink closure for cameraManager.sampleBufferPublisher to prevent
potential retain cycles.

Sources/SmileID/Classes/SelfieCapture/SelfieViewModel.swift [95-107]

 cameraManager.sampleBufferPublisher
     .merge(with: arKitFramePublisher)
     .throttle(
         for: 0.35, scheduler: DispatchQueue.global(qos: .userInitiated),
         latest: true
     )
     .dropFirst(5)
     .compactMap { $0 }
     .sink { [weak self] imageBuffer in
-        self?.analyzeImage(image: imageBuffer)
+        guard let self else { return }
+        self.analyzeImage(image: imageBuffer)
     }
     .store(in: &subscribers)
Suggestion importance[1-10]: 7

Why: The suggestion improves memory management and safety by properly handling weak self unwrapping, preventing potential retain cycles and crashes from force unwrapping.

7
Improve error handling for IP address resolution failures

Add error handling for the case when the IP address lookup fails instead of silently
returning an empty string.

Sources/SmileID/Classes/Networking/Models/v2/Metadata.swift [205-206]

 func getIPAddress(useIPv4: Bool) -> String {
-    var address: String = ""
+    var address: String = "unknown"
Suggestion importance[1-10]: 4

Why: Using a default value of "unknown" instead of an empty string makes it clearer when IP address resolution fails, though the impact is relatively minor.

4
Enhancement
Add proper error handling for graphics context failures

Consider adding error handling for the case when image flipping fails instead of
returning nil silently.

Sources/SmileID/Classes/SelfieCapture/EnhancedSmartSelfieViewModel.swift [313-315]

 guard let context = UIGraphicsGetCurrentContext() else {
+    handleError(SmileIDError.unknown("Failed to get graphics context for image flipping"))
     return nil
 }
Suggestion importance[1-10]: 6

Why: Adding explicit error handling for graphics context failures improves error traceability and debugging capabilities, making the code more robust and maintainable.

6
Improve error message clarity by providing more specific failure details

Consider using a more descriptive error message when throwing the selfie capture
failure error.

Sources/SmileID/Classes/SelfieCapture/SelfieViewModel.swift [391-393]

 guard let selfieImage, livenessImages.count == numLivenessImages else {
-    throw SmileIDError.unknown("Selfie capture failed")
+    throw SmileIDError.unknown("Selfie capture failed: Missing selfie image or incorrect number of liveness images")
 }
Suggestion importance[1-10]: 4

Why: Adding more descriptive error messages helps with debugging and troubleshooting, though it's a minor improvement that doesn't affect functionality.

4

@jumaallan jumaallan merged commit a1c259a into main Dec 13, 2024
3 checks passed
@jumaallan jumaallan deleted the new-smart-selfie-capture branch December 13, 2024 15:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants