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
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.2.1"
val projectVersion = "2025.3.0"

android {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.simprints.simface
package com.simprints.biometrics

import android.content.Context
import android.graphics.Bitmap
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
package com.simprints.simface
package com.simprints.biometrics.simface.detection

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Rect
import androidx.test.core.app.*
import com.simprints.simface.core.SimFace
import com.simprints.simface.core.SimFaceConfig
import com.simprints.simface.core.Utils.IMAGE_SIZE
import com.simprints.simface.data.FaceDetection
import com.simprints.simface.quality.cropAlignFace
import com.simprints.simface.quality.warpAlignFace
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.simprints.biometrics.loadBitmapFromTestResources
import com.simprints.biometrics.simface.Constants
import com.simprints.biometrics.simface.SimFace
import com.simprints.biometrics.simface.SimFaceConfig
import com.simprints.biometrics.simface.data.FaceDetection
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class FaceAlignTest {
private lateinit var context: Context
private lateinit var simFace: SimFace
Expand Down Expand Up @@ -78,10 +80,10 @@ class FaceAlignTest {
assertTrue(warpedAlignedImage != null)

if (warpedAlignedImage != null) {
assertTrue(warpedAlignedImage.width == IMAGE_SIZE)
assertTrue(warpedAlignedImage.width == Constants.IMAGE_SIZE)
}
if (warpedAlignedImage != null) {
assertTrue(warpedAlignedImage.height == IMAGE_SIZE)
assertTrue(warpedAlignedImage.height == Constants.IMAGE_SIZE)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package com.simprints.simface
package com.simprints.biometrics.simface.detection

import android.content.Context
import android.graphics.Bitmap
import androidx.test.core.app.*
import androidx.test.ext.junit.runners.*
import com.simprints.simface.core.SimFace
import com.simprints.simface.core.SimFaceConfig
import com.simprints.simface.data.FaceDetection
import com.simprints.biometrics.loadBitmapFromTestResources
import com.simprints.biometrics.simface.SimFace
import com.simprints.biometrics.simface.SimFaceConfig
import com.simprints.biometrics.simface.data.FaceDetection
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Assert
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.simprints.simface
package com.simprints.biometrics.simface.embedding

import android.content.Context
import android.graphics.Bitmap
import androidx.test.core.app.*
import com.simprints.simface.core.SimFace
import com.simprints.simface.core.SimFaceConfig
import com.simprints.simface.core.Utils
import com.simprints.biometrics.loadBitmapFromTestResources
import com.simprints.biometrics.openTestModelFile
import com.simprints.biometrics.simface.SimFaceConfig
import com.simprints.biometrics.simface.Utils
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Assert.assertArrayEquals
Expand All @@ -20,25 +21,25 @@ import org.junit.Test
* 4. Do the testing
*/
class CustomModelTest {
private lateinit var simFace: SimFace
private lateinit var context: Context
private lateinit var modelManager: MLModelManager
private lateinit var embeddingProcessor: EmbeddingProcessor

@Before
fun setup() {
context = ApplicationProvider.getApplicationContext()
simFace = SimFace()
}

@After
fun cleanup() {
simFace.release()
modelManager.close()
}

@Test
fun test_processes_face_with_custom_model() = runTest {
val testModelFile = context.openTestModelFile()

simFace.initialize(
modelManager = MLModelManager(
SimFaceConfig(
context,
customModel = SimFaceConfig.CustomModel(
Expand All @@ -47,13 +48,15 @@ class CustomModelTest {
),
),
)
embeddingProcessor = TensorFlowEmbeddingProcessor(modelManager)

val bitmap: Bitmap = context.loadBitmapFromTestResources("royalty_free_good_face")
val resultFloat = getFaceEmbeddingFromBitmap(bitmap)

assertArrayEquals(GOOD_FACE_EMBEDDING, resultFloat, 0.1F)
}

private fun getFaceEmbeddingFromBitmap(bitmap: Bitmap): FloatArray = simFace
private fun getFaceEmbeddingFromBitmap(bitmap: Bitmap): FloatArray = embeddingProcessor
.getEmbedding(bitmap)
.let { Utils.byteArrayToFloatArray(it) }
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.simprints.simface
package com.simprints.biometrics.simface.embedding

import android.content.Context
import android.graphics.Bitmap
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.simprints.simface.core.SimFace
import com.simprints.simface.core.SimFaceConfig
import com.simprints.simface.core.Utils
import androidx.test.core.app.*
import androidx.test.ext.junit.runners.*
import com.simprints.biometrics.loadBitmapFromTestResources
import com.simprints.biometrics.simface.SimFaceConfig
import com.simprints.biometrics.simface.Utils
import org.junit.After
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertTrue
Expand All @@ -16,25 +16,26 @@ import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class EmbeddingProcessorTest {
private lateinit var simFace: SimFace
private lateinit var context: Context
private lateinit var modelManager: MLModelManager
private lateinit var embeddingProcessor: EmbeddingProcessor

@Before
fun setup() {
context = ApplicationProvider.getApplicationContext()
simFace = SimFace()
simFace.initialize(SimFaceConfig(context))
modelManager = MLModelManager(SimFaceConfig(context))
embeddingProcessor = TensorFlowEmbeddingProcessor(modelManager)
}

@After
fun cleanup() {
simFace.release()
modelManager.close()
}

@Test
fun get_embedding_with_image() {
val bitmap: Bitmap = context.loadBitmapFromTestResources("royalty_free_good_face")
val result = simFace.getEmbedding(bitmap)
val result = embeddingProcessor.getEmbedding(bitmap)
val resultFloat = Utils.byteArrayToFloatArray(result)

assertTrue(Utils.byteArrayToFloatArray(result).size == 512)
Expand All @@ -48,8 +49,8 @@ class EmbeddingProcessorTest {
val bitmap1: Bitmap = context.loadBitmapFromTestResources("royalty_free_good_face")
val bitmap2: Bitmap = context.loadBitmapFromTestResources("royalty_free_bad_face")

val embedding1 = simFace.getEmbedding(bitmap1)
val embedding2 = simFace.getEmbedding(bitmap2)
val embedding1 = embeddingProcessor.getEmbedding(bitmap1)
val embedding2 = embeddingProcessor.getEmbedding(bitmap2)

assertTrue(!embedding1.contentEquals(embedding2)) // Embeddings should be different
}
Expand All @@ -58,8 +59,8 @@ class EmbeddingProcessorTest {
fun consistency_test_with_same_image() {
val bitmap: Bitmap = context.loadBitmapFromTestResources("royalty_free_good_face")

val embedding1 = simFace.getEmbedding(bitmap)
val embedding2 = simFace.getEmbedding(bitmap)
val embedding1 = embeddingProcessor.getEmbedding(bitmap)
val embedding2 = embeddingProcessor.getEmbedding(bitmap)

assertArrayEquals(embedding1, embedding2) // Embeddings should be identical
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.simprints.simface
package com.simprints.biometrics.simface.embedding

// Define the expected output for our image (the output is computed on Android)
val GOOD_FACE_EMBEDDING = floatArrayOf(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.simprints.biometrics.simface.matcher

import androidx.test.ext.junit.runners.*
import com.simprints.biometrics.simface.Utils.floatArrayToByteArray
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class IdentificationTest {
private lateinit var matchProcessor: MatchProcessor

@Before
fun setup() {
matchProcessor = CosineDistanceMatchProcessor()
}

@Test
fun score_map_should_be_ordered_by_distance() {
val referenceArray = floatArrayToByteArray(floatArrayOf(1.0f, 0.0f))
val arrayList = listOf(
floatArrayToByteArray(floatArrayOf(-1.0f, 0.0f)), // opposite to referenceArray
floatArrayToByteArray(floatArrayOf(1.0f, 0.0f)), // identical to referenceArray
floatArrayToByteArray(floatArrayOf(0.0f, 1.0f)), // orthogonal to referenceArray
floatArrayToByteArray(floatArrayOf(0.707f, 0.707f)), // 45 degrees to referenceArray
)

val sortedScores = matchProcessor.identificationScore(referenceArray, arrayList)
val sortedDistances = sortedScores.map { it.second }

// Closest match (identical vector) should have a score of 1
assertEquals(1.0, sortedDistances[0], 0.0001)

// 45-degree vector (second closest) should have a score of around 0.85355
assertEquals(0.85355, sortedDistances[1], 0.0001)

// Orthogonal vector (further away) should have a score of 0.5
assertEquals(0.5, sortedDistances[2], 0.0001)

// Opposite vector (furthest away) should have a score of 0.0
assertEquals(0.0, sortedDistances[3], 0.0001)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.simprints.biometrics.simface.matcher

import androidx.test.ext.junit.runners.*
import com.simprints.biometrics.simface.Utils.floatArrayToByteArray
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class VerificationTest {
private lateinit var matchProcessor: MatchProcessor

@Before
fun setup() {
matchProcessor = CosineDistanceMatchProcessor()
}

@Test
fun score_between_identical_vectors_should_be_one() {
val array1 = floatArrayToByteArray(floatArrayOf(1.0f, 0.0f, 0.0f))
val array2 = floatArrayToByteArray(floatArrayOf(1.0f, 0.0f, 0.0f))

val distance = matchProcessor.verificationScore(array1, array2)

assertEquals(1.0, distance, 0.0001)
}

@Test
fun score_between_orthogonal_vectors_should_be_one_half() {
val array1 = floatArrayToByteArray(floatArrayOf(1.0f, 0.0f))
val array2 = floatArrayToByteArray(floatArrayOf(0.0f, 1.0f))

val distance = matchProcessor.verificationScore(array1, array2)

assertEquals(0.5, distance, 0.0001)
}

@Test
fun score_between_opposite_vectors_should_be_zero() {
val array1 = floatArrayToByteArray(floatArrayOf(1.0f, 0.0f))
val array2 = floatArrayToByteArray(floatArrayOf(-1.0f, 0.0f))

val distance = matchProcessor.verificationScore(array1, array2)

assertEquals(0.0, distance, 0.0001)
}

@Test
fun score_between_arbitrary_vectors_should_be_between_zero_and_one() {
val array1 = floatArrayToByteArray(floatArrayOf(1.0f, 2.0f, 3.0f))
val array2 = floatArrayToByteArray(floatArrayOf(4.0f, 5.0f, 6.0f))

val distance = matchProcessor.verificationScore(array1, array2)

Assert.assertTrue(distance > 0.0 && distance < 1.0)
}
}
56 changes: 0 additions & 56 deletions src/androidTest/java/com/simprints/simface/IdentificationTest.kt

This file was deleted.

Loading
Loading