应用笔记-Qt 程序中使用 Live 20R 指纹采集器

应用笔记-Qt 程序中使用 Live 20R 指纹采集器

最近一个项目需要用到指纹认证。就在某东上随便搜了个销量比较大的指纹采集器,就是今天这个笔记的主角(Live 20R 指纹采集器)。花了几天时间研究明白了这个小东西是如何使用的。
这里写图片描述

Live 20R 指纹采集器本身提供了 SDK 开发包,叫做 ZKFinger SDK,我使用的版本是 5.0。这个 SDK 里提供了 ActiveX 接口和 C 语言接口。比较这两个接口的功能会发现 ActiveX 接口提供的功能更强,因此,应该优先使用这个接口。

不过我当时没仔细对比,直接就用了 SDK 中提供的 C 语言接口了。这个功能少了点,但是也够我用了。

简单的说,Live 20R 指纹采集器本身只是个指纹图像获取设备。它可以用来获取指纹图像。其他的功能都是 SDK 软件来实现的。因此我将功能划分到了两个 C++类中:

  1. FingerprintReader
  2. FingerprintCacheDB

FingerprintReader 用于获取指纹。FingerprintCacheDB 实现指纹比对等其他功能。另外还有两个辅助类:

  1. FingerprintEnroll
  2. FPCacheDBLoader

FingerprintEnroll 是个辅助类,用于简化指纹登记的代码。FPCacheDBLoader 用于将指纹存入数据库或从数据库中读出。

FingerprintReader

FingerprintReader 这个类直接与硬件打交道。用于获取指纹。里面用到了一个线程,不停的询问Live 20R 指纹采集器是否有新的指纹,如果有则发出一个 Qt 信号 :void onFingerprintReceived(QImage image, QByteArray fpTemplate)。

QByteArray fpTemplate 是从指纹中提取的信息。后面的比对都是用的这个 fpTemplate。指纹图像在后面倒是没什么用的。这里之所以把指纹图像给传出来了,主要是为了在界面上显示出来,看起来比较的酷。

下面直接给代码吧,首先是头文件:

#ifndef FINGERPRINTREADER_H
#define FINGERPRINTREADER_H

#include <QObject>
#include <QThread>
#include <QImage>
#include <QString>

#include <windows.h>

namespace ZKFP
{
/**
 * @brief The FingerprintReader class 这个类完成指纹采集的工作。指纹判别工作由其他类来完成。
 */
class FingerprintReader : public QThread
{
    Q_OBJECT
public:
    explicit FingerprintReader(QObject *parent = nullptr);
    ~FingerprintReader();

    /**
     * @brief getDeviceCount 获取系统中识别出来的指纹识别器的数量
     * @return
     */
    int getDeviceCount();
    QImage acquireImage();
    QString vendorName() const;
    QString productName() const;
    QString serialNumber() const;
    uint16_t getVID() const;
    uint16_t getPID() const;
    bool isFakeFuncOn();
    QSize imageSize() {return QSize(m_image_width, m_image_height);}
signals:
    /**
     * @brief onFeatureInfo 取得指纹初始特征时发出该信号
     * @param quality 表示该指纹特征的质量
     *                0: 好的指纹特征, 1: 特征点不够, 2: 其它原因导致不能取到指纹特征, -1: 可疑
     */
    void onFeatureInfo(int quality);

    /**
     * @brief onFingerTouching 当手指按压纹取像设备时发送该信号。
     */
    void onFingerTouching();

    /**
     * @brief onFingerLeaving 当手离开压纹取像设备时发送该信号。
     */
    void onFingerLeaving();

    void onCapture(bool result);

    void onFingerprintReceived(QImage image, QByteArray fpTemplate);

    void fakeFinger();

public slots:
    bool openDevice(int index = 0);
    void closeDevice();
    void beginCapture();
    void cancelCapture();

    /**
     * @brief setFakeFunc 设置防假开关
     * @param on  true 表示开启防假指纹功能。 false 表示关闭防假指纹功能。
     */
    void setFakeFunc(bool on);


private:
    HANDLE m_handle;

    QString m_vendorName;
    QString m_productName;
    QString m_serialNumber;

