SourceAFIS for Java

SourceAFIS » Java

SourceAFIS for Java is a pure Java 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 for Java from Maven Central:


Or clone sources from GitHub or Bitbucket. Don't forget to configure your build for Java 11+. Last version compatible with Java 8 was 3.13.0. On Android, SourceAFIS requires Android API level 24+ (tested with SourceAFIS 3.14.2). Sources and binaries are distributed under Apache License 2.0. SourceAFIS homepage has some links to fingerprint image databases in case you don't have any sample fingerprints at hand.

If your project is a Java module, add the following declaration to your

requires com.machinezoo.sourceafis;

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.

FingerprintTemplate probe = new FingerprintTemplate(
    new FingerprintImage(
        new FingerprintImageOptions()

FingerprintTemplate candidate = new FingerprintTemplate(
    new FingerprintImage(
        new FingerprintImageOptions()

FingerprintImage(byte[]) constructor accepts common fingerprint image formats. There's also FingerprintImage(int, int, byte[]) constructor for raw grayscale images. Both constructors have overloads taking FingerprintImageOptions. It is recommended to specify image DPI via its dpi(double) method, because SourceAFIS is not scale-invariant and every image should have correctly configured DPI.

SourceAFIS is designed to process fingerprint images coming directly from fingerprint reader. Some fingerprint reader vendors might try to put you through the pain of handling proprietary fingerprint templates. Email them for image acquisition SDK or shop around for better sensor. Trust me, coding against image API is way more enjoyable. If you are stuck, try to pass the proprietary template to FingerprintCompatibility.importTemplate(byte[]). If you are lucky, the template has open-content documentation and an implementation in FingerprintIO library, which SourceAFIS uses to convert foreign template formats.

We can now create FingerprintMatcher, which is just another representation of the fingerprint template passed to FingerprintMatcher(FingerprintTemplate) constructor. The reason for having this separate representation will become apparent later. We can now pass the other template to its 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;
boolean 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 {
    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 call FingerprintMatcher(FingerprintTemplate) 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.

FingerprintTemplate template = new FingerprintTemplate(
    new FingerprintImage(
        new FingerprintImageOptions()
byte[] serialized = template.toByteArray();

Method toByteArray() 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(byte[]) constructor instead of recreating them from fingerprint images.

byte[] serialized = Files.readAllBytes(Paths.get("fingerprint.cbor"));
FingerprintTemplate 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