1. 程式人生 > >電容式 指紋識別 android 智慧硬體

電容式 指紋識別 android 智慧硬體

目前,各個手機中,都是用的電容式指紋識別模組
公司打卡機,大都是光學指紋模組,相對上者便宜一些;

1.現在陳列一些資料;

基於stm32和fpc1011c2的指紋採集系統
指紋識別的模組,現在主流的是,fpc1011或者1020,好多廠家,為了保護演算法,整合演算法後,開放串列埠,讓你用at指令來操作,就和操作sim900a,或者藍芽hc-06一樣,
這裡寫圖片描述
但我們這裡不是用這麼簡單的用,我們要自己封裝演算法,用fpc1011採集器+stm32來做;
這裡寫圖片描述

2.學習充電

對不瞭解,數字影象處理的,請下載:第三版,數字影象處理,岡薩雷斯,中文版;
想用matlab實現的,請下載數字影象處理(matlab版),岡薩雷斯
二者不同,我第一次就買錯了;
這裡寫圖片描述


教程:
這裡寫圖片描述
視訊教程:百度雲盤 韓春梅,或者回複評論,我發給你郵箱;

3.學習思路

1.網上一大堆,matlab實現的程式碼,各個流程,我也不多說,確實可以實現,滿足大學生畢設沒問題,但是要想把每一步,都理解透,返回第二步,學習充電;
2.用vc++,或者c++等寫的上位機,來單獨處理指紋圖片;
3.用vc++,或者c++等上位機,處理,fpc1011的spi輸出的資料,檢視對比,充分利用了pc的處理能力;
4,用matlab處理,fpc1011的spi輸出的資料,檢視對比;
5,用stm32、dsp等mcu來和fpc1011直接通訊,處理資料需要擴充套件sram或者外加flash,儲存某些特徵值或者圖片;

4.來看看fpc1011資料手冊

這裡寫圖片描述

其實,主要還是看看看這些指令;
這裡寫圖片描述
讀出資料後,就需按照資料影象處理流程來做,具體C語言實現,下面提供幾個檔案;

5.影象增強演算法(c語言):

我的stm32 最終版,程式碼,除錯好了,之後,上傳,請稍後;