    QImage m_fpImage;
    int m_image_width;
    int m_image_height;
    int m_image_size;
    int m_image_DPI;
    int m_isFakeOn;

    uint16_t m_vid;
    uint16_t m_pid;

    bool m_init;

    void run() override;
    bool isRealFinger();
    bool m_stop;

};

}


#endif // FINGERPRINTREADER_H

接下来是 cpp 文件:

#include "FingerprintReader.h"
#include "FingerprintCacheDB.h"
#include "ZKFingerReader/libs/include/libzkfp.h"
#include <QDebug>
namespace ZKFP
{

static const int ZKFP_IMAGE_WIDTH_RO = 1;
static const int ZKFP_IMAGE_HEIGHT_RO = 2;
static const int ZKFP_IMAGE_DPI_RW = 3;
static const int ZKFP_IMAGE_SIZE_RO = 106;
static const int ZKFP_VID_PID_RO = 1015;
static const int ZKFP_FAKE_ON_RW = 2002;
static const int ZKFP_REAL_FINGER_RO = 2004;
static const int ZKFP_VENDOR_NAME_RO = 1101;
static const int ZKFP_PRODUCT_NAME = 1102;
static const int ZKFP_SERIAL_NUNBER = 1103;

//static const int MAX_TEMPLATE_SIZE = 2048;
static const int ZKFP_ERR_OK = 0;

int autoInitCount = 0;
static bool autoInit()
{
    if(autoInitCount == 0)
    {
        if( ZKFPM_Init() == 0)
        {
            autoInitCount ++;
            return true;
        }
        else
        {
            return false;
        }
    }
    return true;
}

static bool autoTerminate()
{
    autoInitCount --;
    if(autoInitCount == 0)
    {
        return (ZKFPM_Terminate() == 0);
    }
    return false;
}


FingerprintReader::FingerprintReader(QObject *parent)
    : QThread(parent),
      m_init(false),
      m_handle(0),
      m_image_height(0),
      m_image_width(0),
      m_image_size(0),
      m_vendorName(""),
      m_productName(""),
      m_serialNumber(""),
      m_stop(true)
{
    m_init = autoInit();
    qDebug() << "autoInit = " << m_init;
}

int FingerprintReader::getDeviceCount()
{
    return ZKFPM_GetDeviceCount();
}

bool FingerprintReader::openDevice(int index)
{
    m_handle = ZKFPM_OpenDevice(index);
    if(m_handle != 0)
    {
        setFakeFunc(true);

        unsigned int length = sizeof(int);

        m_image_DPI = 500;
        length = sizeof(int);
        ZKFPM_SetParameters(m_handle, ZKFP_IMAGE_DPI_RW, reinterpret_cast<unsigned char *> (&m_image_DPI), length);
        length = sizeof(int);
        ZKFPM_GetParameters(m_handle, ZKFP_IMAGE_DPI_RW, reinterpret_cast<unsigned char *> (&m_image_DPI), &length);

        ZKFPM_GetParameters(m_handle, ZKFP_IMAGE_WIDTH_RO, reinterpret_cast<unsigned char *> (&m_image_width), &length);
        length = sizeof(int);
        ZKFPM_GetParameters(m_handle, ZKFP_IMAGE_HEIGHT_RO, reinterpret_cast<unsigned char *> (&m_image_height), &length);
        length = sizeof(int);
        ZKFPM_GetParameters(m_handle, ZKFP_IMAGE_SIZE_RO, reinterpret_cast<unsigned char *> (&m_image_size), &length);

        qDebug() << "image width = " << m_image_width << ", height = " << m_image_height << ", DPI = " << m_image_DPI;

        char name[128] = {0};
        memset(name, 0, 128);
        length = 128;
        ZKFPM_GetParameters(m_handle, ZKFP_VENDOR_NAME_RO, reinterpret_cast<unsigned char *>(name), &length);
        m_vendorName.clear();
        m_vendorName.append(name);

        memset(name, 0, 128);
        length = 128;
        ZKFPM_GetParameters(m_handle, ZKFP_PRODUCT_NAME, reinterpret_cast<unsigned char *>(name), &length);
        m_productName.clear();
        m_productName.append(name);

        memset(name, 0, 128);
        length = 128;
        ZKFPM_GetParameters(m_handle, ZKFP_SERIAL_NUNBER, reinterpret_cast<unsigned char *>(name), &length);
        m_serialNumber.clear();
        m_serialNumber.append(name);

        qDebug() << "Vendor Name = " << m_vendorName;
        qDebug() << "Product Name = " << m_productName;
        qDebug() << "Serial Number = " << m_serialNumber;

        uint16_t vid_pid[2] = {0, 0};
        length = 4;
        ZKFPM_GetParameters(m_handle, ZKFP_VID_PID_RO, reinterpret_cast<unsigned char *>(vid_pid), &length);
        m_vid = vid_pid[0];
        m_pid = vid_pid[1];
        qDebug() << "VID = " << m_vid << ", PID = " << m_pid;
        return true;
    }
    else
    {
        m_vendorName.clear();
        m_productName.clear();
        m_serialNumber.clear();
        m_vid = 0;
        m_pid = 0;
    }
    return false;
}

bool FingerprintReader::isFakeFuncOn()
{
    m_isFakeOn = 0;
    unsigned int length = sizeof(m_isFakeOn);
    ZKFPM_GetParameters(m_handle, ZKFP_FAKE_ON_RW, reinterpret_cast<unsigned char *>(&m_isFakeOn), &length);
    return m_isFakeOn;
}

void FingerprintReader::setFakeFunc(bool on)
{
    int fakeON = on ? 1 : 0;
    unsigned int length = sizeof(m_isFakeOn);
    ZKFPM_SetParameters(m_handle, ZKFP_FAKE_ON_RW, reinterpret_cast<unsigned char *> (&fakeON), sizeof(int));
    ZKFPM_GetParameters(m_handle, ZKFP_FAKE_ON_RW, reinterpret_cast<unsigned char *>(&m_isFakeOn), &length);
}

void FingerprintReader::closeDevice()
{
    if(isRunning())
    {
        cancelCapture();
    }
    if(m_handle)
    {
        ZKFPM_CloseDevice(m_handle);
        m_vendorName.clear();
        m_productName.clear();
        m_serialNumber.clear();
        m_image_height = 0;
        m_image_width = 0;
        m_image_size = 0;
        m_vid = 0;
        m_pid = 0;
        m_handle = 0;
    }
}

QImage FingerprintReader::acquireImage()
{
    int width, height;
    uint length = 4;
    ZKFPM_GetParameters(m_handle, 1, (unsigned char*)&width, &length);
    ZKFPM_GetParameters(m_handle, 2, (unsigned char*)&height, &length);
    QImage image(width, height, QImage::Format_Grayscale8);
    ZKFPM_AcquireFingerprintImage(m_handle, image.bits(), width * height);

    return image;
}

QString FingerprintReader::vendorName() const
{
    return m_vendorName;
}

QString FingerprintReader::productName() const
{
    return m_productName;
}

QString FingerprintReader::serialNumber() const
{
    return m_serialNumber;
}

uint16_t FingerprintReader::getVID() const
{
    return m_vid;
}

uint16_t FingerprintReader::getPID() const
{
    return m_pid;
}

bool FingerprintReader::isRealFinger()
{
    int nFakeStatus = 0;
    unsigned int retLen = sizeof(int);
    if (0 == ZKFPM_GetParameters(m_handle, ZKFP_REAL_FINGER_RO, (unsigned char*)&nFakeStatus, &retLen))
    {
        if ((nFakeStatus & 31) == 31)
        {
            return true;
        }
    }
    return false;
}

void FingerprintReader::run()
{
    qDebug() << "Fingerprint sensor thead starting";
    unsigned char * imageBuffer = new unsigned char[m_image_height * m_image_width + 100];
    QImage image(m_image_width, m_image_height, QImage::Format_Grayscale8); //image 要求数据是 32位对齐的。

    unsigned char fpBuffer[MAX_TEMPLATE_SIZE];
    unsigned int tempLen = MAX_TEMPLATE_SIZE;
    while(!m_stop)
    {
        tempLen = MAX_TEMPLATE_SIZE;
        int ret = ZKFPM_AcquireFingerprint(m_handle, imageBuffer, m_image_size, fpBuffer, &tempLen);

        //qDebug() << "AcquireFingerprint = " << ret;
        if (0 == ret)
        {
            qDebug() << "AcquireFingerprint = " << ret;
            qDebug() << "Template size = " << tempLen;
            if ( m_isFakeOn && !isRealFinger() )    //FakeFinger Test
            {
                emit fakeFinger();
                qDebug() << "fakeFinger";
            }
            else
            {
                for(int i = 0; i < m_image_height; i++)
                {
                    unsigned char * to = image.scanLine(i);
                    unsigned char * from  = imageBuffer + i * m_image_width;
                    memcpy(to, from, m_image_width);
                }
                QByteArray pfTemplate(reinterpret_cast<char *>(fpBuffer), tempLen);
                //fpTemplate.truncate(tempLen);
                emit onFingerprintReceived(image, pfTemplate);
                qDebug() << "emit onImageReceived(image, fpTemplate)";

            }
        }
        Sleep(100);
    }
    qDebug() << "Fingerprint sensor thead stoped";
}

void FingerprintReader::beginCapture()
{
    if(m_init && m_handle)
    {
        m_stop = false;
    }
    start();
}

void FingerprintReader::cancelCapture()
{
    m_stop = true;
}

FingerprintReader::~FingerprintReader()
{
    if(m_handle)
    {
        closeDevice();
    }
    autoTerminate();
}


}

