HomeDownloadJavadoc.NET ReferenceForum


for Java and .NET

SourceAFIS is an algorithm recognizing human fingerprints. It can compare two fingerprints 1:1 or search large database 1:N for matching fingerprint. It takes raw fingerprint images on input and produces similarity score on output. Similarity score is then compared to customizable match threshold.

SourceAFIS algorithm has two nearly identical opensource implementations in pure Java and pure .NET. SourceAFIS API is designed for maximum simplicity and ease of use by application developers. Accuracy and speed of matching support small applications handling up to 10,000 fingerprints.

Getting started

The tutorial below covers SourceAFIS for Java 2.0. While SourceAFIS for .NET 2.0 is in development, it is not yet ready for everyday use. There is however an older tutorial covering SourceAFIS for .NET 1.7.

You can download SourceAFIS library from Maven Central. Download section includes links to fingerprint image databases in case you don't have any sample fingerprints at hand.

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, that is 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.

byte[] probeImage = Files.readAllBytes(Paths.get("probe.jpeg"));
byte[] candidateImage = Files.readAllBytes(Paths.get("candidate.jpeg"));
FingerprintTemplate probe = new FingerprintTemplate(probeImage);
FingerprintTemplate candidate = new FingerprintTemplate(candidateImage);

We can now create FingerprintMatcher, which is just another representation of fingerprint template that will become important later, and pass the other template to its match method.

FingerprintMatcher matcher = new FingerprintMatcher(probe);
double score = matcher.match(candidate);

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;
boolean match = 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 through a class describing the user.

class UserDetails {
    int id;
    String name;
    FingerprintTemplate template;

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, Iterable<UserDetails> candidates) {
    FingerprintMatcher matcher = new FingerprintMatcher(probe);
    UserDetails match = null;
    double high = 0;
    for (UserDetails candidate : 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 construct FingerprintMatcher only once, because it is an expensive operation. FingerprintMatcher builds in-memory data structures that speed up matching. Individual calls to match method are relatively fast.

You might be wondering why doesn't SourceAFIS 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 raw fingerprint images every time the application restarts. For this reason, SourceAFIS provides a way to cache fingerprint templates.

byte[] image = Files.readAllBytes(Paths.get("fingerprint.jpeg"));
FingerprintTemplate template = new FingerprintTemplate(image);
String json = template.json();

JSON representation of the template can be stored in database to speed up application restarts. When application starts, it deserializes JSON templates instead of recreating them from fingerprint images.

FingerprintTemplate template = new FingerprintTemplate(json);

JSON 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 JSON templates as a temporary cache.

Next steps