You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于ARKit与Vision的本地人脸图片身份认证实现问询

Local Image-Based Authentication for iOS (No Cloud Services)

Great question! Implementing local image-based authentication on iOS using modern frameworks like Vision (ditching outdated methods and cloud services like Amazon Rekognition) is totally doable. Let's walk through the full implementation step by step:

1. Account Registration Flow

When users create an account, let them select an image from their photo library and securely store it for future authentication checks.

Step 1: Let Users Select an Image

Use UIImagePickerController with sourceType = .photoLibrary to let users pick their auth image:

func presentPhotoLibraryPicker() {
    let picker = UIImagePickerController()
    picker.sourceType = .photoLibrary
    picker.delegate = self
    present(picker, animated: true)
}

// Handle the selected image
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    picker.dismiss(animated: true)
    guard let selectedImage = info[.originalImage] as? UIImage else { return }
    // Save the image securely
    saveAuthImage(selectedImage)
}

Step 2: Securely Store the Image

Never store sensitive auth data in plain files—use Keychain for encryption and device-bound storage:

func saveAuthImage(_ image: UIImage) {
    guard let imageData = image.jpegData(compressionQuality: 0.8) else { 
        print("Failed to convert image to data")
        return 
    }
    
    let keychainQuery: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrAccount as String: "UserAuthImage",
        kSecValueData as String: imageData,
        kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly // Prevents iCloud sync
    ]
    
    // Delete existing entry if it exists (for account updates)
    SecItemDelete(keychainQuery as CFDictionary)
    // Add the new image data to Keychain
    let status = SecItemAdd(keychainQuery as CFDictionary, nil)
    if status != errSecSuccess {
        print("Failed to save auth image to Keychain")
    }
}

2. Login & Camera Capture Flow

On login, use the device camera to capture a new image and compare it against the stored one.

Step 1: Present Camera Picker

Configure UIImagePickerController with sourceType = .camera:

func presentCameraLoginPicker() {
    guard UIImagePickerController.isSourceTypeAvailable(.camera) else {
        print("Camera not available")
        return
    }
    
    let picker = UIImagePickerController()
    picker.sourceType = .camera
    picker.delegate = self
    present(picker, animated: true)
}

// Handle captured image
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    picker.dismiss(animated: true)
    guard let capturedImage = info[.originalImage] as? UIImage else { return }
    // Start image comparison
    authenticateWithCapturedImage(capturedImage)
}

3. Core Image Matching with Vision

Apple's Vision framework provides robust, local image feature matching that's far more reliable than old pixel-comparison methods. Here's how to use it:

import Vision

func authenticateWithCapturedImage(_ capturedImage: UIImage) {
    // Retrieve the stored auth image from Keychain
    guard let savedImageData = getSavedAuthImageData(),
          let savedImage = UIImage(data: savedImageData),
          let savedCGImage = savedImage.cgImage,
          let capturedCGImage = capturedImage.cgImage else {
        print("No saved auth image found or invalid image format")
        handleAuthFailure()
        return
    }
    
    // Use a background queue to avoid blocking the main thread
    DispatchQueue.global(qos: .userInitiated).async {
        do {
            // Generate feature prints for both images
            let savedFeaturePrint = try self.generateImageFeaturePrint(from: savedCGImage)
            let capturedFeaturePrint = try self.generateImageFeaturePrint(from: capturedCGImage)
            
            // Calculate similarity distance (lower = more similar)
            var distance = Float(0)
            try savedFeaturePrint.computeDistance(&distance, to: capturedFeaturePrint)
            
            DispatchQueue.main.async {
                // Adjust this threshold based on your testing (0.7-0.9 works for most cases)
                let authThreshold: Float = 0.8
                if distance < authThreshold {
                    self.handleAuthSuccess()
                } else {
                    print("Authentication failed: distance = \(distance)")
                    self.handleAuthFailure()
                }
            }
        } catch {
            DispatchQueue.main.async {
                print("Error during image comparison: \(error.localizedDescription)")
                self.handleAuthFailure()
            }
        }
    }
}

// Helper to generate Vision feature prints
private func generateImageFeaturePrint(from cgImage: CGImage) throws -> VNImageFeaturePrintObservation {
    let request = VNGenerateImageFeaturePrintRequest()
    let handler = VNImageRequestHandler(cgImage: cgImage)
    try handler.perform([request])
    
    guard let featurePrint = request.results?.first as? VNImageFeaturePrintObservation else {
        throw NSError(domain: "AuthError", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to generate feature print"])
    }
    return featurePrint
}

// Helper to retrieve saved image from Keychain
private func getSavedAuthImageData() -> Data? {
    let keychainQuery: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrAccount as String: "UserAuthImage",
        kSecReturnData as String: kCFBooleanTrue!,
        kSecMatchLimit as String: kSecMatchLimitOne
    ]
    
    var data: AnyObject?
    let status = SecItemCopyMatching(keychainQuery as CFDictionary, &data)
    return status == errSecSuccess ? data as? Data : nil
}

// Placeholder methods for auth result handling
private func handleAuthSuccess() {
    print("Authentication successful!")
    // Navigate to main app flow
}

private func handleAuthFailure() {
    print("Authentication failed. Please try again.")
    // Show error message to user
}

Key Considerations

  • Security: Using Keychain ensures the auth image is encrypted and tied to the device (if you use kSecAttrAccessibleWhenUnlockedThisDeviceOnly). Avoid storing images in Documents or UserDefaults—these are not secure.
  • Threshold Tuning: Test the authThreshold value with real user scenarios. A lower value means stricter matching, while a higher value allows minor variations (like lighting changes).
  • Image Preprocessing: For better accuracy, you can preprocess images (crop to the same aspect ratio, normalize lighting) before generating feature prints.
  • ARKit Integration (Optional): If your use case is face-based authentication, you can combine Vision with ARKit's ARFaceTrackingConfiguration to capture 3D face data for even more precise matching.
  • Performance: Feature extraction is lightweight, but always run it on a background queue to keep the UI responsive.

内容的提问来源于stack exchange,提问作者Egghead

火山引擎 最新活动