FingerprintCacheDB

FingerprintCacheDB 类用于实现指纹的比对。 SDK 中提供的功能包括 1 对 1 的比对,和 1 对 N 的识别。所谓 1 对 N 指的是提前在 FingerprintCacheDB 类里加载 N 个指纹数据。然后提供一个新的指纹,判断这个指纹是否和那 N 个指纹中的某一个是同一个指纹。

下面还是直接给代码吧,首先是头文件:

#ifndef FINGERPRINTCACHEDB_H
#define FINGERPRINTCACHEDB_H
#include <QObject>
#include <windows.h>

namespace ZKFP
{

QString BlobToBase64(QByteArray regTemplate);
QByteArray Base64ToBlob(QString base64String);

/**
 * @brief The FingerprintCacheDB class 指纹识别类,用来进行 1:N 的指纹识别。
 *
 */
class FingerprintCacheDB : public QObject
{
    Q_OBJECT
public:
    FingerprintCacheDB(QObject *parent = nullptr);
    ~FingerprintCacheDB();
    /**
     * @brief verifyFinger 比较两个指纹是否是同一个手指的
     * @param regTemplate 指纹 1
     * @param verTemplate 指纹 2
     * @return 0 - 100 分数越高表明匹配度越好
     */
    int verifyFinger(QByteArray regTemplate, QByteArray verTemplate);
    /**
     * @brief mergeFinger 将三个指纹模板合并成一个
     * @param inTemplate1
     * @param inTemplate2
     * @param inTemplate3
     * @param outTemplate
     * @return
     */
    int mergeFinger(QByteArray inTemplate1, QByteArray inTemplate2, QByteArray inTemplate3, QByteArray &outTemplate);
    int identify(QByteArray inTemplate, unsigned int &fingerID, unsigned int &score);
    int identify(QString base64String, unsigned int &fingerID, unsigned int &score);
    /**
     * @brief count 返回指纹数据库中的数据条数
     * @return
     */
    int count() const;

public slots:
    /**
     * @brief clearAll 清空指纹数据库
     * @return
     */
    int clearAll();
    /**
     * @brief deleteItem 删除 fingerID 对应的数据
     * @param fingerID
     * @return
     */
    int deleteItem(unsigned int fingerID);
    /**
     * @brief addItem 将一个指纹模板登记到指纹数据库中,同时设置这个指纹模板的 ID 为 fingerID.
     * @param fingerID
     * @param regTemplate
     * @return
     */
    int addItem(unsigned int fingerID, QByteArray regTemplate);
    int addItem(unsigned int fingerID, QString base64String);

private:
    HANDLE m_hDBCache;//缓存区句柄
};

}
#endif // FINGERPRINTCACHEDB_H

