Face Verification Library

Introduction

The Face Verification Library is a portable C++ library for face verification, designed for real-time processing on client devices (mobile and desktop).

The SDK provides wrappers in the following languages:

  • C
  • Python
  • C# (.NET)

Features

  • Face detection
  • Face embedding extraction for verification
  • Embedding comparison with similarity scoring
  • Async API with both std::future and callback interfaces
  • Configurable concurrency
  • Support for 3rd party face detectors
  • Cross-platform (desktop, mobile)

Supported Platforms

Hardware Requirements

  • CPU: Any modern 64-bit capable CPU (x86-64 with AVX, ARM8)
  • GPU: No special requirement
  • RAM: 2 GB of available RAM required
  • Camera: Minimum resolution: 640x480

Software Requirements

The SDK is regularly tested on the following operating systems:

Platform Version
Windows 11
Linux Ubuntu 24.04
macOS 15 Sequoia
iOS 18
Android 27

Platform / Architecture Support

Platform Architecture Status
Linux x86_64, ARM64 Supported
Windows x86_64 Supported
macOS ARM64 Supported
iOS ARM64 Supported
Android ARM64 Supported

Available APIs

  • C++ - Core native API
  • C - C-compatible API for FFI integration
  • Python - Python bindings via pybind11
  • .NET - C# bindings with async/await support

API Comparison

Create verifier

Detect faces

Embed face

Compare faces

Get model name

Get SDK version

Getting Started

API Reference

Quick Examples

C++

#include "faceverifier.h"

#include <opencv2/core.hpp>
#include <opencv2/imgcodec.hpp>

#include <iostream>

int main()
{
   fvl::FaceVerifier verifier("model/model.realZ");

   cv::Mat image1 = cv::imread("image1.jpg");
   cv::Mat image2 = cv::imread("image2.jpg");

   std::vector<fvl::Face> faces1 = verifier.detectFaces({image1.ptr(), image1.cols, image1.rows, static_cast<int>(image1.step1()), fvl::ImageFormat::BGR}).get();
   std::vector<fvl::Face> faces2 = verifier.detectFaces({image2.ptr(), image2.cols, image2.rows, static_cast<int>(image2.step1()), fvl::ImageFormat::BGR}).get();

   std::vector<std::vector<float>> embeddings1, embeddings2;
   for (const Face& face: faces1)
       embeddings1.push_back(verifier.embedFace(face).get());
   for (const Face& face: faces2)
       embeddings2.push_back(verifier.embedFace(face).get());

   for (size_t i = 0; i < embeddings1.size(); ++i)
       for (size_t j = 0; j < embeddings2.size(); ++j)
           if (verifier.compare_faces(embeddings1[i], embeddings2[j]).similarity > 0.3) {
               fvl::BoundingBox bbox1 = faces1[i].bounding_box();
               fvl::BoundingBox bbox2 = faces2[j].bounding_box();
               std::cout << "(" << bbox1.x << ", " << bbox1.y << ", " << bbox1.width << ", " << bbox1.height << ") from image 1 and";
               std::cout << "(" << bbox2.x << ", " << bbox2.y << ", " << bbox2.width << ", " << bbox2.height << ") from image 2 are the same people";
               std::cout << std::endl;
           }

   return 0;
}

Python

import realeyes.face_verification as fvl
import cv2

verifier = fvl.FaceVerifier('model/model.realZ')

image1 = cv2.imread('image1.jpg')[:, :, ::-1]  # opencv reads BGR we need RGB
image2 = cv2.imread('image2.jpg')[:, :, ::-1]  # opencv reads BGR we need RGB

faces1 = verifier.detect_faces(image1)
faces2 = verifier.detect_faces(image2)

embeddings1 = [verifier.embed_face(face) for face in faces1]
embeddings2 = [verifier.embed_face(face) for face in faces2]

for i, e1 in enumerate(embeddings1):
    for j, e2 in enumerate(embeddings2):
        if verifier.compare_faces(e1, e2).similarity > 0.3:
            print(f'{faces1[i]} from image 1 and {faces2[j]} from image 2 are the same person!')