/*#############################################################################
 * 檔名:imageenhance.c
 * 功能:  實現了影象增強演算法
 * 
#############################################################################*/
#include <math.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> #include "imagemanip.h" /****************************************************************************** ** 影象增強部分 ** ** 該增強演算法針對指紋影象設計,它標記了指紋影象中沒有使用的區域,而其它的區域 ** 在增強後,脊線可以被清晰的分離出來(使用一個閾值)。 ** ** 該演算法生成了一個脊線方向圖,一個掩碼圖。 ** ** 可參考如下兩篇文章: ** 1 - Fingerprint Enhancement: Lin Hong, Anil Jain, Sharathcha Pankanti, ** and Ruud Bolle. [Hong96] ** 2 - Fingerprint Image Enhancement, Algorithm and Performance Evaluation: ** Lin Hong, Yifei Wan and Anil Jain. [Hong98] ** ** 增強演算法使用了 文獻(2) 中的幾個步驟: ** A - 歸一化 ** B - 計算方向圖 ** C - 計算頻率 ** D - 計算區域掩碼 ** E - 濾波 ** ******************************************************************************/ #define P(x,y) ((int32_t)p[(x)+(y)*pitch]) /****************************************************************************** ** 採用了Gabor方向濾波器,如下: ** ** / 1|x' y' |\ ** h(x,y:phi,f) = exp|- -|--- + ---| |.cos(2.PI.f.x') ** \ 2|dx dy |/ ** ** x' = x.cos(phi) + y.sin(phi) ** y' = -x.sin(phi) + y.cos(phi) ** ** 定義如下: ** G 歸一化後的影象 ** O 方向圖 ** F 頻率圖 ** R 掩碼影象 ** E 增強後的影象 ** Wg Gabor濾波器視窗大小 ** ** / 255 if R(i,j) = 0 ** | ** | Wg/2 Wg/2 ** | --- --- ** E(i,j)= | \ \ ** | -- -- h(u,v:O(i,j),F(i,j)).G(i-u,j-v) otherwise ** | / / ** \ --- --- ** u=-Wg/2 v=-Wg/2 ** ******************************************************************************/ inline FvsFloat_t EnhanceGabor(FvsFloat_t x, FvsFloat_t y, FvsFloat_t phi, FvsFloat_t f, FvsFloat_t r2) { FvsFloat_t dy2 = 1.0/r2; FvsFloat_t dx2 = 1.0/r2; FvsFloat_t x2, y2; phi += M_PI/2; x2 = -x*sin(phi) + y*cos(phi); y2 = x*cos(phi) + y*sin(phi); return exp(-0.5*(x2*x2*dx2 + y2*y2*dy2))*cos(2*M_PI*x2*f); } static FvsError_t ImageEnhanceFilter ( FvsImage_t normalized, const FvsImage_t mask, const FvsFloat_t* orientation, const FvsFloat_t* frequence, FvsFloat_t radius ) { FvsInt_t Wg2 = 8; FvsInt_t i,j, u,v; FvsError_t nRet = FvsOK; FvsImage_t enhanced = NULL; FvsInt_t w = ImageGetWidth (normalized); FvsInt_t h = ImageGetHeight(normalized); FvsInt_t pitchG = ImageGetPitch (normalized); FvsByte_t* pG = ImageGetBuffer(normalized); FvsFloat_t sum, f, o; /* 平方 */ radius = radius*radius; enhanced = ImageCreate(); if (enhanced==NULL || pG==NULL) return FvsMemory; if (nRet==FvsOK) nRet = ImageSetSize(enhanced, w, h); if (nRet==FvsOK) { FvsInt_t pitchE = ImageGetPitch (enhanced); FvsByte_t* pE = ImageGetBuffer(enhanced); if (pE==NULL) return FvsMemory; (void)ImageClear(enhanced); for (j = Wg2; j < h-Wg2; j++) for (i = Wg2; i < w-Wg2; i++) { if (mask==NULL || ImageGetPixel(mask, i, j)!=0) { sum = 0.0; o = orientation[i+j*w]; f = frequence[i+j*w]; for (v = -Wg2; v <= Wg2; v++) for (u = -Wg2; u <= Wg2; u++) { sum += EnhanceGabor ( (FvsFloat_t)u, (FvsFloat_t)v, o,f,radius ) * pG[(i-u)+(j-v)*pitchG]; } if (sum>255.0) sum = 255.0; if (sum<0.0) sum = 0.0; pE[i+j*pitchE] = (uint8_t)sum; } } nRet = ImageCopy(normalized, enhanced); } (void)ImageDestroy(enhanced); return nRet; } /* }}} */ /****************************************************************************** * 功能:指紋影象增強演算法 * 該演算法描述起來比較複雜,其後處理的部分是基於Gabor濾波器的, 引數動態計算。影象處理時引數依次改變,所以要做一個原圖的備份。 * 引數:image 指紋影象 * direction 脊線方向,需要事先計算 * frequency 脊線頻率,需要事先計算 * mask 指示指紋的有效區域 * radius 濾波器半徑,大多數情況下,4.0即可。 值越大,噪聲可以受到更大抑制,但會產生更多的偽特徵。 * 返回:錯誤編號 ******************************************************************************/ FvsError_t ImageEnhanceGabor(FvsImage_t image, const FvsFloatField_t direction, const FvsFloatField_t frequency, const FvsImage_t mask, const FvsFloat_t radius) { FvsError_t nRet = FvsOK; FvsFloat_t * image_orientation = FloatFieldGetBuffer(direction); FvsFloat_t * image_frequence = FloatFieldGetBuffer(frequency); if (image_orientation==NULL || image_frequence==NULL) return FvsMemory; nRet = ImageEnhanceFilter(image, mask, image_orientation, image_frequence, radius); return nRet; }

6.實現了指紋直方圖的操作

/*#############################################################################
 * 檔名:histogram.c
 * 功能:  實現了指紋直方圖的操作
 * 
#############################################################################*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "histogram.h"

/* 直方圖可以快速計算點陣圖的一些資訊,比如均值,方差等 */
typedef struct iFvsHistogram_t
{
    FvsUint_t       ptable[256];    /* 8點陣圖像的直方圖 */
    FvsInt_t        ncount;         /* 直方圖中的點數 */
    FvsInt_t        nmean;          /* -1 = 還沒有計算 */
    FvsInt_t        nvariance;      /* -1 = 還沒有計算 */
} iFvsHistogram_t;