然后是 cpp 文件:

#include "FingerprintCacheDB.h"
#include "ZKFingerReader/libs/include/libzkfp.h"
#include <QDebug>

namespace ZKFP
{

QString BlobToBase64(QByteArray regTemplate)
{
    int size = (regTemplate.size() * 8 + 5 )/ 6;
    char * str = new char[size + 1];
    memset(str, 0, size + 1);
    ZKFPM_BlobToBase64(reinterpret_cast<unsigned char *>(regTemplate.data()), regTemplate.size(),
                       str, size + 1);
    QString ret(str);

//    QString ret2 = regTemplate.toBase64();
//    if(ret == ret2)
//    {
//        qDebug() << "base64 is same";
//    }
    delete []str;
    return ret;
}

QByteArray Base64ToBlob(QString base64String)
{
    int size = base64String.size() * 6 / 8;
    unsigned char *blob = new unsigned char[size];
    int length = ZKFPM_Base64ToBlob(reinterpret_cast<char *> (base64String.data()), blob, size);
    QByteArray ret(reinterpret_cast<char*> (blob), length);
    delete [] blob;
    return ret;
}

FingerprintCacheDB::FingerprintCacheDB(QObject *parent)
    :QObject(parent),
      m_hDBCache(0)
{
    m_hDBCache = ZKFPM_DBInit();
}

FingerprintCacheDB::~FingerprintCacheDB()
{
    ZKFPM_DBFree(m_hDBCache);
}

int FingerprintCacheDB::clearAll()
{
    return ZKFPM_DBClear(m_hDBCache);
}

int FingerprintCacheDB::addItem(unsigned int fingerID, QString base64String)
{
    QByteArray blob = Base64ToBlob(base64String);
    return addItem(fingerID, blob);
}

int FingerprintCacheDB::addItem(unsigned int fingerID, QByteArray regTemplate)
{
    return ZKFPM_DBAdd(m_hDBCache, fingerID, reinterpret_cast<unsigned char *> (regTemplate.data()), regTemplate.size());
}

int FingerprintCacheDB::deleteItem(unsigned int fingerID)
{
    return ZKFPM_DBDel(m_hDBCache, fingerID);
}

int FingerprintCacheDB::count() const
{
    if(m_hDBCache)
    {
        int c;
        ZKFPM_DBCount(m_hDBCache, reinterpret_cast<unsigned int*> (&c));
        return c;
    }
    return 0;
}

int FingerprintCacheDB::verifyFinger(QByteArray regTemplate, QByteArray verTemplate)
{
    if(m_hDBCache)
    {
        int score = ZKFPM_DBMatch(m_hDBCache,
                                  reinterpret_cast<unsigned char *> (regTemplate.data()), regTemplate.size(),
                                  reinterpret_cast<unsigned char *> (verTemplate.data()), verTemplate.size());
        return score;
    }
    return -1;
}

int FingerprintCacheDB::mergeFinger(QByteArray in1, QByteArray in2, QByteArray in3, QByteArray &outTemplate)
{
    if(m_hDBCache)
    {
        unsigned int cbRegTemp = MAX_TEMPLATE_SIZE;
        outTemplate.resize(MAX_TEMPLATE_SIZE);
        outTemplate.fill(0);
        int ret = ZKFPM_DBMerge(m_hDBCache,
                                reinterpret_cast<unsigned char *> (in1.data()),
                                reinterpret_cast<unsigned char *> (in2.data()),
                                reinterpret_cast<unsigned char *> (in3.data()),
                                reinterpret_cast<unsigned char *> (outTemplate.data()), &cbRegTemp);
        outTemplate.resize(cbRegTemp);
        return ret;
    }
    return -1;
}

int FingerprintCacheDB::identify(QString base64String, unsigned int &fingerID, unsigned int &score)
{
    QByteArray fpTemp = Base64ToBlob(base64String);
    return identify(fpTemp, fingerID, score);
}

int FingerprintCacheDB::identify(QByteArray inTemplate, unsigned int &fingerID, unsigned int &score)
{
    return ZKFPM_DBIdentify(m_hDBCache,
                     reinterpret_cast<unsigned char *> (inTemplate.data()), inTemplate.size(),
                     &fingerID, &score);
}

}

