.net, Computer Vision, Fingerprint Enrollment

How to use C# to create a bitmap of a fingerprint from the DigitalPersona U.are.U 4000 fingerprint scanner, Part #1

In a previous post, I used the BioMini fingerprint scanner to generate a bitmap image of a fingerprint. I used the Neurotechnology Free Fingerprint Verification SDK with the BioMini hardware.

As part of the process, I created an interface which allowed me to enroll a fingerprint, and create the image, which defines a good surface for all I want to do at the moment. I designed this interface based on the very small amount of knowledge I have of fingerprint scanners and SDKs – so I was still interested to see if this interface would be useful (or even workable) for another scanner and SDK.

To test this, I started looking for other scanners and SDKs – and one candidate which looked very suitable was the digitalPersona U.are.U 4000B sensor. This has a .NET SDK available, but make sure that when you are buying the scanner device that you get the SDK as well – it’s possible to purchase these separately.

WP_20160324_18_02_27_Pro_LI

This SDK comes with a couple of sample Windows applications – but I’ve a personal preference to try to get things to work in a console application, just because it allows me to focus more on the code to get the scanner working (and less on the code to get the Windows app working). So I decided to write a Console application for the U.are.U 4000B scanner.

There are a few simple steps:

  1. Add references to the libraries DPFPDevNET.dll and DPFPShrNET.dll, both of which come with the SDK;
  2. Instantiate a DPFP.Capture.Capture object;
  3. Associate an event handler class with this Capture object, which has handlers for the events:
    • OnComplete;
    • OnFingerGone;
    • OnFingerTouch;
    • OnReaderConnect;
    • OnReaderDisconnect;
    • OnSampleQuality.
  4. Begin capturing a fingerprint from the scanner by calling the StartCapture method from the Capture object.
  5. After placing your finger on the reader, the event OnFingerTouch will be fired.
  6. After the scan has successfully complete, the OnComplete event is fired.
    • A parameter of the OnComplete handler contains information about the scanned fingerprint.
  7. Stop capturing a fingerprint from the scanner by calling the StopCapture method from the Capture object.

This seemed pretty straightforward – I wrote the class below.

public class FingerPrintScanner : DPFP.Capture.EventHandler
{
    public Capture capture { getset; } = new Capture();
    
    public void EnrollAndSavePicture()
    {
        capture.EventHandler = this;
        capture.StartCapture();
    }
    
    public void OnComplete(object capture, string readerSerialNumber, Sample sample)
    {
        ((Capture)capture).StopCapture();
 
        var sampleConvertor = new SampleConversion();
        Bitmap bitmap = null;
        sampleConvertor.ConvertToPicture(sample, ref bitmap);
 
        bitmap.Save(@"C:\Users\jeremy\Desktop\fingerprint.bmp");
    }
 
    public void OnFingerGone(object capture, string readerSerialNumber) { }
    public void OnFingerTouch(object capture, string readerSerialNumber) { }
    public void OnReaderConnect(object capture, string readerSerialNumber) { }
    public void OnReaderDisconnect(object capture, string readerSerialNumber) { }
    public void OnSampleQuality(object capture, string readerSerialNumber, CaptureFeedback captureFeedback) { }
}

And this allowed me to write the following simple program.

class Program
{
    static void Main(string[] args)
    {
        var scanner = new FingerPrintScanner();
        scanner.EnrollAndSavePicture();
    }
}

So this is a good start – I was able to capture a fingerprint and save it to my desktop. However, this implementation doesn’t use the interface I designed last time, which has separate methods for Enroll and CreateBitmapFile. I refactored the code slightly to implement this interface.

public class DigitalPersonaFingerPrintScanner : DPFP.Capture.EventHandler, IFingerprintScanner
{
    private Capture _capture;
    private Sample _sample;
 
    public void Enroll()
    {
        _capture = new Capture();
        _capture.EventHandler = this;
        _capture.StartCapture();
    }
 
    public void CreateBitmapFile(string pathToSaveBitmapTo)
    {
        if (_sample == null)
        {
            throw new NullReferenceException(nameof(_sample));
        }
 
        var sampleConvertor = new SampleConversion();
        Bitmap bitmap = null;
        sampleConvertor.ConvertToPicture(_sample, ref bitmap);
 
        bitmap.Save(pathToSaveBitmapTo);
    }
 
    public void Dispose()
    {
        _capture?.StopCapture();
        _capture?.Dispose();
    }
 
    public void OnComplete(object capture, string readerSerialNumber, Sample sample)
    {
        _capture.StopCapture();
        this._sample = sample;
    }
 
    public void OnFingerGone(object capture, string readerSerialNumber) { }
    public void OnFingerTouch(object capture, string readerSerialNumber) { }
    public void OnReaderConnect(object capture, string readerSerialNumber) { }
    public void OnReaderDisconnect(object capture, string readerSerialNumber) { }
    public void OnSampleQuality(object capture, string readerSerialNumber, CaptureFeedback captureFeedback) { }
}

This compiled, and I expected to be able to run the code below.

using (var scanner = new DigitalPersonaFingerPrintScanner())
{
    scanner.Enroll();
    scanner.CreateBitmapFile(@"C:\Users\jeremy\Desktop\fingerprint.bmp");
}

Unfortunately there was a problem – when designing the implementation, I hadn’t taken account of the fact that the device and SDK is driven by events – so after I start running the program, it’ll happily wait for someone to put their finger on the device screen and won’t block the main thread. So control flows straight on after the call to Enroll to the method which tries to create an image. However, because the fingerprint sample might hadn’t been successfully scanned at that point, I got a null reference exception.

nullreferenceexception

In the second part of this, I’ll describe how I fixed this problem, using the ManualResetEvent object.