chickenduy
chickenduy

Reputation: 51

Windows Biometric Framework Extract image from fingerprint sensor

I'm trying to extract fingerprint images from my fingerprint sensors.

With my first fingerprint sensor (113x115) it works great.

The current sensor (114x57) it gives me a noisy image.

The code has been adapted to the sensor data (size, padding, width, height).

ImgCompressionAlg = 0;

PixelDepth = 8

#include "stdafx.h"
#include "Strsafe.h"

using namespace std;

//-----------------------------------------------------------------------------
// Forward declarations of local functions for Private Pool Setup.

HRESULT CaptureSample();
bool SaveBMP(BYTE* buffer, int width, int height, int pixelPerVertical, int pixelPerHorizontal, long paddedsize, LPCTSTR bmpfile, BYTE* firstPixel);

//-----------------------------------------------------------------------------

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr = S_OK;
    hr = CaptureSample();
    _tprintf(_T("Capture Sample: hr = 0x%x\n"), hr);

    std::cin;

    return 0;
}

//-----------------------------------------------------------------------------

HRESULT CaptureSample()
{
restart:
    HRESULT hr = S_OK;
    WINBIO_SESSION_HANDLE sessionHandle = NULL;
    WINBIO_UNIT_ID unitId = 0;
    WINBIO_REJECT_DETAIL rejectDetail = 0;
    PWINBIO_BIR sample = NULL;
    SIZE_T sampleSize = 0;
    PWINBIO_UNIT_SCHEMA unitSchema = NULL;
    SIZE_T unitCount = 0;
    SIZE_T index = 0;
    ofstream ofs;
    string name;

    // Connect to the system pool.  
    hr = WinBioOpenSession(
        WINBIO_TYPE_FINGERPRINT,    // Service provider
        WINBIO_POOL_SYSTEM,         // Pool type
        WINBIO_FLAG_RAW,            // Access: Capture raw data
        NULL,                       // Array of biometric unit IDs
        0,                          // Count of biometric unit IDs
        WINBIO_DB_DEFAULT,          // Default database
        &sessionHandle              // [out] Session handle
    );
    if (FAILED(hr))
    {
        wprintf_s(L"\n WinBioOpenSession failed. hr = 0x%x\n", hr);
        goto e_Exit;
    }
    // Enumerate the installed biometric units.
    hr = WinBioEnumBiometricUnits(
        WINBIO_TYPE_FINGERPRINT,        // Type of biometric unit
        &unitSchema,                    // Array of unit schemas
        &unitCount);                   // Count of unit schemas

    if (FAILED(hr))
    {
        wprintf_s(L"\n WinBioEnumBiometricUnits failed. hr = 0x%x\n", hr);
        goto e_Exit;
    }

    // Display information for each installed biometric unit.
    wprintf_s(L"\nSensors: \n");
    for (index = 0; index < unitCount; ++index)
    {
        wprintf_s(L"\n[%d]: \tUnit ID: %d\n",
            index,
            unitSchema[index].UnitId);
        wprintf_s(L"\tDevice instance ID: %s\n",
            unitSchema[index].DeviceInstanceId);
        wprintf_s(L"\tPool type: %d\n",
            unitSchema[index].PoolType);
        wprintf_s(L"\tBiometric factor: %d\n",
            unitSchema[index].BiometricFactor);
        wprintf_s(L"\tSensor subtype: %d\n",
            unitSchema[index].SensorSubType);
        wprintf_s(L"\tSensor capabilities: 0x%08x\n",
            unitSchema[index].Capabilities);
        wprintf_s(L"\tDescription: %s\n",
            unitSchema[index].Description);
        wprintf_s(L"\tManufacturer: %s\n",
            unitSchema[index].Manufacturer);
        wprintf_s(L"\tModel: %s\n",
            unitSchema[index].Model);
        wprintf_s(L"\tSerial no: %s\n",
            unitSchema[index].SerialNumber);
        wprintf_s(L"\tFirmware version: [%d.%d]\n",
            unitSchema[index].FirmwareVersion.MajorVersion,
            unitSchema[index].FirmwareVersion.MinorVersion);
    }

    wprintf_s(L"\n Pick a Sensor: ");
    int i;
    std::cin >> i;

    unitId = unitSchema[i].UnitId;

    // Capture a biometric sample.
    wprintf_s(L"\n Calling WinBioCaptureSample - Swipe sensor...\n");
    hr = WinBioCaptureSample(
        sessionHandle,
        WINBIO_NO_PURPOSE_AVAILABLE,
        WINBIO_DATA_FLAG_RAW,
        &unitId,
        &sample,
        &sampleSize,
        &rejectDetail
    );
    if (FAILED(hr))
    {
        if (hr == WINBIO_E_BAD_CAPTURE)
        {
            wprintf_s(L"\n Bad capture; reason: %d\n", rejectDetail);
        }
        else
        {
            wprintf_s(L"\n WinBioCaptureSample failed. hr = 0x%x\n", hr);
        }
        goto e_Exit;
    }

    wprintf_s(L"\n Swipe processed - Unit ID: %d\n", unitId);
    wprintf_s(L"\n Captured %d bytes.\n", sampleSize);

    PWINBIO_BIR_HEADER BirHeader = (PWINBIO_BIR_HEADER) (((PBYTE)sample) + sample->HeaderBlock.Offset); //header points to the offset of the header block
    PWINBIO_BDB_ANSI_381_HEADER AnsiBdbHeader = (PWINBIO_BDB_ANSI_381_HEADER)(((PBYTE)sample) + sample->StandardDataBlock.Offset); //header points to the beginning of the standard data block
    PWINBIO_BDB_ANSI_381_RECORD AnsiBdbRecord = (PWINBIO_BDB_ANSI_381_RECORD)(((PBYTE)AnsiBdbHeader) + sizeof(WINBIO_BDB_ANSI_381_HEADER)); //record points to the record of the standard data block

    BYTE* firstPixel = (BYTE*)((BYTE*)AnsiBdbRecord) + sizeof(WINBIO_BDB_ANSI_381_RECORD); //points to the data of first pixel

    wprintf_s(L"\n ID: %d\n", AnsiBdbHeader->ProductId.Owner);
    wprintf_s(L" Horizontal Img. Res.: %d\n", AnsiBdbHeader->HorizontalImageResolution);
    wprintf_s(L" Horizontal Scan Img. Res.: %d pixels/centimeter\n", AnsiBdbHeader->HorizontalScanResolution);
    wprintf_s(L" Vertical Img. Res.: %d\n", AnsiBdbHeader->VerticalImageResolution);
    wprintf_s(L" Vertical Scan Img. Res.: %d pixels/centimeter\n", AnsiBdbHeader->VerticalScanResolution);
    wprintf_s(L" Pixel Depth: %d\n", AnsiBdbHeader->PixelDepth); //number of bits in one pixel
    wprintf_s(L" Element Count: %d\n", AnsiBdbHeader->ElementCount); //number of images

    wprintf_s(L" Width: %d\n", AnsiBdbRecord->HorizontalLineLength);
    wprintf_s(L" Height: %d\n", AnsiBdbRecord->VerticalLineLength);
    wprintf_s(L" Blocklength: %d\n", AnsiBdbRecord->BlockLength);
    wprintf_s(L" Compression Algorithm: %d\n", AnsiBdbHeader->ImageCompressionAlg);

    wprintf_s(L" Address of First Pixel in HEX: 0x%x\n", firstPixel);
    wprintf_s(L" Address of First Pixel in DEC: %d\n", firstPixel);

    //AnsiBdbRecord->BlockLength is size of data + record header
    long blocklength = AnsiBdbRecord->BlockLength - sizeof(AnsiBdbRecord); //blocklength is size of raw image data
    //long size = sizeof(char) * 4;

    bool b = SaveBMP(firstPixel, AnsiBdbRecord->HorizontalLineLength, AnsiBdbRecord->VerticalLineLength, AnsiBdbRecord->VerticalLineLength, AnsiBdbRecord->HorizontalLineLength, blocklength, L"C:\\Users\ChickenDuy\Documents\bio_key_extraction\bio_key_extraction\bio_key_extraction\fingerprint.bmp", firstPixel);

    if (b) {
        wprintf_s(L"\n SaveBMP succeeded");
    }
    else {
        wprintf_s(L"\n SaveBMP failed");
    }


e_Exit:
    if (sample != NULL)
    {
        WinBioFree(sample);
        sample = NULL;
    }
    if (unitSchema != NULL)
    {
        WinBioFree(unitSchema);
        unitSchema = NULL;
    }
    if (sessionHandle != NULL)
    {
        WinBioCloseSession(sessionHandle);
        sessionHandle = NULL;
    }

    wprintf_s(L"\n Press 0 to repeat.\n Press 1 to exit\n. ");
    std::cin >> i;

    if (i == 0) {
        goto restart;
    }

    return hr;
}

