
On-device face and Indonesian e-KTP detection with YuNet face detector, passive liveness, quality checks, KTP rectification, and selfie-to-KTP matching via swappable inference backend.
On-device face and Indonesian e-KTP detection for Kotlin Multiplatform.
Faktel is an open-source Kotlin Multiplatform (KMP) library that runs computer-vision pipelines on the device - no cloud round trip, no vendor CV SDK. One shared Kotlin API for Android, iOS and desktop JVM.
Status: pre-1.0 (
0.x). The API can still change between minor versions; every change is recorded in the CHANGELOG. See Limitations before using it for anything security-sensitive.
| Capability | API | Backed by | Needs a model? |
|---|---|---|---|
| Face detection + 5 landmarks | YuNetFaceDetector |
YuNet (MIT) | yes - bundled in repo |
| Face quality gate (size, blur, exposure, pose, cut-off) | FaceQualityAssessor |
pure Kotlin | no |
| Passive liveness (print/replay attacks) | MiniFasNetLivenessDetector |
MiniFASNetV2 (Apache-2.0) | yes - bundled in repo |
| Face embedding + selfie-to-KTP matching |
ArcFaceEmbedder, FaceMatcher
|
any ArcFace-style ONNX | yes - bring your own (why) |
| KTP card localisation |
ClassicalKtpDetector (or your own KtpDetector) |
pure Kotlin | no |
| KTP rectification + validation (aspect ratio 1.586, size, blur, glare, portrait placement) | KtpScanner |
pure Kotlin + face detector | optional |
| Camera-frame conversion (NV21 / YUV_420_888 / BGRA / RGBA, rotate, flip) | RgbImage.from* |
pure Kotlin | no |
All heavy math runs through one small seam, InferenceEngine, so the ML runtime is swappable.
| Artifact | Contents | Targets |
|---|---|---|
faktel |
Umbrella: depends on everything below; also the iOS Faktel.xcframework
|
Android, iOS, JVM |
faktel-core |
Image type, geometry, preprocessing, quality metrics, InferenceEngine interface |
Android, iOS, JVM |
faktel-face |
Detector, quality, liveness, embedding, matching | Android, iOS, JVM |
faktel-ktp |
KTP detection, rectification, validation | Android, iOS, JVM |
faktel-ort |
ONNX Runtime backend (Android + JVM; iOS via Swift package) | Android, JVM (+ Swift) |
Group ID: io.github.cybersafetyid.faktel. Details: architecture.
// build.gradle.kts of your shared module
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.cybersafetyid.faktel:faktel:<version>")
}
}
}Then wire the platform pieces (one-time): Android - iOS - Desktop JVM.
Shared code depends only on the InferenceEngine interface:
class SelfieChecker(engine: InferenceEngine, yunet: ByteArray, fas: ByteArray) {
private val analyzer = FaceAnalyzer(
detector = YuNetFaceDetector(engine, yunet),
liveness = MiniFasNetLivenessDetector(engine, fas),
)
fun check(frame: RgbImage): FaceAnalysis = analyzer.analyze(frame) // detect -> quality -> liveness
}val scanner = KtpScanner(faceDetector = YuNetFaceDetector(engine, yunet))
val scan = scanner.scan(photo)
if (scan.isAcceptable) {
val card = scan.card!! // upright 1011x638 image
val portrait = scan.portrait!! // face box in card coordinates
} else {
showHint(scan.issues) // e.g. [BLURRY, GLARE] -> "hold steady, avoid reflections"
}Selfie-to-KTP match: docs/guides/selfie-ktp-matching.md.
Start at docs/README.md. Highlights: Getting started - Architecture - Face guide - KTP guide - Models & licences - Tuning thresholds - Privacy & security - Troubleshooting - API reference.
KtpDetector interface is where it plugs in.Unit tests run on JVM and the iOS simulator in CI. The real models (YuNet, MiniFASNetV2, an ArcFace MobileFaceNet) were exercised end-to-end through ONNX Runtime on desktop JVM and on an iPhone simulator with matching results. On-device Android runtime (as opposed to compilation) is exercised by your app; see docs/development/testing.md for what is and is not covered automatically.
Contributions are welcome - bug reports, docs, thresholds from real devices, new backends, a learned KTP detector. Read CONTRIBUTING.md and the Code of Conduct. Versioning follows SemVer. Security issues: SECURITY.md.
Apache License 2.0. Bundled model weights keep their own licences - see models/MODELS.md and NOTICE.
On-device face and Indonesian e-KTP detection for Kotlin Multiplatform.
Faktel is an open-source Kotlin Multiplatform (KMP) library that runs computer-vision pipelines on the device - no cloud round trip, no vendor CV SDK. One shared Kotlin API for Android, iOS and desktop JVM.
Status: pre-1.0 (
0.x). The API can still change between minor versions; every change is recorded in the CHANGELOG. See Limitations before using it for anything security-sensitive.
| Capability | API | Backed by | Needs a model? |
|---|---|---|---|
| Face detection + 5 landmarks | YuNetFaceDetector |
YuNet (MIT) | yes - bundled in repo |
| Face quality gate (size, blur, exposure, pose, cut-off) | FaceQualityAssessor |
pure Kotlin | no |
| Passive liveness (print/replay attacks) | MiniFasNetLivenessDetector |
MiniFASNetV2 (Apache-2.0) | yes - bundled in repo |
| Face embedding + selfie-to-KTP matching |
ArcFaceEmbedder, FaceMatcher
|
any ArcFace-style ONNX | yes - bring your own (why) |
| KTP card localisation |
ClassicalKtpDetector (or your own KtpDetector) |
pure Kotlin | no |
| KTP rectification + validation (aspect ratio 1.586, size, blur, glare, portrait placement) | KtpScanner |
pure Kotlin + face detector | optional |
| Camera-frame conversion (NV21 / YUV_420_888 / BGRA / RGBA, rotate, flip) | RgbImage.from* |
pure Kotlin | no |
All heavy math runs through one small seam, InferenceEngine, so the ML runtime is swappable.
| Artifact | Contents | Targets |
|---|---|---|
faktel |
Umbrella: depends on everything below; also the iOS Faktel.xcframework
|
Android, iOS, JVM |
faktel-core |
Image type, geometry, preprocessing, quality metrics, InferenceEngine interface |
Android, iOS, JVM |
faktel-face |
Detector, quality, liveness, embedding, matching | Android, iOS, JVM |
faktel-ktp |
KTP detection, rectification, validation | Android, iOS, JVM |
faktel-ort |
ONNX Runtime backend (Android + JVM; iOS via Swift package) | Android, JVM (+ Swift) |
Group ID: io.github.cybersafetyid.faktel. Details: architecture.
// build.gradle.kts of your shared module
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.cybersafetyid.faktel:faktel:<version>")
}
}
}Then wire the platform pieces (one-time): Android - iOS - Desktop JVM.
Shared code depends only on the InferenceEngine interface:
class SelfieChecker(engine: InferenceEngine, yunet: ByteArray, fas: ByteArray) {
private val analyzer = FaceAnalyzer(
detector = YuNetFaceDetector(engine, yunet),
liveness = MiniFasNetLivenessDetector(engine, fas),
)
fun check(frame: RgbImage): FaceAnalysis = analyzer.analyze(frame) // detect -> quality -> liveness
}val scanner = KtpScanner(faceDetector = YuNetFaceDetector(engine, yunet))
val scan = scanner.scan(photo)
if (scan.isAcceptable) {
val card = scan.card!! // upright 1011x638 image
val portrait = scan.portrait!! // face box in card coordinates
} else {
showHint(scan.issues) // e.g. [BLURRY, GLARE] -> "hold steady, avoid reflections"
}Selfie-to-KTP match: docs/guides/selfie-ktp-matching.md.
Start at docs/README.md. Highlights: Getting started - Architecture - Face guide - KTP guide - Models & licences - Tuning thresholds - Privacy & security - Troubleshooting - API reference.
KtpDetector interface is where it plugs in.Unit tests run on JVM and the iOS simulator in CI. The real models (YuNet, MiniFASNetV2, an ArcFace MobileFaceNet) were exercised end-to-end through ONNX Runtime on desktop JVM and on an iPhone simulator with matching results. On-device Android runtime (as opposed to compilation) is exercised by your app; see docs/development/testing.md for what is and is not covered automatically.
Contributions are welcome - bug reports, docs, thresholds from real devices, new backends, a learned KTP detector. Read CONTRIBUTING.md and the Code of Conduct. Versioning follows SemVer. Security issues: SECURITY.md.
Apache License 2.0. Bundled model weights keep their own licences - see models/MODELS.md and NOTICE.