/******************************************************************************
  * 功能:建立一個新的直方圖物件
  * 引數:無
  * 返回:失敗返回空,否則返回直方圖物件
******************************************************************************/
FvsHistogram_t HistogramCreate()
{
    iFvsHistogram_t* p = NULL;
    p = (FvsHistogram_t)malloc(sizeof(iFvsHistogram_t));

    if (p!=NULL)
    {
        /* 重置表 */
        HistogramReset(p);
    }
    return (FvsHistogram_t)p;
}


/******************************************************************************
  * 功能:破壞一個存在的直方圖物件
  * 引數:histogram 直方圖物件指標
  * 返回:錯誤編號
******************************************************************************/
void HistogramDestroy(FvsHistogram_t histogram)
{
    iFvsHistogram_t* p = NULL;
    if (histogram==NULL)
        return;
    p = histogram;
    free(p);
}


/******************************************************************************
  * 功能:重置一個存在的直方圖物件為0
  * 引數:histogram 直方圖物件指標
  * 返回:錯誤編號
******************************************************************************/
FvsError_t HistogramReset(FvsHistogram_t hist)
{
    iFvsHistogram_t* histogram = (iFvsHistogram_t*)hist;
    int i;
    for (i = 0; i < 256; i++)
        histogram->ptable[i] = 0;
    histogram->ncount    = 0;
    histogram->nmean     = -1;
    histogram->nvariance = -1;
    return FvsOK;
}


/******************************************************************************
  * 功能:計算一個8-bit影象的直方圖
  * 引數:histogram 直方圖物件指標
  *       image     影象指標
  * 返回:錯誤編號
******************************************************************************/
FvsError_t HistogramCompute(FvsHistogram_t hist, const FvsImage_t image)
{
    iFvsHistogram_t* histogram = (iFvsHistogram_t*)hist;
    FvsError_t nRet = FvsOK;
    FvsInt_t w      = ImageGetWidth(image);
    FvsInt_t h      = ImageGetHeight(image);
    FvsInt_t pitch  = ImageGetPitch(image);
    uint8_t* p      = ImageGetBuffer(image);
    FvsInt_t x, y;

    if (histogram==NULL || p==NULL)
        return FvsMemory;

    /* 首先重置直方圖 */
    nRet = HistogramReset(hist);
    /* 計算 */
    if (nRet==FvsOK)
    {
        FvsInt_t pos;
        for (y=0; y<h; y++)
        {
            pos = pitch*y;
            for (x=0; x<w; x++)
            {
                histogram->ptable[p[pos++]]++;
            }
        }
        histogram->ncount = w*h;
    }

    return nRet;
}


/******************************************************************************
  * 功能:計算一個直方圖物件的均值
  * 引數:histogram 直方圖物件指標
  * 返回:均值
******************************************************************************/
FvsByte_t HistogramGetMean(const FvsHistogram_t hist)
{
    iFvsHistogram_t* histogram = (iFvsHistogram_t*)hist;
    FvsInt_t val, i;

    val = histogram->nmean;
    if (val==-1)
    {
        val = 0;
        for (i = 1; i < 255; i++)
            val += i*histogram->ptable[i];

        i = histogram->ncount;
        if (i>0)
            val = val/i;
        else
            val = 0;

        histogram->nmean = val;
    }
    return (uint8_t)val;
}


/******************************************************************************
  * 功能:計算一個直方圖物件的方差
  * 引數:histogram 直方圖物件指標
  * 返回:方差
******************************************************************************/
FvsUint_t HistogramGetVariance(const FvsHistogram_t hist)
{
    iFvsHistogram_t* histogram = (iFvsHistogram_t*)hist;
    FvsInt_t val;
    FvsInt_t i;
    uint8_t mean;

    val = histogram->nvariance;
    if (val==-1)
    {
        /* 計算均值 */
        mean = HistogramGetMean(hist);
        val  = 0;
        for (i = 0; i < 255; i++)
            val += histogram->ptable[i]*(i - mean)*(i - mean);

        i = histogram->ncount;
        if (i>0)
            val = val/i;
        else
            val = 0;

        histogram->nvariance = val;
    }
    return (FvsUint_t)val;
}



7.實現了主要的影象形態學操作

/*#############################################################################
 * 檔名:img_morphology.c
 * 功能:  實現了主要的影象形態學操作
 * 
#############################################################################*/


#include "img_base.h"

#include <string.h>


#define P(x,y)      p[(x)+(y)*pitch]

