Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/android-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_call:

jobs:
android-tests:
Expand Down
49 changes: 49 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Publish to GitHub Packages

on:
workflow_dispatch:

jobs:
run-tests:
name: Run Test Suite
uses: ./.github/workflows/android-tests.yml
secrets: inherit

publish:
needs: run-tests
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "17"

- name: Cache Gradle packages
uses: actions/cache@v3
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, there was a v4 of this action, not sure how critical the changes are.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will leave it at v3 for now to be consistent with the other workflows

with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Build libraries
run: ./gradlew build
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this build both libraries or only SimFace?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should build both; that's how it worked when running it locally


- name: Publish to GitHub Packages
env:
USERNAME: ${{ github.actor }}
TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew publish
25 changes: 14 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ detection and [EdgeFace](https://github.com/otroshi/edgeface) for embedding (tem
2. Embedding Creation: This module is used to generate a 512-float vector representation of face images.
3. Matching and Identification: Used for verification and identification purposes.

## SimQ Library

**SimQ** is a standalone Android library for comprehensive face quality assessment. It can be used independently or as part of SimFace for enhanced quality evaluation. SimQ provides a quality score from 0.0 to 1.0, with customizable weights for each metric and configurable thresholds.

**📚 [View Full SimQ Documentation](simq/README.md)** for installation, usage examples, and advanced configuration options.

## Include the library into the project.

### Option 1 (Recommended)

1. Add the repository to your ```settings.gradle.kts``` under ```dependencyResolutionManagement``` under ```respositories```:
1. Add the repository to your `settings.gradle.kts` under `dependencyResolutionManagement` under `respositories`:

```kotlin
maven {
Expand All @@ -25,10 +31,10 @@ maven {
}
```

2. Import the dependencies in ```build.gradle.kts```:
2. Import the dependencies in `build.gradle.kts`:

```kotlin
implementation("com.simprints.biometrics:simface:2025.2.0")
implementation("com.simprints.biometrics:simface:2025.4.0")
```

## Implement the functionality.
Expand Down Expand Up @@ -93,34 +99,31 @@ simFace.getFaceDetectionProcessor().detectFace(faceImage, onSuccess = { faces ->
### Face Detection and Embedding

We first initialize the library. Then we can use the
```simFace.detectFace``` method to detect faces in images and evaluate their quality.
`simFace.detectFace` method to detect faces in images and evaluate their quality.
We can repeat this process multiple times until a sufficiently good face image is selected.

Afterwards, we can use the ```simFace.getEmbedding``` method to obtain a vector template
Afterwards, we can use the `simFace.getEmbedding` method to obtain a vector template
from the selected image. The embedding is represented by a 512 float array.

### Verification and Identification

The same steps are taken to initialize the library.

Then, the matching of two templates is carried out using the ```simFace.verificationScore``` method,
Then, the matching of two templates is carried out using the `simFace.verificationScore` method,
which returns a score in the [0, 1] range, being 1 a perfect match.

Identification can be carried using the ```simFace.identificationScore``` method which returns a mapping of the
Identification can be carried using the `simFace.identificationScore` method which returns a mapping of the
referenceVectors to the the score with respect to the probe.

Both methods use the cosine similarity between vectors as a measure of the score.

## System Requirements

The library works with a minimum version of Android 6.0 (API Level 23). It has been tested and runs
smoothly on *Samsung Galaxy A03 Core* which has the following specifications:
smoothly on _Samsung Galaxy A03 Core_ which has the following specifications:

- Android 11
- 1.6GHz Octa-core
- 2GB RAM
- 8MP f/2.0 Camera
- 32GB Storage



2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {

val projectGroupId = "com.simprints.biometrics"
val projectArtifactId = "simface"
val projectVersion = "2025.3.2"
val projectVersion = "2025.4.0"

group = projectGroupId
version = projectVersion
Expand Down
120 changes: 120 additions & 0 deletions simq/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# SimQ - Face Quality Assessment

SimQ is an Android library for assessing the quality of face images. It analyzes multiple quality metrics to provide a comprehensive quality score between 0.0 and 1.0.

## Features

SimQ evaluates face images based on four key metrics:

- **Alignment**: Evaluates face pose angles (pitch, yaw, roll) and eye state
- **Blur**: Measures image sharpness using Laplacian variance
- **Brightness**: Assesses image luminance levels
- **Contrast**: Evaluates pixel intensity variation

## Installation

Add the dependency to your `build.gradle.kts`:

```kotlin
implementation("com.simprints.biometrics:simq:2025.4.0")
```

## Basic Usage

```kotlin
// Initialize SimQ with default parameters
val simQ = SimQ()

// Calculate quality score for a face image
val qualityScore = simQ.calculateFaceQuality(
bitmap = faceBitmap,
pitch = 5.0,
yaw = -3.0,
roll = 0.0
)

// Quality score ranges from 0.0 (poor) to 1.0 (excellent)
if (qualityScore >= 0.6) {
// Image quality is sufficient
}
```

## Advanced Usage

### Custom Weights

Customize how much each metric contributes to the final score:

```kotlin
val customWeights = QualityWeights(
alignment = 0.25,
blur = 0.35,
brightness = 0.25,
contrast = 0.10,
eyeOpenness = 0.05
)

val simQ = SimQ(faceWeights = customWeights)
```

### Custom Parameters

Adjust the thresholds for each quality metric:

```kotlin
val customParameters = QualityParameters(
maxAlignmentAngle = 15.0,
minBlur = 60_000.0,
maxBlur = 120_000.0,
minBrightness = 40.0,
optimalBrightnessLow = 90.0,
optimalBrightnessHigh = 140.0,
maxBrightness = 180.0,
minContrast = 35.0,
maxContrast = 50.0
)

val simQ = SimQ(faceParameters = customParameters)
```

## Parameters Reference

### calculateFaceQuality()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine for now, but I would suggest adding Dokka and generating documentation based on code comments, as this will get outdated sooner or later.


| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `bitmap` | `Bitmap` | Required | The cropped face image |
| `pitch` | `Double` | `0.0` | Face pitch angle (head nod) in degrees |
| `yaw` | `Double` | `0.0` | Face yaw angle (head rotation) in degrees |
| `roll` | `Double` | `0.0` | Face roll angle (head tilt) in degrees |
| `leftEyeOpenness` | `Double?` | `null` | Left eye openness probability (0.0-1.0) |
| `rightEyeOpenness` | `Double?` | `null` | Right eye openness probability (0.0-1.0) |
| `centerCrop` | `Float` | `0.5f` | Fraction of image to analyze (0.0-1.0) |
| `horizontalDisplacement` | `Float` | `0.0f` | Horizontal shift for center crop (-1.0 to 1.0) |
| `verticalDisplacement` | `Float` | `0.0f` | Vertical shift for center crop (-1.0 to 1.0) |

### QualityWeights (Default Values)

| Weight | Default | Description |
|--------|---------|-------------|
| `alignment` | `0.28` | Face pose contribution |
| `blur` | `0.30` | Sharpness contribution |
| `brightness` | `0.30` | Luminance contribution |
| `contrast` | `0.10` | Contrast contribution |
| `eyeOpenness` | `0.02` | Eye state contribution |

### QualityParameters (Default Values)

| Parameter | Default | Description |
|-----------|---------|-------------|
| `maxAlignmentAngle` | `20.0` | Maximum combined angle deviation |
| `maxIndividualAngle` | `25.0` | Maximum single angle deviation |
| `minBlur` | `50,000.0` | Minimum acceptable Laplacian variance |
| `maxBlur` | `100,000.0` | Optimal Laplacian variance |
| `minBrightness` | `30.0` | Minimum acceptable brightness (0-255) |
| `optimalBrightnessLow` | `80.0` | Lower bound of optimal brightness |
| `optimalBrightnessHigh` | `150.0` | Upper bound of optimal brightness |
| `maxBrightness` | `190.0` | Maximum acceptable brightness |
| `brightnessSteepness` | `0.3` | Brightness scoring curve steepness |
| `minContrast` | `30.0` | Minimum acceptable contrast (std dev) |
| `maxContrast` | `47.0` | Optimal contrast (std dev) |
41 changes: 41 additions & 0 deletions simq/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.jetbrains.kotlin.android)
`maven-publish`
}

val projectGroupId = "com.simprints.biometrics"
val projectArtifactId = "simq"
val projectVersion = "2025.4.0"

group = projectGroupId
version = projectVersion

android {
namespace = "com.simprints.simq"
compileSdk = 36
Expand Down Expand Up @@ -50,3 +58,36 @@ dependencies {
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(libs.kotlinx.coroutines.test)
}

publishing {
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/simprints/Biometrics-SimFace")
credentials {
username = project.findProperty("gpr.user") as String? ?: System.getenv("USERNAME")
password = project.findProperty("gpr.key") as String? ?: System.getenv("TOKEN")
}
}
}
publications {
create<MavenPublication>("ReleaseAar") {
groupId = projectGroupId
artifactId = projectArtifactId
version = projectVersion
afterEvaluate { artifact(tasks.getByName("bundleReleaseAar")) }

pom.withXml {
val dependenciesNode = asNode().appendNode("dependencies")

configurations.getByName("api").dependencies.map { dependency ->
dependenciesNode.appendNode("dependency").also {
it.appendNode("groupId", dependency.group)
it.appendNode("artifactId", dependency.name)
it.appendNode("version", dependency.version)
}
}
}
}
}
}
Loading