bool SaveBMP(BYTE* buffer, int width, int height, int pixelPerVertical, int pixelPerHorizontal, long data_size, LPCTSTR bmpfile, BYTE* firstPixel) {
    BITMAPFILEHEADER bmfh;
    BITMAPINFOHEADER info;
    memset(&bmfh, 0, sizeof(BITMAPFILEHEADER));
    memset(&info, 0, sizeof(BITMAPINFOHEADER));

    //long sizeOfColorTable = 4*255*sizeof(char); //size of the color table is 00-FF with a four byte coding RGB0
    int padding = 4 - (width % 4); //calculate the padding needed

    bmfh.bfType = 19778; // Don't question it. Magic Word (B and M). It's necessary. Seriously.
    bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + data_size; //size of the whole bitmap
    bmfh.bfReserved1 = 0;
    bmfh.bfReserved2 = 0;
    bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); //start position of data after the header

    info.biSize = sizeof(BITMAPINFOHEADER); //size of the bitmap info header
    info.biWidth = width; //width of the bitmap
    info.biHeight = height; //height of the bitmap
    info.biPlanes = 1; //dimensions of the image
    info.biBitCount = 8; //encoding of raw data -> 8 bits per pixel
    info.biCompression = 0; //compression algorithm
    info.biSizeImage = (width*sizeof(char) + padding*sizeof(char))*height; //size of image data in bytes including padding
    info.biXPelsPerMeter = pixelPerHorizontal;
    info.biYPelsPerMeter = pixelPerVertical;
    info.biClrUsed = 0;
    info.biClrImportant = 0;


    ofstream bmp("fingerprint.bmp", ios::binary | ios::trunc | ios::out); //open as output and binary file and delete everything in the current .bmp file

    if (bmp.is_open()) {
        wprintf_s(L"\n Opening BMP successful\n");
        bmp.write((char*)&bmfh, sizeof(BITMAPFILEHEADER)); //write BitmapFileHeader
        bmp.write((char*)&info, sizeof(BITMAPINFOHEADER)); //write BitmapInfoHeader
    }
    else {
        wprintf_s(L"\n Opening BMP failed\n");
        return false;
    }

    //char pixelArray[pWidth][pHeight]; //create a pixel array with the size of the image
    char pixelArray[1];
    BYTE* pixel = new BYTE;
    pixel = &(*firstPixel); //new pointer to the first pixel

    int* x = 0;
    ////grey index color array
    for (int i = 0; i < 256; i++) {
            bmp.write((char*) &i, sizeof(char)); //write red
            bmp.write((char*) &i, sizeof(char)); //write green
            bmp.write((char*) &i, sizeof(char)); //write blue
            bmp.write((char*) &x, sizeof(char)); //write 0
            //wprintf(L"\n Saving Color: %x %x %x %x", i, i, i, 0);
    }


    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            memset(&pixelArray[0], *pixel, sizeof(char)); //get data from sample and save into array 
            bmp.write((char*)&pixelArray[0], sizeof(char)); //save pixel to bitmap
            pixel = &(*pixel) + sizeof(char); //set pointer to the next byte
            //wprintf(L"\n Saving PixelArray[%d][%d] 0x%x", j, i, *pixel);
        }
        //113 pixels * 3 bytes = 339 bytes -> missing 1 byte (bio_key) because 24bit/3byte pixel depth
        //114 pixels * 1 byte = 114 bytes -> missing 2 bytes (small sensor) because 8bit/1byte pixel depth
        for (int j = 0; j < padding; j++) {
            bmp.write((char*)&x, sizeof(char)); //pad to a multiple of 4 bytes
            //wprintf(L"\n Saving PixelArray[%d][%d] 0x%x", 115, i, 0);
        }
    }       
bmp.close();
return true;
}

Sensor 1

Sensor 2

Hope somebody will be able to help.

Upvotes: 2

Views: 1650

Answers (1)

user3761570
user3761570

Reputation: 97

You have a bug in your code, so the bmp is corrupted:

if (width % 4 != 0)padding = 4 - (width % 4); else padding = 0;

Upvotes: 0

Related Questions