/******************************************************************************
  * 功能:影象膨脹演算法
  * 引數:image   指紋影象
  * 返回:錯誤編號
******************************************************************************/
FvsError_t ImageDilate(FvsImage_t image)
{
    FvsInt_t w      = ImageGetWidth (image);
    FvsInt_t h      = ImageGetHeight(image);
    FvsInt_t pitch  = ImageGetPitch (image);
    FvsInt_t size   = ImageGetSize  (image);
    FvsByte_t* p    = ImageGetBuffer(image);
    FvsInt_t x,y;

    if (p==NULL)
        return FvsMemory;

    for (y=1; y<h-1; y++)
    for (x=1; x<w-1; x++)
    {
        if (P(x,y)==0xFF)
        {
            P(x-1, y) |= 0x80;
            P(x+1, y) |= 0x80;
            P(x, y-1) |= 0x80;
            P(x, y+1) |= 0x80;
        }
    }

    for (y=0; y<size; y++)
        if (p[y])
            p[y] = 0xFF;

    return FvsOK;
}


/******************************************************************************
  * 功能:影象腐蝕演算法
  * 引數:image   指紋影象
  * 返回:錯誤編號
******************************************************************************/
FvsError_t ImageErode(FvsImage_t image)
{
    FvsInt_t w      = ImageGetWidth (image);
    FvsInt_t h      = ImageGetHeight(image);
    FvsInt_t pitch  = ImageGetPitch (image);
    FvsInt_t size   = ImageGetSize  (image);
    FvsByte_t* p    = ImageGetBuffer(image);
    FvsInt_t x,y;

    if (p==NULL)
        return FvsMemory;

    for (y=1; y<h-1; y++)
    for (x=1; x<w-1; x++)
    {
        if (P(x,y)==0x0)
        {
            P(x-1, y) &= 0x80;
            P(x+1, y) &= 0x80;
            P(x, y-1) &= 0x80;
            P(x, y+1) &= 0x80;
        }
    }

    for (y=0; y<size; y++)
        if (p[y]!=0xFF)
            p[y] = 0x0;

    return FvsOK;
}


8.實現了影象細化操作

/*#############################################################################
 * 檔名:img_thin.c
 * 功能:  實現了影象細化操作
 *
#############################################################################*/

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

#include "imagemanip.h"

#ifndef min
#define min(a,b) (((a)<(b))?(a):(b))
#endif

#define NOT_BK(pos) (image[pos]!=0)
#define IS_BK(pos)  (image[pos]==0)


bool_t MatchPattern(uint8_t image[], int x, int y, int w, int h)
{
    bool_t nRet = false;

    /* 驗證有無出界 */
    int lhe = y * w;        /* 本行   */
    int lup = lhe - w;      /* 上一行 */
    int ldo = lhe + w;      /* 下一行 */

    int tl = lup + x - 1;   /* 左上 */
    int tc = lup + x;       /* 中上 */
    int tr = lup + x + 1;   /* 右上 */
    int hl = lhe + x - 1;   /* 左   */
    int hr = lhe + x + 1;   /* 右   */
    int bl = ldo + x - 1;   /* 左下 */
    int bc = ldo + x;       /* 中下 */
    int br = ldo + x + 1;   /* 右下 */

    /* 第一模式
        ? ? ? one not 0
        0 1 0
        ? ? ? one not 0
    */
    if  ( image[hr]==0  &&  image[hl]==0  &&
        ((image[tl]!=0) || (image[tc]!=0) || (image[tr]!=0))&&
        ((image[bl]!=0) || (image[bc]!=0) || (image[br]!=0))
        )
    {
        nRet = true;
    }
    /* 同樣的旋轉90度
        ? 0 ?
        ? 1 ?
        ? 0 ?
    */
    else
    if  ( image[tc]==0  &&  image[bc]==0  &&
        ((image[bl]!=0) || (image[hl]!=0) || (image[tl]!=0))&&
        ((image[br]!=0) || (image[hr]!=0) || (image[tr]!=0))
        )
    {
        nRet = true;
    }
    /*
        ? ? ?
        ? 1 0
        ? 0 1
    */
    else
    if
        (image[br]==0xFF     &&  image[hr]==0  &&  image[bc]==0  &&
        (image[tr]!=0   ||  image[tc]!=0   ||
         image[tl]!=0   ||  image[hl]!=0   || image[bl]!=0)
         ) 
    {
        nRet = true;
    }
    /*
        ? ? ?
        0 1 ?
        1 0 ?
    */
    else
    if
        (image[bl]==0xFF     &&  image[hl]==0  &&  image[bc]==0   &&
        (image[br]!=0   ||  image[hr]!=0  ||
         image[tr]!=0   ||  image[tc]!=0  ||  image[tl]!=0))
    {
        nRet = true;
    }
    /*
        1 0 ?
        0 1 ?
        ? ? ?
    */
    else
    if
        (image[tl]==0xFF     &&  image[tc]==0  &&  image[hl]==0   &&
        (image[bl]!=0   ||  image[bc]!=0  ||
         image[br]!=0   ||  image[hr]!=0  ||  image[tr]!=0))
    {
        nRet = true;
    }
    /*
        ? 0 1
        ? 1 0
        ? ? ?
    */
    else
    if
        (image[tr]==0xFF     &&  image[hr]==0  &&  image[tc]==0   &&
        (image[tl]!=0   ||  image[hl]!=0  ||
         image[bl]!=0   ||  image[bc]!=0  ||  image[br]!=0))
    {
        nRet = true;
    }

    image[y*w + x] = (nRet==true)?0xFF:0x00;

    return nRet;
}


