active_face_liveness 0.1.0 copy "active_face_liveness: ^0.1.0" to clipboard
active_face_liveness: ^0.1.0 copied to clipboard

On-device active face liveness for Flutter using RGBW illumination, eye response, head turns, parallax, and replay evidence.

active_face_liveness #

On-device active face liveness for Flutter using a randomized RGBW illumination challenge, eye response, head turns, parallax, screen/replay evidence, and multi-region face colour correlation.

The package includes a ready-to-use scanner UI and returns a typed result with optional PNG bytes, Base64, both representations, or no image.

This package is an experimental liveness signal, not certified Presentation Attack Detection (PAD). Do not use it as the only control for high-risk identity, financial, or KYC workflows.

Features #

  • Front-camera-only active liveness flow
  • Randomized full-intensity RGBW sequence lasting two seconds
  • Blink or squint response bound to the white pulse
  • Multi-region temporal colour correlation
  • Two head turns with configurable continuous hold time
  • Nose/cheek motion coherence and temporal parallax
  • Presentation-media and repeated-frame evidence
  • Automatic application-brightness restoration
  • Best full-frame capture selected during the scan
  • Configurable capture contrast and maximum output size
  • PNG bytes, Base64, both, or disabled image output
  • Typed configuration, evidence, and result objects

Platform support #

  • Android
  • iOS

All face and object processing is performed on the device.

Installation #

dependencies:
  active_face_liveness: ^0.1.0

Android #

Add camera permission to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />

iOS #

Add a usage description to ios/Runner/Info.plist:

<key>NSCameraUsageDescription</key>
<string>Camera access is required to perform the liveness scan.</string>

Basic usage #

If the application only needs a true/false answer, integration is one line:

final isLive = await ActiveFaceLiveness.verify(context);

verify uses the privacy-first preset and does not encode or return a face image.

Use start when the application needs failure details, evidence, or an image:

import 'package:active_face_liveness/active_face_liveness.dart';

final result = await ActiveFaceLiveness.start(context);

if (result.isLive) {
  print('Verified as a real person');
} else if (result.cancelled) {
  print('The user cancelled the scan');
} else {
  print(result.failureReason ?? 'Liveness could not be verified');
}

ActiveFaceLiveness.start requests camera permission, chooses the first front-facing camera, opens the scanner route, restores application brightness, and returns a LivenessResult.

Configure capture contrast and output #

Contrast is applied only to the returned image. It never changes the camera pixels used by the liveness decision.

final result = await ActiveFaceLiveness.start(
  context,
  config: const LivenessConfig(
    capturedImageContrast: 1.25,
    capturedImageMaxLongEdge: 1024,
    imageOutput: LivenessImageOutput.both,
    outsideFaceGuideColor: Color(0x66000000),
  ),
);

final pngBytes = result.pngBytes;
final base64Png = result.base64Image;

UI, header language, and progress #

final result = await ActiveFaceLiveness.start(
  context,
  config: const LivenessConfig(
    strings: LivenessStrings.english(),
    ui: LivenessUiConfig(
      primaryColor: Color(0xFF006C66),
      showStepNumber: true,
    ),
  ),
  onStepChanged: (step) => analytics.logEvent(name: step.name),
);

LivenessResult.failureCode is a stable machine-readable error. Results and evidence can be serialized without including biometric image data:

final payload = result.toJson(); // Does not contain the captured image.

The Base64 value contains the same PNG payload and does not include a data:image/png;base64, prefix.

Image output modes #

Value pngBytes base64Image Encoding performed
LivenessImageOutput.none No No No
LivenessImageOutput.pngBytes Yes No Yes
LivenessImageOutput.base64 No Yes Yes
LivenessImageOutput.both Yes Yes Yes

Configuration #

Property Default Description
imageOutput pngBytes Returned image representation
capturedImageContrast 1.0 Output-only contrast from 0.0 to 3.0
capturedImageMaxLongEdge 800 Maximum long edge in pixels
scanTimeout 75 seconds Maximum duration of one scan
turnHoldDuration 1 second Required hold for each head turn
farFaceMinFraction 0.20 Minimum initial face-width fraction
farFaceMaxFraction 0.42 Maximum initial face-width fraction
nearFaceMinFraction 0.50 Minimum near-stage fraction
nearFaceMaxFraction 0.90 Maximum near-stage fraction
screenBrightness 1.0 Application brightness during RGBW
illuminationOpacity 0.94 RGBW overlay opacity
outsideFaceGuideColor Color(0x66000000) Colour outside the face guide; use null to disable
antiSpoofScoreThreshold 0.58 Minimum combined evidence score

Changing detection thresholds can materially affect false accepts and false rejects. Calibrate them on every supported device family and lighting range.

Result #

final LivenessResult result = await ActiveFaceLiveness.start(context);

result.isLive;
result.classification;
result.cancelled;
result.failureReason;
result.pngBytes;
result.base64Image;
result.imageWidth;
result.imageHeight;
result.imageSharpness;
result.evidence;
result.technicalDetails;

High-level evidence is available without parsing the diagnostic map:

final evidence = result.evidence;

print(evidence.lightChallengePassed);
print(evidence.blinkChallengePassed);
print(evidence.firstTurnPassed);
print(evidence.secondTurnPassed);
print(evidence.parallaxPassed);
print(evidence.antiSpoofScore);

Direct widget usage #

Applications that already manage cameras and permissions can open ActiveFaceLivenessView directly:

final result = await Navigator.of(context).push<LivenessResult>(
  MaterialPageRoute(
    builder: (_) => ActiveFaceLivenessView(
      cameraDescription: frontCamera,
      config: const LivenessConfig(
        imageOutput: LivenessImageOutput.pngBytes,
      ),
    ),
  ),
);

How the flow works #

flowchart TD
    A["Open front camera"] --> B["Initial face distance and media check"]
    B --> C["Move closer and calibrate exposure"]
    C --> D["Random RGBW challenge for 2 seconds"]
    D --> E{"Colour response + white-pulse eye response pass?"}
    E -- "No" --> X["Return failed result"]
    E -- "Yes" --> F["Hold first head turn"]
    F --> G["Hold opposite head turn"]
    G --> H{"Motion coherence and parallax pass?"}
    H -- "No" --> X
    H -- "Yes" --> I["Fuse anti-spoof evidence"]
    I --> J{"Mandatory gates and score pass?"}
    J -- "No" --> X
    J -- "Yes" --> K["Return real-person result"]
    X --> L["Restore brightness and return best capture"]
    K --> L

Image handling and privacy #

The package keeps only the highest-scoring eligible frame in memory. It rotates and mirrors the complete camera frame, scales it while preserving aspect ratio, applies the configured output contrast, and encodes it as PNG. It does not write the capture to the gallery or another persistent file.

The integrating application is responsible for consent, privacy notices, retention, encryption, transport security, and deletion.

Running the example #

cd example
flutter run

The example lets you change contrast and select PNG, Base64, both, or no image before starting a scan.

0
likes
150
points
42
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

On-device active face liveness for Flutter using RGBW illumination, eye response, head turns, parallax, and replay evidence.

Repository (GitHub)
View/report issues

Topics

#liveness #face-detection #anti-spoofing #biometrics #on-device

License

MIT (license)

Dependencies

camera, face_detection_tflite, flutter, flutter_litert, object_detection, screen_brightness

More

Packages that depend on active_face_liveness