capacitor-face-embedder
v0.0.5
Published
Native face embedding using ML models
Readme
capacitor-face-embedder
Native face embedding using ML models for Ionic / Capacitor apps.
Supports:
- Face embedding generation
- Face comparison (1:1)
- Face recognition (1:N)
- Embedding averaging for improved accuracy
Install
npm install capacitor-face-embedder
npx cap syncAPI
generateEmbedding
generateEmbedding(options: { image: string }) => Promise<{
embedding: number[]
}>getFaceEmbedding
getFaceEmbedding(options: { image: string }) => Promise<{
embedding: number[]
timeMillis: number
}>Generates a face embedding from a base64 image.
Plugin Setup
import { registerPlugin } from '@capacitor/core'
export type FaceEmbedding = number[]
export type FaceRecord = {
id: string
embedding: FaceEmbedding
}
export interface FaceEmbedderPlugin {
getFaceEmbedding(options: { image: string }): Promise<{
embedding: FaceEmbedding
timeMillis: number
}>
}
export const FaceEmbedder =
registerPlugin<FaceEmbedderPlugin>('FaceEmbedder')Utilities
Cosine Similarity
export const cosineSimilarity = (
a: FaceEmbedding,
b: FaceEmbedding
): number => {
if (a.length !== b.length) {
throw new Error('Embedding size mismatch')
}
let dot = 0
for (let i = 0; i < a.length; i++) {
dot += a[i] * b[i]
}
return dot
}Compare Faces (1:1)
export const compareFaces = (
emb1: FaceEmbedding,
emb2: FaceEmbedding,
threshold = 0.65
) => {
const similarity = cosineSimilarity(emb1, emb2)
return {
similarity,
isMatch: similarity >= threshold
}
}Find Best Match (1:N Recognition)
export const findBestMatch = (
target: FaceEmbedding,
database: FaceRecord[],
threshold = 0.65
) => {
let bestMatch = null
let highest = -1
for (const item of database) {
const sim = cosineSimilarity(target, item.embedding)
if (sim > highest) {
highest = sim
bestMatch = item
}
}
return {
match: highest >= threshold ? bestMatch : null,
similarity: highest
}
}Average Embeddings
export const averageEmbeddings = (embeddings: FaceEmbedding[]) => {
if (embeddings.length === 0) {
throw new Error('No embeddings provided')
}
const length = embeddings[0].length
const avg = new Array(length).fill(0)
for (const emb of embeddings) {
if (emb.length !== length) {
throw new Error('Embedding size mismatch in averaging')
}
for (let i = 0; i < length; i++) {
avg[i] += emb[i]
}
}
return avg.map(v => v / embeddings.length)
}Usage Example
import {
FaceEmbedder,
compareFaces
} from 'capacitor-face-embedder'
const runEmbedding = async () => {
const res1 = await FaceEmbedder.getFaceEmbedding({ image: img1 })
const res2 = await FaceEmbedder.getFaceEmbedding({ image: img2 })
const result = compareFaces(
res1.embedding,
res2.embedding,
0.65
)
console.log(result)
}Best Practice
Capture multiple angles:
- Center
- Left
- Right
- Up
- Down
Then average:
const finalEmbedding = averageEmbeddings(faceSamples)Recommended Thresholds
- High security: 0.70 – 0.75
- Balanced: 0.60 – 0.68
- Flexible: 0.55 – 0.60