/* 細化影象 */
FvsError_t ImageThin3(Image_t imgf)
{
    bool_t Remain;
    int temp;
    uint8_t* image = ImageGetBuffer(imgf);
    register int x, y;
    int w = ImageGetWidth(imgf);  /* 影象寬度 */
    int h = ImageGetHeight(imgf); /* 影象高度 */
    int tmp;
    int row;

    /* 提高細化速度 */
    int _lastY;
    int _newY;

    /* 初始化 */
    _lastY = _newY = 1;

    /* 標記:全部完成後再處理 */
    Remain = true;
    while (Remain)
    {
        _lastY = 1;

        _newY = h;

        Remain = false;
        fprintf(stderr, ".");

        temp   = false;
        for (y = _lastY; y < h-1; y++)
            for (x = 1; x < w-1; x++)
            {
                row = y*w;
                tmp = image[row +(x + 1)]; 
                if (image[row + x] == 0xFF && tmp == 0
                    && MatchPattern(image, x, y, w, h) == false)
                    if (temp==false)
                    {
                        _newY  = min(_newY, y);
                        Remain = true;
                        temp   = true;
                    }
            } 

        for (x = w*_lastY; x < w*h; x++)
            if (image[x] == 0x00)
                image[x] = 0;

        temp   = false;
        for (y = _lastY; y < h-1; y++)
            for (x = 1; x < w-1; x++)
            {
                row = y*w; 
                tmp = image[(y - 1) * w + x]; 
                if (image[row + x] == 0xFF && tmp == 0
                    && MatchPattern(image, x, y, w, h)==false)
                    if (temp==false)
                    {
                        _newY  = min(_newY, y);
                        Remain = true;
                        temp   = true;
                    }
            } /* end for y */

        for (x = w*_lastY; x < w*h; x++)
            if (image[x] == 0x00)
                image[x] = 0;

        temp   = false;
        for (y = _lastY; y < h-1; y++)
            for (x = 1; x < w-1; x++)
            {
                row = y*w;
                tmp = image[row +(x - 1)]; /* -> */
                if (image[row + x] == 0xFF && tmp == 0
                    && MatchPattern(image, x, y, w, h)==false)
                    if (temp==false)
                    {
                        _newY  = min(_newY, y);
                        Remain = true;
                        temp   = true;
                    }
            } /* end for y */

        for (x = w*_lastY; x < w*h; x++)
            if (image[x] == 0x00)
                image[x] = 0;

        temp   = false;
        for (y = _lastY; y < h-1; y++)
            for (x = 1; x < w-1; x++)
            {
                row = y*w;                    
                tmp = image[(y + 1) * w + x]; 
                if (image[row + x] == 0xFF && tmp == 0
                    && MatchPattern(image, x, y, w, h)==false)
                    if (temp==false)
                    {
                        _newY  = min(_newY, y);
                        Remain = true;
                        temp   = true;
                    }
            } /* end for y */

        for (x = w*_lastY; x < w*h; x++)
            if (image[x] == 0x00)
                image[x] = 0;

    } /* end while */

    return FvsOK;
}


9.一些基本的影象操作

/*#############################################################################
 * 檔名:img_base.c
 * 功能:  一些基本的影象操作
 * 
#############################################################################*/


#include "img_base.h"

#include "histogram.h"

#include <math.h>
#include <stdlib.h>
#include <string.h>