FingerprintEnroll

FingerprintEnroll 用于将同一手指的三次采集到的指纹模板合成一个指纹模板。样合成后的指纹模板包含指纹信息更全面,判别准确率更高。这个过程的核心是个状态机,中间要判断每次获得的指纹是否合适,并剔除不合理的指纹。

下面是头文件;

#ifndef FINGERPRINTENROLL_H
#define FINGERPRINTENROLL_H

/**
 *
 */


#include <QObject>
#include <QByteArray>
#include <QImage>

namespace ZKFP
{

class FingerprintReader;
class FingerprintCacheDB;

/**
 * @brief The FingerprintEnroll class 辅助类,用于将同一手指的三次采集到的指纹模板合成一个指纹模板
 *                                    这样合成后的指纹模板包含指纹信息更全面,判别准确率更高。
 *                                    其实这个过程不应该叫 enroll, enroll 应该包括将结果注册到数据库中。
 *                                    但是没想好更好的词,所以暂时就叫这个词吧。
 */
class FingerprintEnroll : public QObject
{
    Q_OBJECT
public:
    explicit FingerprintEnroll(FingerprintCacheDB *cacheDB = nullptr, QObject *parent = nullptr);
    void setFingerprintCacheDB(FingerprintCacheDB *cacheDB);
    QByteArray regTemplate() const {return m_regTemplate;}
signals:
    void enrollFinished(QByteArray regTemplate);
    void enrollFailed();
    void fingerprintRejected();
    void fingerprintAccepted();
public slots:
    void reset();
    bool addFPTemplate(QByteArray fpTemplate);
    bool addFingerprint(QImage fingerImage, QByteArray fpTemplate);
    void beginEnroll(ZKFP::FingerprintReader * reader);
    void cancelEnroll();
private:
    FingerprintReader * m_reader;
    FingerprintCacheDB *m_cacheDB;
    QByteArray m_fpTemplate[3];
    QByteArray m_regTemplate;
    bool m_bRegister;
    int m_enrollIdx;
};

}
#endif // FINGERPRINTENROLL_H