C#

using Realeyes.FaceVerification;
using OpenCvSharp;

using var verifier = new FaceVerifier("model/model.realZ");

using var image1 = Cv2.ImRead("image1.jpg");
using var image2 = Cv2.ImRead("image2.jpg");

// Convert OpenCV Mat to byte array
var imageData1 = image1.ToBytes();
var imageData2 = image2.ToBytes();

var header1 = new ImageHeader(imageData1, image1.Width, image1.Height,
    image1.Width * image1.Channels(), ImageFormat.BGR);
var header2 = new ImageHeader(imageData2, image2.Width, image2.Height,
    image2.Width * image2.Channels(), ImageFormat.BGR);

// Detect faces in parallel
var detectTask1 = verifier.DetectFacesAsync(header1);
var detectTask2 = verifier.DetectFacesAsync(header2);

using var faces1 = await detectTask1;
using var faces2 = await detectTask2;

// Embed all faces in parallel
var embeddingTasks = new List<Task<float[]>>();
foreach (var face in faces1)
    embeddingTasks.Add(verifier.EmbedFaceAsync(face));
foreach (var face in faces2)
    embeddingTasks.Add(verifier.EmbedFaceAsync(face));

var allEmbeddings = await Task.WhenAll(embeddingTasks);

var embeddings1 = allEmbeddings.Take(faces1.Count).ToArray();
var embeddings2 = allEmbeddings.Skip(faces1.Count).ToArray();

// Compare embeddings
for (int i = 0; i < embeddings1.Length; i++)
{
    for (int j = 0; j < embeddings2.Length; j++)
    {
        var match = verifier.CompareFaces(embeddings1[i], embeddings2[j]);
        if (match.Similarity > 0.3f)
        {
            var bbox1 = faces1[i].BoundingBox;
            var bbox2 = faces2[j].BoundingBox;
            Console.WriteLine($"({bbox1.X}, {bbox1.Y}, {bbox1.Width}, {bbox1.Height}) from image 1 and " +
                              $"({bbox2.X}, {bbox2.Y}, {bbox2.Width}, {bbox2.Height}) from image 2 are the same person!");
        }
    }
}

Results

The Face objects consist of the following members:

  • bounding_box: Bounding box of the detected face (left, top, width, height).
  • confidence: Confidence of the detection ([0,1] — higher is better).
  • landmarks: 5 landmarks from the face:
    1. Left eye
    2. Right eye
    3. Nose (tip)
    4. Left mouth corner
    5. Right mouth corner
Face landmarks
Face landmarks

3rd Party Face Detector

It is possible to calculate the embedding of a face which was detected with a different library. You can create a Face object by specifying the source image and the landmarks.

Dependencies

The public C++ API hides all the implementation details from the user, and it only depends on the C++17 Standard Library. It also provides a binary compatible interface, making it possible to change the underlying implementation without the need of recompilation of the user code.

Release Notes

  • Version 1.5.0 (15 Dec 2025)

    • Improved performance on ARM CPUs
    • Cleaner .NET API
    • New public C API
  • Version 1.4.0 (7 Feb 2024)

    • Experimental .NET support
  • Version 1.3.0 (9 Jun 2023)

    • Add model config version 2 support
  • Version 1.2.0 (9 Jun 2023)

    • Add support for AES encryption
  • Version 1.1.0 (3 Apr 2023)

    • Add support for 3rd party face detectors
  • Version 1.0.0 (1 Mar 2023)

    • Initial release

3rd Party Licenses

While the SDK is released under a proprietary license, the following open-source projects were used in it with their respective licenses:

Library License
OpenCV 3-clause BSD
TensorFlow Apache License 2.0
Protobuf 3-clause BSD
zlib zlib License
minizip-ng zlib License
stlab Boost Software License 1.0
docopt.cpp MIT License
pybind11 3-clause BSD
fmtlib MIT License

License

Proprietary - Realeyes Data Services Ltd.