SourceAFIS for .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.
Download
Get SourceAFIS from NuGet:
PM> Install-Package SourceAFIS -Version 3.13.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)
.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;
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:
- parallelization strategy,
- population filtering, for example searching only men under 30,
- finger position filtering, for example matching right thumb only to right thumbs,
- multi-finger search, for example requiring both left index and right index finger to match, and
- multi-view search where multiple templates for the same finger are kept for every user to improve recognition rates.
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
- Download SourceAFIS from NuGet if you haven't done so yet.
- Check XML comments in sources for exact API semantics.
- Look into template format and algorithm transparency if you need more data than the basic API provides.
- Benchmark SourceAFIS performance on target hardware using SourceAFIS CLI.
- Submit an issue if you find a bug or need an enhancement (including docs).
- Report an issue on GitHub or Bitbucket if something is wrong, including requests for documentation, or submit a pull request via GitHub or Bitbucket.
- Consider custom development when everything else fails.