之后是 cpp 文件:

#include "FingerprintEnroll.h"
#include "FingerprintCacheDB.h"
#include "FingerprintReader.h"
#include <QDebug>
namespace ZKFP
{

static const int ENROLLCNT  = 3;

FingerprintEnroll::FingerprintEnroll(FingerprintCacheDB *cacheDB, QObject *parent)
    : QObject(parent),
      m_cacheDB(0),
      m_reader(0),
      m_enrollIdx(0),
      m_bRegister(false)
{
    m_cacheDB = cacheDB;
}

void FingerprintEnroll::setFingerprintCacheDB(FingerprintCacheDB *cacheDB)
{
    m_cacheDB = cacheDB;
}

void FingerprintEnroll::reset()
{
    if(m_cacheDB && m_reader)
    {
        disconnect(m_reader, SIGNAL(onFingerprintReceived(QImage,QByteArray)),
                   this, SLOT(addFingerprint(QImage,QByteArray)));
    }
    m_enrollIdx = 0;
    m_bRegister = false;
    m_regTemplate.clear();
    m_fpTemplate[0].clear();
    m_fpTemplate[1].clear();
    m_fpTemplate[2].clear();

}

void FingerprintEnroll::beginEnroll(FingerprintReader * reader)
{
    reset();
    m_reader = reader;

    if(m_cacheDB && m_reader)
    {
        connect(m_reader, SIGNAL(onFingerprintReceived(QImage,QByteArray)),
                this, SLOT(addFingerprint(QImage,QByteArray)));
    }
    m_reader->beginCapture();
}

void FingerprintEnroll::cancelEnroll()
{
    reset();
    m_reader->cancelCapture();
}

bool FingerprintEnroll::addFingerprint(QImage fingerImage, QByteArray fpTemplate)
{
    Q_UNUSED(fingerImage);
    return addFPTemplate(fpTemplate);
}

bool FingerprintEnroll::addFPTemplate(QByteArray fpTemplate)
{
    qDebug() << "m_enrollIdx = " << m_enrollIdx;
    if(m_cacheDB == 0)
    {
        return false;
    }
    if (m_enrollIdx >= ENROLLCNT)
    {
        //reset();
        return false;
    }
    if (m_enrollIdx > 0)
    {
        int score = m_cacheDB->verifyFinger(m_fpTemplate[m_enrollIdx - 1], fpTemplate);
        qDebug() << "score = " << score;
        if (score <= 0)
        {
            //m_enrollIdx = 0;
            //m_bRegister = false;
            emit fingerprintRejected();
            qDebug() << "fingerprintRejected()";
            return false;
        }
    }
    m_fpTemplate[m_enrollIdx] = fpTemplate;

    if (++m_enrollIdx >= ENROLLCNT)
    {
        int ret = m_cacheDB->mergeFinger(m_fpTemplate[0], m_fpTemplate[1], m_fpTemplate[2], m_regTemplate);
        //m_enrollIdx = 0;
        m_bRegister = FALSE;
        if (0 == ret)
        {
            emit enrollFinished(m_regTemplate);
            disconnect(m_reader, SIGNAL(onFingerprintReceived(QImage,QByteArray)),
                this, SLOT(addFingerprint(QImage,QByteArray)));
            m_reader->cancelCapture();
            qDebug() << "enrollFinished()";
            return true;
        }
        else
        {
            emit enrollFailed();
            disconnect(m_reader, SIGNAL(onFingerprintReceived(QImage,QByteArray)),
                this, SLOT(addFingerprint(QImage,QByteArray)));
            qDebug() << "enrollFailed()";
            return false;
        }
        if(m_reader)
        {
            disconnect(m_reader, SIGNAL(onFingerprintReceived(QImage,QByteArray)),
                this, SLOT(addFingerprint(QImage,QByteArray)));
        }
    }
    else
    {
        emit fingerprintAccepted();
        qDebug() << "fingerprintAccepted()";
        return true;
    }
    return false;
}

}

