SourceAFIS for .NET

SourceAFIS » .NET

SourceAFIS for .NET is a pure C# port of SourceAFIS, an algorithm for recognition of human fingerprints. It can compare two fingerprints 1:1 or search a large database 1:N for matching fingerprint. It takes fingerprint images on input and produces similarity score on output. Similarity score is then compared to customizable match threshold.


Get SourceAFIS from NuGet:

<PackageReference Include="SourceAFIS" Version="3.14.0" />

Or clone sources from GitHub or Bitbucket. Sources and binaries are distributed under Apache License 2.0. SourceAFIS targets .NET Standard 2.0, so it should work in both .NET Framework 4.7.2+ and .NET Core 2.0+.

Matching two fingerprints 1:1

We start by creating FingerprintTemplate objects for both fingerprints. Fingerprint template is a biometric description of human fingerprint. It contains features relevant for matching. In case of SourceAFIS, these features are fingerprint minutiae, essentially ridge endings and bifurcations.

Template construction is a separate step, because it is computationally expensive, taking significant fraction of a second to compute. Application developers need to think where to put this step in their code.

var options = new FingerprintImageOptions { Dpi = 500 };

var probe = new FingerprintTemplate(
    new FingerprintImage(
        332, 533, File.ReadAllBytes("probe.dat"), options));

var candidate = new FingerprintTemplate(
    new FingerprintImage(
        320, 407, File.ReadAllBytes("candidate.dat"), options));

FingerprintImage constructor accepts raw grayscale image along with image width and height. Decoding of standard image formats (PNG, JPEG, ...) is not currently supported in order to maintain compatibility with .NET Standard 2.0. FingerprintImage accepts optional FingerprintImageOptions parameter. It is recommended to specify image DPI via its Dpi property, because SourceAFIS is not scale-invariant and every image should have correctly configured DPI.

We can now create FingerprintMatcher, which is just another representation of the fingerprint template passed to FingerprintMatcher constructor. The reason for having this separate representation will become apparent later. We can now pass the other template to FingerprintMatcher's Match(FingerprintTemplate) method.

double score = new FingerprintMatcher(probe)

Variable score now holds similarity score of the two fingerprints. Similarity score is an approximate measure of similarity of two fingerprints. The higher it is, the more likely is the match.

Similarity score is necessarily probabilistic. You can never be sure of a match no matter how high the score is. It is however possible to come up with a reasonable threshold beyond which match is extremely likely.

double threshold = 40;
bool matches = score >= threshold;

Certainty in fingerprint recognition is measured by FMR: false match rate. FMR is the frequency with which the system incorrectly recognizes non-matching fingerprints as matching. Threshold 40 corresponds to FMR 0.01%. Applications can increase the threshold to get exponentially lower FMR at the cost of slightly higher FNMR: false non-match rate. In the end, choice of threshold is application-dependent, but 40 is a good starting point.

Searching fingerprint databases (1:N matching)

Let's say we want to compare one probe fingerprint, which was just read from fingerprint reader, against a number of candidate fingerprints, which are already stored in a database. Databases are usually on-disk, but fingerprint databases must be in-memory, because probe fingerprint must be compared to each one of the candidate fingerprints.

With 1:N matching, it is no longer sufficient to return matching score. We need to identify the user whose fingerprint was matched, perhaps by a class describing the user.

class UserDetails
    public int Id { get; set; }
    public string Name { get; set; }
    public FingerprintTemplate Template { get; set; }

We can now define a method that takes the probe fingerprint and a list of candidate fingerprints and returns the best match or null if there is no match.

UserDetails Find(FingerprintTemplate probe, IEnumerable<UserDetails> candidates)
    var matcher = new FingerprintMatcher(probe);
    UserDetails match = null;
    double high = 0;
    foreach (var candidate in candidates)
        double score = matcher.Match(candidate.Template);
        if (score > high)
            high = score;
            match = candidate;
    double threshold = 40;
    return high >= threshold ? match : null;

It is important to call FingerprintMatcher constructor only once, because it is an expensive operation. It builds in-memory data structures that speed up matching. Individual calls to Match(FingerprintTemplate) method are relatively fast.

You might be wondering why SourceAFIS does not provide such search method in the API. Aside from making it easier to associate templates with application-defined user identities, keeping the search loop on application side allows applications to customize the loop. Examples of such customizations include:

Caching fingerprint templates

It would be unreasonably expensive to recreate all fingerprint templates in the database from fingerprint images every time the application restarts. For this reason, SourceAFIS provides a way to cache fingerprint templates.

var options = new FingerprintImageOptions { Dpi = 500 };
var template = new FingerprintTemplate(
    new FingerprintImage(
        332, 533, File.ReadAllBytes("fingerprint.dat"), options));
byte[] serialized = template.ToByteArray();

Method ToByteArray() on FingerprintTemplate produces binary representation of the template that can be stored in a database to speed up application restarts. When application starts, it deserializes templates using FingerprintTemplate constructor taking byte[] instead of recreating them from fingerprint images.

byte[] serialized = File.ReadAllBytes("fingerprint.cbor");
var template = new FingerprintTemplate(serialized);

Serialized templates are not a substitute for fingerprint images. They are tied to specific SourceAFIS version. In order to allow SourceAFIS upgrades, applications should always store original fingerprint images and treat serialized templates as a temporary cache.

Next steps