/******************************************************************************
  * 功能:影象二值化
  * 引數:image       指紋影象
  *       size        閾值
  * 返回:錯誤編號
******************************************************************************/
FvsError_t ImageBinarize(FvsImage_t image, const FvsByte_t limit)
{
    FvsInt_t n;
    FvsByte_t *pimg = ImageGetBuffer(image);
    FvsInt_t size = ImageGetSize(image);
    if (pimg==NULL)
        return FvsMemory;
    /* 迴圈遍歷 */
    for (n = 0; n < size; n++, pimg++)
    {
        /* 閾值化 */
        *pimg = (*pimg < limit)?(FvsByte_t)0xFF:(FvsByte_t)0x00;
    }
    return ImageSetFlag(image, FvsImageBinarized);
}


/******************************************************************************
  * 功能:影象翻轉操作
  * 引數:image       指紋影象
  * 返回:錯誤編號
******************************************************************************/
FvsError_t ImageInvert(FvsImage_t image)
{
    FvsByte_t* pimg = ImageGetBuffer(image);
    FvsInt_t size = ImageGetSize(image);
    FvsInt_t n;
    if (pimg==NULL)
        return FvsMemory;
    for (n = 0; n < size; n++, pimg++)
    {
        *pimg = 0xFF - *pimg;
    }
    return FvsOK;
}


/******************************************************************************
  * 功能:影象合併操作
  * 引數:image1    第一個指紋影象,用於儲存結果
  *       image2    第二個指紋影象
  * 返回:錯誤編號
******************************************************************************/
FvsError_t ImageAverage(FvsImage_t image1, const FvsImage_t image2)
{
    FvsByte_t* p1 = ImageGetBuffer(image1);
    FvsByte_t* p2 = ImageGetBuffer(image2);
    FvsInt_t size1 = ImageGetSize(image1);
    FvsInt_t size2 = ImageGetSize(image2);
    FvsInt_t i;

    if (p1==NULL || p2==NULL)
        return FvsMemory;
    if (size1!=size2)
        return FvsBadParameter;

    for (i = 0; i < size1; i++, p1++)
    {
        *p1 = (*p1+*p2++)>>1;
    }
    return FvsOK;
}



/******************************************************************************
  * 功能:影象邏輯合併操作
  * 引數:image1    第一個指紋影象,用於儲存結果
  *       image2    第二個指紋影象
  * 返回:錯誤編號
******************************************************************************/
FvsError_t ImageLogical
    (
    FvsImage_t image1,
    const FvsImage_t image2,
    const FvsLogical_t operation
    )
{
    FvsByte_t* p1 = ImageGetBuffer(image1);
    FvsByte_t* p2 = ImageGetBuffer(image2);
    FvsInt_t size1 = ImageGetSize(image1);
    FvsInt_t i;

    if (p1==NULL || p2==NULL)
        return FvsMemory;
    if (ImageCompareSize(image1, image2)==FvsFalse)
        return FvsBadParameter;

    switch (operation)
    {
    case FvsLogicalOr:
        for (i = 0; i < size1; i++, p1++)
            *p1 = (*p1) | (*p2++);                    
        break;
    case FvsLogicalAnd:
        for (i = 0; i < size1; i++, p1++)
            *p1 = (*p1) & (*p2++);
        break;
    case FvsLogicalXor:
        for (i = 0; i < size1; i++, p1++)
            *p1 = (*p1) ^ (*p2++);
        break;
    case FvsLogicalNAnd:
        for (i = 0; i < size1; i++, p1++)
            *p1 = ~((*p1) & (*p2++));
        break;
    case FvsLogicalNOr:
        for (i = 0; i < size1; i++, p1++)
            *p1 = ~((*p1) | (*p2++));
        break;
    case FvsLogicalNXor:
        for (i = 0; i < size1; i++, p1++)
            *p1 = ~((*p1) ^ (*p2++));
        break;
    }
    return FvsOK;
}


/******************************************************************************
  * 功能:影象合併操作
  *       使用了模計算,0和255的結果是0而不是上一個函式的127。
  * 引數:image1    第一個指紋影象,用於儲存結果
  *       image2    第二個指紋影象
  * 返回:錯誤編號
******************************************************************************/
FvsError_t ImageAverageModulo(FvsImage_t image1, const FvsImage_t image2)
{
    FvsByte_t* p1 = ImageGetBuffer(image1);
    FvsByte_t* p2 = ImageGetBuffer(image2);