FPCacheDBLoader

FPCacheDBLoader 是个数据库代码。用于将指纹存储到数据库,或者从数据库中读出。因为功能比较见到那,所以使用的是 SQLite。这个数据库功能比较少,但是对这个项目来说也足够了。这个代码只是个示例,不同的项目中肯定还要根据具体的需求来改写。

下面是头文件:

#ifndef FPCACHEDBLOADER_H
#define FPCACHEDBLOADER_H

#include <QObject>
#include <QList>
#include <QString>
#include <QMap>
#include <QSql>
#include <QSqlDatabase>

namespace ZKFP
{
class FingerprintCacheDB;
/**
 * @brief The FPCacheDBLoader class 辅助类,用于加载指纹数据库。
 */
class FPCacheDBLoader : public QObject
{
    Q_OBJECT
public:
    explicit FPCacheDBLoader(QObject *parent = nullptr);
    /**
     * @brief loadFromFile 从文件中加载指纹数据库,并将指纹数据送到 db
     * @param fileName 保存指纹数据的文件
     * @param db
     * @return true 表示成功, false 表示失败
     */
    bool loadFromFile(QString fileName, FingerprintCacheDB *fpDB);
    void initFPCacheDB(FingerprintCacheDB *fpDB);
    QList<int> userIDs();
    QString userName(int userID);
    QMap<int, QString> userNames();
    bool userInfo(int userID, QString &name, QString &password, QByteArray &fpTemplate);
    bool addUser(int userID, QString name, QString password, QByteArray fpTemplate);
    bool deleteUser(int userID);
signals:

public slots:

private:
    QSqlDatabase m_database;
};

}

#endif // FPCACHEDBLOADER_H

之后是 cpp 文件:

#include "FPCacheDBLoader.h"
#include <QSqlQuery>
#include <QDebug>
#include <QDir>
#include <QStandardPaths>
#include "ZKFingerReader/FingerprintCacheDB.h"

