compareFaces static method

FaceComparisonResult compareFaces(
  1. Float64List embedding1,
  2. Float64List embedding2
)

Compare two face embeddings using Euclidean distance (FaceNet standard) For normalized embeddings, Euclidean distance and cosine similarity are related: Euclidean distance = sqrt(2 * (1 - cosine_similarity))

Implementation

static FaceComparisonResult compareFaces(
  Float64List embedding1,
  Float64List embedding2,
) {
  // Calculate Euclidean distance (standard for FaceNet)
  double sumSquared = 0.0;
  final len = min(embedding1.length, embedding2.length);

  for (int i = 0; i < len; i++) {
    final diff = embedding1[i] - embedding2[i];
    sumSquared += diff * diff;
  }
  final euclideanDistance = sqrt(sumSquared);

  // For L2-normalized embeddings, convert to cosine similarity
  // cosine_similarity = 1 - (euclidean_distance^2 / 2)

  // Convert to similarity percentage
  // FaceNet thresholds (from original paper and common practice):
  // - Euclidean distance < 0.6: same person (typically ~1.1 for 99% accuracy)
  // - For normalized embeddings, distance < 1.1 is typically same person
  // We use a more conservative threshold

  double similarityPercentage;

  // Using Euclidean distance for thresholding (more reliable for FaceNet)
  // Typical thresholds: 0.6-1.1 for same person
  if (euclideanDistance < 0.6) {
    // Very high similarity (same person, very confident)
    similarityPercentage = 100.0 - (euclideanDistance / 0.6) * 10.0; // 90-100%
  } else if (euclideanDistance < 0.8) {
    // High similarity (likely same person)
    similarityPercentage = 90.0 - ((euclideanDistance - 0.6) / 0.2) * 15.0; // 75-90%
  } else if (euclideanDistance < 1.0) {
    // Good similarity (probably same person)
    similarityPercentage = 75.0 - ((euclideanDistance - 0.8) / 0.2) * 20.0; // 55-75%
  } else if (euclideanDistance < 1.1) {
    // Moderate similarity (could be same person)
    similarityPercentage = 55.0 - ((euclideanDistance - 1.0) / 0.1) * 15.0; // 40-55%
  } else {
    // Low similarity (likely different person)
    // Distance > 1.1 typically means different person
    final excess = euclideanDistance - 1.1;
    similarityPercentage = max(0.0, 40.0 - (excess * 40.0));
  }

  // Determine match level
  String matchLevel;
  bool isMatch;

  if (similarityPercentage >= 70) {
    matchLevel = 'Strong Match';
    isMatch = true;
  } else if (similarityPercentage >= 55) {
    matchLevel = 'Likely Match';
    isMatch = true;
  } else if (similarityPercentage >= 40) {
    matchLevel = 'Possible Match';
    isMatch = false; // Conservative: require higher threshold
  } else {
    matchLevel = 'No Match';
    isMatch = false;
  }

  return FaceComparisonResult(
    distance: euclideanDistance,
    similarityPercentage: similarityPercentage,
    isMatch: isMatch,
    matchLevel: matchLevel,
  );
}