namespace ZKFP
{

FPCacheDBLoader::FPCacheDBLoader(QObject *parent) : QObject(parent)
{
    m_database =  QSqlDatabase::addDatabase("QSQLITE");
    QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QDir dir;
    dir.mkdir(path);
    path += "/fingerprintDB.db";
    qDebug() << path;
    m_database.setDatabaseName(path);
    m_database.open();
    QSqlQuery query(m_database);
    query.exec("CREATE TABLE IF NOT EXISTS [fingerprint]("
               "user_id INTEGER NOT NULL UNIQUE,"
               "name TEXT,"
               "password TEXT,"
               "fingerprint BLOB);");

}

QMap<int, QString> FPCacheDBLoader::userNames()
{
    QSqlQuery query(m_database);
    query.setForwardOnly(true);
    query.prepare("SELECT user_id, name FROM fingerprint");
    query.exec();
    QMap<int, QString> names;
    while( query.next( ) )
    {
        int id = query.value(0).toInt();
        QString name = query.value(1).toString();

        names[id] = name;
    }
    return names;
}

QList<int> FPCacheDBLoader::userIDs()
{
    QSqlQuery query(m_database);
    query.setForwardOnly(true);
    query.prepare("SELECT user_id FROM fingerprint");
    query.exec();
    QList<int> IDs;
    while( query.next( ) )
    {
        int id = query.value(0).toInt();
        IDs << id;
    }
    return IDs;
}

bool FPCacheDBLoader::addUser(int userID, QString name, QString password, QByteArray fpTemplate)
{
    QSqlQuery query(m_database);
    query.prepare("INSERT into fingerprint(user_id, name, password, fingerprint) VALUES(:user_id, :name,:password, :fingerprint) ;");
    query.bindValue(":user_id", userID);
    query.bindValue(":name", name);
    query.bindValue(":password", password);
    query.bindValue(":fingerprint", fpTemplate);
    return query.exec();
}

bool FPCacheDBLoader::userInfo(int userID, QString &name, QString &password, QByteArray &fpTemplate)
{
    QSqlQuery query(m_database);
    query.setForwardOnly(true);
    query.prepare("SELECT * FROM fingerprint WHERE user_id = :id");
    query.bindValue(":id", userID);
    query.exec();
    if( query.next( ) )
    {
        name = query.value(1).toString();
        password = query.value(2).toString();
        fpTemplate = query.value(3).toByteArray();
        return true;
    }
    return false;
}

bool FPCacheDBLoader::deleteUser(int userID)
{
    QSqlQuery query(m_database);
    query.prepare("DELETE FROM fingerprint WHERE user_id = :id");
    query.bindValue(":id", userID);
    return query.exec();
}

QString FPCacheDBLoader::userName(int userID)
{
    QSqlQuery query(m_database);
    query.setForwardOnly(true);
    query.prepare("SELECT name FROM fingerprint WHERE user_id = :id");
    query.bindValue(":id", userID);
    query.exec();
    if( query.next( ) )
    {
        QString name = query.value(0).toString();
        return name;
    }
    return QString();
}

void FPCacheDBLoader::initFPCacheDB(FingerprintCacheDB *fpDB)
{
    fpDB->clearAll();
    QSqlQuery query(m_database);
    query.setForwardOnly(true);
    query.exec("SELECT user_id, fingerprint FROM fingerprint");
    while( query.next( ) )
    {
        int fid = query.value(0).toInt();
        QByteArray fpTemplate = query.value(1).toByteArray();
        fpDB->addItem(fid, fpTemplate);
        qDebug() << "fid = " << fid;
    }
}

bool FPCacheDBLoader::loadFromFile(QString fileName, ZKFP::FingerprintCacheDB *fpDB)
{
    QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE");
    database.setDatabaseName(fileName);

    if(database.open())
    {
        qDebug() << "database open succ";
        fpDB->clearAll();
        QSqlQuery query(database);
        query.setForwardOnly(true);
        query.exec("SELECT user_id, fingerprint FROM fingerprint");
        while( query.next( ) )
        {
            int fid = query.value(0).toInt();
            QByteArray fpTemplate = query.value(1).toByteArray();
            fpDB->addItem(fid, fpTemplate);
        }
        return true;
    }
    return false;
}


}

猜你喜欢

转载自blog.csdn.net/liyuanbhu/article/details/78226521