电容式 指纹识别 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);
    FvsInt_t size1 = ImageGetSize(image1);
    FvsInt_t size2 = ImageGetSize(image2);
    FvsInt_t i;
    FvsByte_t v1, v2;

    if (size1!=size2)
        return FvsBadParameter;

    if (p1==NULL || p2==NULL)
        return FvsMemory;

    for (i = 0; i < size1; i++)
    {
        v1 = *p1;
        v2 = *p2;
        if (v1<128) v1+=256;
        if (v2<128) v2+=256;
        v1 += v2;
        v1 >>=1;
        v1 = v1%256;
        *p1++ = (uint8_t)v1;
    }
    return FvsOK;
}


/******************************************************************************
  * 功能:图像平移操作
  * 参数:image    指纹图像
  *       vx       X方向的平移量
  *       vy       Y方向的平移量
  * 返回:错误编号
******************************************************************************/
FvsError_t ImageTranslate(FvsImage_t image, const FvsInt_t vx, const FvsInt_t vy)
{
    return FvsOK;
}


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


/******************************************************************************
  * 功能:图像纹理
  * 参数:image       指纹图像
  *       horizontal  水平或垂直纹理
  * 返回:错误编号
******************************************************************************/
FvsError_t ImageStripes(FvsImage_t image, const FvsBool_t horizontal)
{
    FvsByte_t* p = ImageGetBuffer(image);
    FvsInt_t w     = ImageGetWidth (image);
    FvsInt_t h     = ImageGetHeight(image);
    FvsInt_t pitch = ImageGetPitch (image);
    FvsInt_t x,y;
    if (p==NULL)
        return FvsMemory;
    if (horizontal==FvsFalse)
    {
        for (y = 0; y < h; y++)
        for (x = 0; x < w; x++)
            P(x,y) = (FvsByte_t)x%256;
    }
    else
    {
        for (y = 0; y < h; y++)
        for (x = 0; x < w; x++)
            P(x,y) = (FvsByte_t)y%256;
    }
    return FvsOK;
}


/******************************************************************************
  * 功能:改变图像的发光度,使其在[255..255]之间变动
  * 参数:image         指纹图像
  *       luminosity    相关的发光度
  * 返回:错误编号
******************************************************************************/
FvsError_t ImageLuminosity(FvsImage_t image, const FvsInt_t luminosity)
{
    FvsByte_t* p = ImageGetBuffer(image);
    FvsInt_t  w = ImageGetWidth (image);
    FvsInt_t  h = ImageGetHeight(image);
    FvsInt_t pitch = ImageGetPitch (image);
    FvsInt_t x,y;
    FvsFloat_t fgray, a, b;
    if (p==NULL)
        return FvsMemory;
    if (luminosity>0)
    {
        a = (255.0 - abs(luminosity)) / 255.0;
        b = (FvsFloat_t)luminosity;
    }
    else
    {
        a = (255.0 - abs(luminosity)) / 255.0;
        b = 0.0;
    }
    for (y = 0; y < h; y++)
    for (x = 0; x < w; x++)
    {
        fgray = (FvsFloat_t)P(x,y);
        fgray = b + a*fgray;
        if (fgray < 0.0)    fgray = 0.0;
        if (fgray > 255.0)  fgray = 255.0;
        P(x,y)= (uint8_t)fgray;
    }
    return FvsOK;
}


/******************************************************************************
  * 功能:改变图像的对比度,使其在[-127..127]变动
  * 参数:image      指纹图像
  *       contrast   对比度因子
  * 返回:错误编号
******************************************************************************/
FvsError_t ImageContrast(FvsImage_t image, const FvsInt_t contrast)
{
    FvsByte_t* p = ImageGetBuffer(image);
    FvsInt_t  w = ImageGetWidth (image);
    FvsInt_t  h = ImageGetHeight(image);
    FvsInt_t pitch = ImageGetPitch (image);
    FvsInt_t x,y;
    FvsFloat_t fgray, a, b;
    if (p==NULL)
        return FvsMemory;
    a = (FvsFloat_t)((127.0 + contrast) / 127.0);
    b = (FvsFloat_t)(-contrast);
    for (y = 0; y < h; y++)
    for (x = 0; x < w; x++)
    {
        fgray = (FvsFloat_t)P(x,y);
        fgray = b + a*fgray;
        if (fgray < 0.0)    fgray = 0.0;
        if (fgray > 255.0)  fgray = 255.0;
        P(x,y)= (uint8_t)fgray;
    }
    return FvsOK;
}


/******************************************************************************
  * 功能:图像软化操作,通过计算均值实现
  * 参数:image     指纹图像
  *       size      软化窗口大小
  * 返回:错误编号
******************************************************************************/
FvsError_t ImageSoftenMean(FvsImage_t image, const FvsInt_t size)
{
    FvsByte_t* p1  = ImageGetBuffer(image);
    FvsByte_t* p2;
    FvsInt_t   w   = ImageGetWidth (image);
    FvsInt_t   h   = ImageGetHeight(image);
    FvsInt_t pitch = ImageGetPitch (image);
    FvsInt_t pitch2;
    FvsInt_t x,y,s,p,q,a,c;
    FvsImage_t im2;

    im2 = ImageCreate();

    if (im2==NULL || p1==NULL)
        return FvsMemory;

    s = size/2;     /* 大小 */
    a = size*size;  /* 面积 */    
    if (a==0)
    return FvsBadParameter;

    /* 拷贝图像进行计算 */
    ImageCopy(im2, image);
    p2 = ImageGetBuffer(im2);
    if (p2==NULL)
    {
    ImageDestroy(im2);
    return FvsMemory;
    }
    pitch2 = ImageGetPitch (im2);

    for (y = s; y < h-s; y++)
    for (x = s; x < w-s; x++)
    {
    c = 0;
    for (q=-s;q<=s;q++)
    for (p=-s;p<=s;p++)
    {
            c += p2[(x+p)+(y+q)*pitch2];
    }
        p1[x+y*pitch] = c/a;
    }

    ImageDestroy(im2);
    return FvsOK;
}


/******************************************************************************
  * 功能:图像归一化操作,使其具有给定的均值和方差
  * 参数:image     指纹图像
  *       mean      给定的均值
  *       variance  给定的标准方差
  * 返回:错误编号
******************************************************************************/
FvsError_t ImageNormalize(FvsImage_t image, const FvsByte_t mean, const FvsUint_t variance)
{
    FvsByte_t* p = ImageGetBuffer(image);
    FvsInt_t   w = ImageGetWidth (image);
    FvsInt_t   h = ImageGetHeight(image);
    FvsInt_t   pitch = ImageGetPitch (image);
    FvsInt_t   x,y;
    FvsFloat_t fmean, fsigma, fmean0, fsigma0, fgray;
    FvsFloat_t fcoeff = 0.0;

    FvsHistogram_t histogram = NULL;
    FvsError_t nRet;

    if (p==NULL)
        return FvsMemory;
    histogram = HistogramCreate();
    if (histogram!=NULL)
    {
        /* 计算直方图 */
        nRet = HistogramCompute(histogram, image);
        if (nRet==FvsOK)
        {
            /* 计算方差和均值 */
            fmean   = (FvsFloat_t)HistogramGetMean(histogram);
            fsigma  = sqrt((FvsFloat_t)HistogramGetVariance(histogram));

            fmean0  = (FvsFloat_t)mean;
            fsigma0 = sqrt((FvsFloat_t)variance);
            if (fsigma>0.0)
                fcoeff = fsigma0/fsigma;
            for (y = 0; y < h; y++)
            for (x = 0; x < w; x++)
            {
                fgray = (FvsFloat_t)P(x,y);
                fgray = fmean0 + fcoeff*(fgray - mean);
                if (fgray < 0.0)    fgray = 0.0;
                if (fgray > 255.0)  fgray = 255.0;
                P(x,y)= (uint8_t)fgray;
            }
        }
        HistogramDestroy(histogram);
    }
    return nRet;
}


10.实现了主要的图像处理操作

/*#############################################################################
 * 文件名:imagemanip.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 PIJKL p[i+k + (j+l)*nSizeX]


/******************************************************************************
  * 功能:图像缩放操作
  * 参数:image       指纹图像
  *       size        缩放的图像块大小
  *       tolerance   消去直方图的边界
  * 返回:错误编号
******************************************************************************/
FvsError_t ImageLocalStretch(FvsImage_t image, const FvsInt_t size, 
                        const FvsInt_t tolerance)
{
    /* 定义一些变量 */
    int nSizeX = ImageGetWidth(image)  - size + 1;
    int nSizeY = ImageGetHeight(image) - size + 1;
    FvsInt_t i, j, t, l;
    FvsInt_t sum, denom;
    FvsByte_t a = 0;
    FvsInt_t k = 0;
    FvsByte_t b = 255;
    int hist[256];
    FvsByte_t* p = ImageGetBuffer(image);
    if (p==NULL)
        return FvsMemory;
    for (j=0; j < nSizeY; j+=size)
    {
        for (i=0; i < nSizeX; i+=size)
        {
            /* 计算直方图 */
            memset(hist, 0, 256*sizeof(int));
            for (l = 0; l<size; l++)
                for (k = 0; k<size; k++)
                    hist[PIJKL]++;

            /* 伸缩 */
            for (k=0,   sum=0;   k <256; k++)
            {
                sum+=hist[k];
                a = (FvsByte_t)k;
                if (sum>tolerance) break;
            }

            for (k=255, sum=0; k >= 0; k--)
            {
                sum+=hist[k];
                b = (FvsByte_t)k;
                if (sum>tolerance) break;
            }

            denom = (FvsInt_t)(b-a);
            if (denom!=0)
            {
                for (l = 0; l<size; l++)
                {
                    for (k = 0; k<size; k++)
                    {
                        if (PIJKL<a) PIJKL = a;
                        if (PIJKL>b) PIJKL = b;
                        t = (FvsInt_t)((((PIJKL)-a)*255)/denom);
                        PIJKL = (FvsByte_t)(t);
                    }
                }
            }
        }
    }

    return FvsOK;
}


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

/******************************************************************************
** 估算脊线的方向
** 给定一个归一化的指纹图像,算法的主要步骤如下:
**
** 1 - 将G分成大小为 w x w - (15 x 15) 的块;
**
** 2 - 计算每个象素 (i,j)的梯度 dx(i,j) 和 dy(i,j) ,
**     根据计算的需求,梯度算子可以从简单的Sobel算子到复杂的Marr-Hildreth 算子。
**
** 3 - 估算优势方向(i,j), 使用如下的操作:
**
**               i+w/2 j+w/2
**               ---   --- 
**               \     \
**     Nx(i,j) =  --    -- 2 dx(u,v) dy(u,v)
**               /     /
**               ---   ---
**            u=i-w/2 v=j-w/2
**
**               i+w/2 j+w/2
**               ---   --- 
**               \     \
**     Ny(i,j) =  --    -- dx(u,v) - dy(u,v)
**               /     /
**               ---   ---
**            u=i-w/2 v=j-w/2
**
**                  1    -1  / Nx(i,j) \
**     Theta(i,j) = - tan   |  -------  |
**                  2        \ Ny(i,j) /
**
**     这里,Theta(i,j)是局部脊线方向的最小方差估计,以像素 (i,j) 为中心。
**     从数学的角度看,它代表傅立叶频谱中直角占有时的方向。
**
** 4 - 由于有噪声,脊线的中断,细节点等等的存在,在输入图像中,对局部脊线
**     方向的估计并不总是正确的。由于局部脊线方向变化缓慢,所以可以用低通
**     滤波器来修正不正确的脊线方向。为了运用低通滤波器,方向图必须转换成
**     连续的矢量域,定义如下:
**       Phi_x(i,j) = cos( 2 x theta(i,j) )
**       Phi_y(i,j) = sin( 2 x theta(i,j) )
**     在矢量域,可以用如下的卷积低通滤波:
**       Phi2_x(i,j) = (W @ Phi_x) (i,j)
**       Phi2_y(i,j) = (W @ Phi_y) (i,j)
**     W是一个二维的低通滤波器。
**
** 5 - 用如下公式计算 (i,j) 处的方向:
**
**              1    -1  / Phi2_y(i,j) \
**     O(i,j) = - tan   |  -----------  |
**              2        \ Phi2_x(i,j) /
**
** 用这个算法可以得到相当平滑的方向图
**
*/

static FvsError_t FingerprintDirectionLowPass(FvsFloat_t* theta, 
                    FvsFloat_t* out, FvsInt_t nFilterSize, 
                    FvsInt_t w, FvsInt_t h)
{
    FvsError_t nRet = FvsOK;
    FvsFloat_t* filter = NULL;
    FvsFloat_t* phix   = NULL;
    FvsFloat_t* phiy   = NULL;
    FvsFloat_t* phi2x  = NULL;
    FvsFloat_t* phi2y  = NULL;
    FvsInt_t fsize  = nFilterSize*2+1;
    size_t nbytes = (size_t)(w*h*sizeof(FvsFloat_t));
    FvsFloat_t nx, ny;
    FvsInt_t val;
    FvsInt_t i, j, x, y;

    filter= (FvsFloat_t*)malloc((size_t)fsize*fsize*sizeof(FvsFloat_t));
    phix  = (FvsFloat_t*)malloc(nbytes);
    phiy  = (FvsFloat_t*)malloc(nbytes);
    phi2x = (FvsFloat_t*)malloc(nbytes);
    phi2y = (FvsFloat_t*)malloc(nbytes);

    if (filter==NULL || phi2x==NULL || phi2y==NULL || phix==NULL || phiy==NULL)
        nRet = FvsMemory;
    else
    {
        /* 置 0 */
        memset(filter, 0, (size_t)fsize*fsize*sizeof(FvsFloat_t));
        memset(phix,   0, nbytes);
        memset(phiy,   0, nbytes);
        memset(phi2x,  0, nbytes);
        memset(phi2y,  0, nbytes);

        /* 步骤4 */
        for (y = 0; y < h; y++)
        for (x = 0; x < w; x++)
        {
            val = x+y*w;
            phix[val] = cos(theta[val]);
            phiy[val] = sin(theta[val]);
        }
        /* 构造低通滤波器 */
        nx = 0.0;
        for (j = 0; j < fsize; j++)
        for (i = 0; i < fsize; i++)
        {
            filter[j*fsize+i] = 1.0;

            nx += filter[j*fsize+i]; /* 系数和 */
        }
        if (nx>1.0)
        {
            for (j = 0; j < fsize; j++)
            for (i = 0; i < fsize; i++)
                /* 归一化结果 */
                filter[j*fsize+i] /= nx;
        }
        /* 低通滤波 */
        for (y = 0; y < h-fsize; y++)
        for (x = 0; x < w-fsize; x++)
        {
            nx = 0.0;
            ny = 0.0;
            for (j = 0; j < fsize; j++)
            for (i = 0; i < fsize; i++)
            {
                val = (x+i)+(j+y)*w;
                nx += filter[j*fsize+i]*phix[val];
                ny += filter[j*fsize+i]*phiy[val];
            }
            val = x+y*w;
            phi2x[val] = nx;
            phi2y[val] = ny;
        }
        /* 销毁 phix, phiy */
        if (phix!=NULL) 
        { 
            free(phix); 
            phix=NULL; 
        }
        if (phiy!=NULL) 
        { 
            free(phiy); 
            phiy=NULL; 
        }

        /* 步骤5 */
        for (y = 0; y < h-fsize; y++)
        for (x = 0; x < w-fsize; x++)
        {
            val = x+y*w;
            out[val] = atan2(phi2y[val], phi2x[val])*0.5;
        }
    }

    if (phix!=NULL)  free(phix);
    if (phiy!=NULL)  free(phiy);
    if (phi2x!=NULL) free(phi2x);
    if (phi2y!=NULL) free(phi2y);
    if (filter!=NULL)free(filter);

    return nRet;
}


/******************************************************************************
  * 功能:计算指纹图像脊线的方向。
          该算法在许多论文中都有描述,如果图像做了归一化,并且对比度较高,
          则最后的处理效果也较好。
          方向的值在-PI/2和PI/2之间,弧度和脊并不相同。
          选取的块越大,分析的效果也越好,但所需的处理计算时间也越长。
          由于指纹图像中脊线方向的变化比较缓慢,所以低通滤波器可以较好的
          过虑掉方向中的噪声和错误。
  * 参数:image          指向图像对象的指针
  *       field          指向浮点域对象的指针,保存结果
  *       nBlockSize     块大小
  *       nFilterSize    滤波器大小
  * 返回:错误编号
******************************************************************************/
FvsError_t FingerprintGetDirection(const FvsImage_t image, 
                        FvsFloatField_t field, const FvsInt_t nBlockSize, 
                        const FvsInt_t nFilterSize)
{
    /* 输入图像的宽度和高度 */
    FvsInt_t w       = ImageGetWidth (image);
    FvsInt_t h       = ImageGetHeight(image);
    FvsInt_t pitch   = ImageGetPitch (image);
    FvsByte_t* p     = ImageGetBuffer(image);
    FvsInt_t i, j, u, v, x, y;
    FvsFloat_t dx[(nBlockSize*2+1)][(nBlockSize*2+1)];
    FvsFloat_t dy[(nBlockSize*2+1)][(nBlockSize*2+1)];
    FvsFloat_t nx, ny;
    FvsFloat_t* out;
    FvsFloat_t* theta  = NULL;
    FvsError_t nRet = FvsOK;

    /* 输出图像 */
    nRet = FloatFieldSetSize(field, w, h);
    if (nRet!=FvsOK) return nRet;
    nRet = FloatFieldClear(field);
    if (nRet!=FvsOK) return nRet;
    out = FloatFieldGetBuffer(field);

    /* 为方向数组申请内存 */
    if (nFilterSize>0)
    {
        theta = (FvsFloat_t*)malloc(w * h * sizeof(FvsFloat_t));
        if (theta!=NULL)
            memset(theta, 0, (w * h * sizeof(FvsFloat_t)));
    }

    /* 内存错误,返回 */
    if (out==NULL || (nFilterSize>0 && theta==NULL))
        nRet = FvsMemory;
    else
    {
        /* 1 - 图像分块 */
        for (y = nBlockSize+1; y < h-nBlockSize-1; y++)
        for (x = nBlockSize+1; x < w-nBlockSize-1; x++)
        {
            /* 2 - 计算梯度 */
            for (j = 0; j < (nBlockSize*2+1); j++)
            for (i = 0; i < (nBlockSize*2+1); i++)
            {
                dx[i][j] = (FvsFloat_t)
                           (P(x+i-nBlockSize,   y+j-nBlockSize) -
                            P(x+i-nBlockSize-1, y+j-nBlockSize));
                dy[i][j] = (FvsFloat_t)
                           (P(x+i-nBlockSize,   y+j-nBlockSize) -
                            P(x+i-nBlockSize,   y+j-nBlockSize-1));
            }

            /* 3 - 计算方向 */
            nx = 0.0;
            ny = 0.0;
            for (v = 0; v < (nBlockSize*2+1); v++)
            for (u = 0; u < (nBlockSize*2+1); u++)
            {
                nx += 2 * dx[u][v] * dy[u][v];
                ny += dx[u][v]*dx[u][v] - dy[u][v]*dy[u][v];
            }
            /* 计算角度 (-pi/2 .. pi/2) */
            if (nFilterSize>0)
                theta[x+y*w] = atan2(nx, ny);
            else
                out[x+y*w] = atan2(nx, ny)*0.5;
        }
        if (nFilterSize>0)
            nRet = FingerprintDirectionLowPass(theta, out, nFilterSize, w, h);
    }

    if (theta!=NULL) free(theta);
    return nRet;
}


/* 指纹频率域 */

/******************************************************************************
** 这个步骤里,我们估计指纹脊线的频率。在局部邻域里,没有凸现的细节点或者孤点,
** 沿着脊线和谷底,可以用一个正弦曲线波形作为模型,因此,局部脊线频率是指纹图
** 像的另一个本质的特征。对指纹图像G进行归一化,O是其方向图,估算局部脊线频率
** 的步骤如下:
**
** 1 - 图像分块 w x w - (16 x 16)
**
** 2 - 对每块,计算大小为l x w (32 x 16)的方向图窗口
**
** 3 - 对中心在 (i,j) 的每块, 计算脊线和谷底的 x-signature
**     X[0], X[1], ... X[l-1] 采用如下公式:
**
**              --- w-1
**            1 \
**     X[k] = -  --  G (u, v), k = 0, 1, ..., l-1
**            w /
**              --- d=0
**
**     u = i + (d - w/2).cos O(i,j) + (k - l/2).sin O(i,j)
**
**     v = j + (d - w/2).sin O(i,j) - (k - l/2).cos O(i,j)
**
**     如果方向图窗口中没有细节点和孤立的点,则x-signature形成了一个离散
**     的正弦曲线波,与方向图中脊线和谷底的频率一样。因此,脊线和谷底的
**     频率可以由x-signature来估计。设T(i,j)是两个峰顶的平均距离,则频率
**     OHM(i,j)可以这样计算:OHM(i,j) = 1 / T(i,j)。
**
**     如果没有两个连续的峰顶,则频率置为-1,说明其无效。
**
** 4 - 对于一个指纹图像而言,脊线频率的值在一个范围之内变动,比如说对于500
**     dpi的图像,变动范围为[1/3, 1/25],因此,如果估计出的频率不在这个范
**     围内,说明频率估计无效,同意置为-1。
**
** 5 - 如果某块有断点或者细节点,则不会有正弦曲线,其频率可以由邻块的频率
**     插值估计(比如说高斯函数,均值为0,方差为9,宽度为7)。
**
** 6 - 脊线内部距离变化缓慢,可以用低通滤波器
**
*/

/* 宽度 */
#define BLOCK_W     16
#define BLOCK_W2     8

/* 长度 */
#define BLOCK_L     32
#define BLOCK_L2    16

#define EPSILON     0.0001
#define LPSIZE      3

#define LPFACTOR    (1.0/((LPSIZE*2+1)*(LPSIZE*2+1)))


FvsError_t FingerprintGetFrequency(const FvsImage_t image, const FvsFloatField_t direction,
           FvsFloatField_t frequency)
{
    /* 输入图像的宽度和高度 */
    FvsError_t nRet = FvsOK;
    FvsInt_t w      = ImageGetWidth (image);
    FvsInt_t h      = ImageGetHeight(image);
    FvsInt_t pitchi = ImageGetPitch (image);
    FvsByte_t* p    = ImageGetBuffer(image);
    FvsFloat_t* out;
    FvsFloat_t* freq;
    FvsFloat_t* orientation = FloatFieldGetBuffer(direction);

    FvsInt_t x, y, u, v, d, k;
    size_t size;

    if (p==NULL)
        return FvsMemory;

    /* 输出图像的内存申请 */
    nRet = FloatFieldSetSize(frequency, w, h);
    if (nRet!=FvsOK) return nRet;
    (void)FloatFieldClear(frequency);
    freq = FloatFieldGetBuffer(frequency);
    if (freq==NULL)
        return FvsMemory;

    /* 输出的内存申请 */
    size = w*h*sizeof(FvsFloat_t);
    out  = (FvsFloat_t*)malloc(size);
    if (out!=NULL)
    {
        FvsFloat_t dir = 0.0;
        FvsFloat_t cosdir = 0.0;
        FvsFloat_t sindir = 0.0;

        FvsInt_t peak_pos[BLOCK_L];     /* 顶点           */
        FvsInt_t peak_cnt;              /* 顶点数目     */
        FvsFloat_t peak_freq;           /* 顶点频率     */
        FvsFloat_t Xsig[BLOCK_L];       /* x signature  */
        FvsFloat_t pmin, pmax;

        memset(out,  0, size);
        memset(freq, 0, size);

        /* 1 - 图像分块  BLOCK_W x BLOCK_W - (16 x 16) */
        for (y = BLOCK_L2; y < h-BLOCK_L2; y++)
        for (x = BLOCK_L2; x < w-BLOCK_L2; x++)
        {
            /* 2 - 脊线方向的窗口 l x w (32 x 16) */
            dir = orientation[(x+BLOCK_W2) + (y+BLOCK_W2)*w];
            cosdir = -sin(dir); 
            sindir = cos(dir);

            /* 3 - 计算 x-signature X[0], X[1], ... X[l-1] */
            for (k = 0; k < BLOCK_L; k++)
            {
                Xsig[k] = 0.0;
                for (d = 0; d < BLOCK_W; d++)
                {
                    u = (FvsInt_t)(x + (d-BLOCK_W2)*cosdir + (k-BLOCK_L2)*sindir);
                    v = (FvsInt_t)(y + (d-BLOCK_W2)*sindir - (k-BLOCK_L2)*cosdir);
                    /* clipping */
                    if (u<0) u = 0; else if (u>w-1) u = w-1;
                    if (v<0) v = 0; else if (v>h-1) v = h-1;
                    Xsig[k] += p[u + (v*pitchi)];
                }
                Xsig[k] /= BLOCK_W;
            }

            /* 计算 T(i,j) */
            /* 寻找 x signature 中的顶点 */
            peak_cnt = 0;

            pmax = pmin = Xsig[0];
            for (k = 1; k < BLOCK_L; k++)
            {
                if (pmin>Xsig[k]) pmin = Xsig[k];
                if (pmax<Xsig[k]) pmax = Xsig[k];
            }
            if ((pmax - pmin)>64.0)
            {
                for (k = 1; k < BLOCK_L-1; k++)
                if ((Xsig[k-1] < Xsig[k]) && (Xsig[k] >= Xsig[k+1]))
                {
                    peak_pos[peak_cnt++] = k;
                }
            }
            /* 计算均值 */
            peak_freq = 0.0;
            if (peak_cnt>=2)
            {
                for (k = 0; k < peak_cnt-1; k++)
                    peak_freq += peak_pos[k+1]-peak_pos[k];
                peak_freq /= peak_cnt-1;
            }
            /* 4 - 验证频率范围 [1/25-1/3] */
            /*     可以扩大到 [1/30-1/2]   */
            if (peak_freq > 30.0)
                out[x+y*w] = 0.0;
            else if (peak_freq < 2.0)
                out[x+y*w] = 0.0;
            else
                out[x+y*w] = 1.0/peak_freq;
        }
        /* 5 - 未知点 */
        for (y = BLOCK_L2; y < h-BLOCK_L2; y++)
        for (x = BLOCK_L2; x < w-BLOCK_L2; x++)
        {
            if (out[x+y*w]<EPSILON)
            {
                if (out[x+(y-1)*w]>EPSILON)
                {
                    out[x+(y*w)] = out[x+(y-1)*w];
                }
                else
                {
                    if (out[x-1+(y*w)]>EPSILON)
                        out[x+(y*w)] = out[x-1+(y*w)];
                }
            }
        }
        /* 6 - 频率插值 */
        for (y = BLOCK_L2; y < h-BLOCK_L2; y++)
        for (x = BLOCK_L2; x < w-BLOCK_L2; x++)
        {
            k = x + y*w;
            peak_freq = 0.0;
            for ( v = -LPSIZE; v <= LPSIZE; v++)
            for ( u = -LPSIZE; u <= LPSIZE; u++)
                peak_freq += out[(x+u)+(y+v)*w];
            freq[k] = peak_freq*LPFACTOR;
        }
        free(out);
    }
    return nRet;
}


/******************************************************************************
  * 功能:获取指纹图像的有效区域,以进行进一步的处理。
  *       如果某个区域不可用用,则掩码置为0,包括如下区域:
  *       边界,背景点,图像质量很差的区域。
  *       有效区域的掩码置为255。
  * 参数:image        指纹图像
  *       direction    脊线方向
  *       frequency    脊线频率
  *       mask         输出的掩码
  * 返回:错误编号
******************************************************************************/
FvsError_t FingerprintGetMask(const FvsImage_t image, 
                    const FvsFloatField_t direction,
                    const FvsFloatField_t frequency, FvsImage_t mask)
{
    FvsError_t nRet = FvsOK;
    FvsFloat_t freqmin = 1.0 / 25;
    FvsFloat_t freqmax = 1.0 / 3;

    /* 输入图像的宽度高度 */
    FvsInt_t w      = ImageGetWidth (image);
    FvsInt_t h      = ImageGetHeight(image);
    FvsByte_t* out;
    FvsInt_t pitchout;
    FvsInt_t pos, posout, x, y;
    FvsFloat_t* freq = FloatFieldGetBuffer(frequency);

    if (freq==NULL)
        return FvsMemory;

    /* 需要做改进:检查 */
    nRet = ImageSetSize(mask, w, h);
    if (nRet==FvsOK)
        nRet = ImageClear(mask);
    out = ImageGetBuffer(mask);
    if (out==NULL)
        return FvsMemory;
    if (nRet==FvsOK)
    {
    pitchout = ImageGetPitch(mask);

    for (y = 0; y < h; y++)
        for (x = 0; x < w; x++)
        {
            pos    = x + y * w;
            posout = x + y * pitchout;
            out[posout] = 0;
            if (freq[pos] >= freqmin && freq[pos] <= freqmax)
            {
                out[posout] = 255;
            }
        }
    /* 补洞 */
    for (y = 0; y < 4; y++)
        (void)ImageDilate(mask);
    /* 去除边界 */
    for (y = 0; y < 12; y++)
        (void)ImageErode(mask);
    }
    return nRet;
}


/* 细化算法 */
#undef P
#define P(x,y)      ((x)+(y)*pitch)
#define REMOVE_P    { p[P(x,y)]=0x80; changed = FvsTrue; }


/******************************************************************************
** 邻域点定义如下:
**     9 2 3
**     8 1 4
**     7 5 6
******************************************************************************/
/* 宏定义 */
#define P1  p[P(x  ,y  )]
#define P2  p[P(x  ,y-1)]
#define P3  p[P(x+1,y-1)]
#define P4  p[P(x+1,y  )]
#define P5  p[P(x+1,y+1)]
#define P6  p[P(x  ,y+1)]
#define P7  p[P(x-1,y+1)]
#define P8  p[P(x-1,y  )]
#define P9  p[P(x-1,y-1)]


FvsError_t ImageRemoveSpurs(FvsImage_t image)
{
    FvsInt_t w       = ImageGetWidth(image);
    FvsInt_t h       = ImageGetHeight(image);
    FvsInt_t pitch   = ImageGetPitch(image);
    FvsByte_t* p     = ImageGetBuffer(image);
    FvsInt_t x, y, n, t, c;

    c = 0;

    do 
    {
    n = 0;    
    for (y=1; y<h-1; y++)
        for (x=1; x<w-1; x++)
        {
            if( p[P(x,y)]==0xFF)
            {
                t=0;
                if (P3==0 && P2!=0 && P4==0) t++;
                if (P5==0 && P4!=0 && P6==0) t++;
                if (P7==0 && P6!=0 && P8==0) t++;
                if (P9==0 && P8!=0 && P2==0) t++;
                if (P3!=0 && P4==0) t++;
                if (P5!=0 && P6==0) t++;
                if (P7!=0 && P8==0) t++;
                if (P9!=0 && P2==0) t++;
                if (t==1)
                {
                    p[P(x,y)] = 0x80;
                    n++;        
                }
            }
        }
        for (y=1; y<h-1; y++)
            for (x=1; x<w-1; x++)
            {
                if( p[P(x,y)]==0x80)
                p[P(x,y)] = 0;
            }   
    } while (n>0 && ++c < 5);

    return FvsOK;
}


/* a) 验证其有2-6个邻点 */
#define STEP_A  n = 0; /* 邻点个数 */ \
                if (P2!=0) n++; if (P3!=0) n++; if (P4!=0) n++; if (P5!=0) n++; \
                if (P6!=0) n++; if (P7!=0) n++; if (P8!=0) n++; if (P9!=0) n++; \
                if (n>=2 && n<=6)

/* b) 统计由0变1的个数 */
#define STEP_B  t = 0; /* 变化的数目 */ \
                if (P9==0 && P2!=0) t++; if (P2==0 && P3!=0) t++; \
                if (P3==0 && P4!=0) t++; if (P4==0 && P5!=0) t++; \
                if (P5==0 && P6!=0) t++; if (P6==0 && P7!=0) t++; \
                if (P7==0 && P8!=0) t++; if (P8==0 && P9!=0) t++; \
                if (t==1)

/******************************************************************************
  * 功能:细化指纹图像
  *       图像必须是二值化过的(只包含0x00或oxFF)
  *       该算法基于领域的判断,决定某个象素该移去还是保留
  * 参数:image   指纹图像
  * 返回:错误编号
******************************************************************************/ 
FvsError_t ImageThinConnectivity(FvsImage_t image)
{
    FvsInt_t w       = ImageGetWidth(image);
    FvsInt_t h       = ImageGetHeight(image);
    FvsInt_t pitch   = ImageGetPitch(image);
    FvsByte_t* p     = ImageGetBuffer(image);
    FvsInt_t x, y, n, t;
    FvsBool_t changed = FvsTrue;

    if (p==NULL)
        return FvsMemory;
    if (ImageGetFlag(image)!=FvsImageBinarized)
        return FvsBadParameter;

    while (changed==FvsTrue)
    {
        changed = FvsFalse;
        for (y=1; y<h-1; y++)
        for (x=1; x<w-1; x++)
        {
            if (p[P(x,y)]==0xFF)
            {
                STEP_A
                {
                    STEP_B
                    {
                        /*
                        c) 2*4*6=0  (2,4 ,or 6 为0)
                        d) 4*6*8=0
                        */
                        if (P2*P4*P6==0 && P4*P6*P8==0)
                            REMOVE_P;

                    }
                }
            }
        }

        for (y=1; y<h-1; y++)
        for (x=1; x<w-1; x++)
            if (p[P(x,y)]==0x80)
                p[P(x,y)] = 0;

        for (y=1; y<h-1; y++)
        for (x=1; x<w-1; x++)
        {
            if (p[P(x,y)]==0xFF)
            {
                STEP_A
                {
                    STEP_B
                    {
                        /*
                        c) 2*6*8=0
                        d) 2*4*8=0
                        */
                        if (P2*P6*P8==0 && P2*P4*P8==0)
                            REMOVE_P;

                    }
                }
            }
        }

        for (y=1; y<h-1; y++)
        for (x=1; x<w-1; x++)
            if (p[P(x,y)]==0x80)
                p[P(x,y)] = 0;
    }

    ImageRemoveSpurs(image);

    return ImageSetFlag(image, FvsImageThinned);
}



/* 重新定义 REMOVE_P */
#undef REMOVE_P


#define REMOVE_P    { p[P(x,y)]=0x00; changed = FvsTrue; }


/******************************************************************************
  * 功能:细化指纹图像,使用“Hit and Miss”结构元素。
  *       图像必须是二值化过的(只包含0x00或oxFF)
  *       该算法的缺点是产生很多伪造的线条(伪特征),
  *       必须由另外的算法来消除,后处理非常必要。
  * 参数:image   指纹图像
  * 返回:错误编号
******************************************************************************/
FvsError_t ImageThinHitMiss(FvsImage_t image)
{
    FvsInt_t w      = ImageGetWidth(image);
    FvsInt_t h      = ImageGetHeight(image);
    FvsInt_t pitch  = ImageGetPitch(image);
    FvsByte_t* p    = ImageGetBuffer(image);

    /* 
    // 
    // 0 0 0      0 0 
    //   1      1 1 0
    // 1 1 1      1
    //
    */
    FvsInt_t x,y, t;
    FvsBool_t changed = FvsTrue;

    if (p==NULL)
        return FvsMemory;

    if (ImageGetFlag(image)!=FvsImageBinarized)
        return FvsBadParameter;

    while (changed==FvsTrue)
    {
        changed = FvsFalse;
        for (y=1; y<h-1; y++)
        for (x=1; x<w-1; x++)
        {
            if (p[P(x,y)]==0xFF)
            {
                /*
                // 0 0 0  0   1  1 1 1  1   0
                //   1    0 1 1    1    1 1 0
                // 1 1 1  0   1  0 0 0  1   0
                */
                if (p[P(x-1,y-1)]==0 && p[P(x,y-1)]==0 && p[P(x+1,y-1)]==0 &&
                    p[P(x-1,y+1)]!=0 && p[P(x,y+1)]!=0 && p[P(x+1,y+1)]!=0)
                    REMOVE_P;

                if (p[P(x-1,y-1)]!=0 && p[P(x,y-1)]!=0 && p[P(x+1,y-1)]!=0 &&
                    p[P(x-1,y+1)]==0 && p[P(x,y+1)]==0 && p[P(x+1,y+1)]==0)
                    REMOVE_P;

                if (p[P(x-1,y-1)]==0 && p[P(x-1,y)]==0 && p[P(x-1,y+1)]==0 &&
                    p[P(x+1,y-1)]!=0 && p[P(x+1,y)]!=0 && p[P(x+1,y+1)]!=0)
                    REMOVE_P;

                if (p[P(x-1,y-1)]!=0 && p[P(x-1,y)]!=0 && p[P(x-1,y+1)]!=0 &&
                    p[P(x+1,y-1)]==0 && p[P(x+1,y)]==0 && p[P(x+1,y+1)]==0)
                    REMOVE_P;

                /*
                //   0 0  0 0      1      1  
                // 1 1 0  0 1 1  0 1 1  1 1 0
                //   1      1    0 0      0 0
                */                
                if (p[P(x,y-1)]==0 && p[P(x+1,y-1)]==0 && p[P(x+1,y)]==0 &&
                    p[P(x-1,y)]!=0 && p[P(x,y+1)]!=0)
                    REMOVE_P;

                if (p[P(x-1,y-1)]==0 && p[P(x,y-1)]==0 && p[P(x-1,y)]==0 &&
                    p[P(x+1,y)]!=0 && p[P(x,y+1)]!=0)
                    REMOVE_P;

                if (p[P(x-1,y+1)]==0 && p[P(x-1,y)]==0 && p[P(x,y+1)]==0 &&
                    p[P(x+1,y)]!=0 && p[P(x,y-1)]!=0)
                    REMOVE_P;

                if (p[P(x+1,y+1)]==0 && p[P(x+1,y)]==0 && p[P(x,y+1)]==0 &&
                    p[P(x-1,y)]!=0 && p[P(x,y-1)]!=0)
                    REMOVE_P;                
            }
        }
    }

    ImageRemoveSpurs(image);

    return ImageSetFlag(image, FvsImageThinned);
}





11.实现了指纹图像的基本操作

/*##########################################################
* 文件名:image.c
* 功能: 实现了指纹图像的基本操作

###################################*/

#include “stdlib.h”
#include “stdio.h”
#include “string.h”
#include “image.h”

/* 指纹图像结构。256级灰度图 */
typedef struct iFvsImage_t
{
FvsByte_t pimg; / 8-bit图像数组 */
FvsInt_t w; /* 宽度 */
FvsInt_t h; /* 高度 */
FvsInt_t pitch; /* 倾斜度 */
FvsImageFlag_t flags; /* 标记 */
} iFvsImage_t;

/********************************************************************
* 功能:创建一个新的图像对象
* 参数:无
* 返回:失败返回空,否则返回新的图像对象
********************************************************************/
FvsImage_t ImageCreate()
{
iFvsImage_t* p = NULL;

p = (FvsImage_t)malloc(sizeof(iFvsImage_t));
if (p!=NULL)
{
    p->h        = 0;
    p->w        = 0;
    p->pitch    = 0;
    p->pimg     = NULL;
    p->flags    = FvsImageGray; /* 缺省的标记 */    
}
return (FvsImage_t)p;

}

/********************************************************************
* 功能:销毁一个图像对象
* 参数:image 指向图像对象的指针
* 返回:无
********************************************************************/
void ImageDestroy(FvsImage_t image)
{
iFvsImage_t* p = NULL;
if (image==NULL)
return;
(void)ImageSetSize(image, 0, 0);
p = image;
free(p);
}

/********************************************************************
* 功能:设置图像标记,该操作大部分由库函数自动完成
* 参数:image 指向图像对象的指针
* flag 标记
* 返回:错误编号
********************************************************************/
FvsError_t ImageSetFlag(FvsImage_t img, const FvsImageFlag_t flag)
{
iFvsImage_t* image = (iFvsImage_t*)img;
image->flags = flag;
return FvsOK;
}

/********************************************************************
* 功能:获得图像标记
* 参数:image 指向图像对象的指针
* 返回:图像标记
********************************************************************/
FvsImageFlag_t ImageGetFlag(const FvsImage_t img)
{
iFvsImage_t* image = (iFvsImage_t*)img;
return image->flags;
}

/********************************************************************
* 功能:设置一个图像对象的大小
* 参数:image 指向图像对象的指针
* width 图像宽度
* height 图像高度
* 返回:错误编号
********************************************************************/
FvsError_t ImageSetSize(FvsImage_t img, const FvsInt_t width,
const FvsInt_t height)
{
iFvsImage_t* image = (iFvsImage_t*)img;
FvsError_t nRet = FvsOK;
FvsInt_t newsize = width*height;

/* size为0的情况 */
if (newsize==0)
{
    if (image->pimg!=NULL)
    {
        free(image->pimg);
        image->pimg = NULL;
        image->w = 0;
        image->h = 0;
        image->pitch = 0;
    }
    return FvsOK;
}

if (image->h*image->w != newsize)
{
    free(image->pimg);
    image->w = 0;
    image->h = 0;
    image->pitch = 0;
    /* 申请内存 */
    image->pimg = (uint8_t*)malloc((size_t)newsize);
}

if (image->pimg == NULL)
    nRet = FvsMemory;
else
{
    image->h = height;
    image->w = width;
    image->pitch = width;
}
return nRet;

}

/********************************************************************
* 功能:拷贝图像
* 参数:destination 指向目标图像对象的指针
* source 指向源图像对象的指针
* 返回:错误编号
********************************************************************/
FvsError_t ImageCopy(FvsImage_t destination, const FvsImage_t source)
{
iFvsImage_t* dest = (iFvsImage_t*)destination;
iFvsImage_t* src = (iFvsImage_t*)source;
FvsError_t nRet = FvsOK;

nRet = ImageSetSize(dest, src->w, src->h);

if (nRet==FvsOK)
    memcpy(dest->pimg, src->pimg, (size_t)src->h*src->w);

/* 拷贝标记 */
dest->flags = src->flags;

return nRet;

}

/********************************************************************
* 功能:清空图像
* 参数:image 指向图像对象的指针
* 返回:错误编号
********************************************************************/
FvsError_t ImageClear(FvsImage_t img)
{
return ImageFlood(img, 0);
}

/********************************************************************
* 功能:设置图像中所有象素为特定值
* 参数:image 指向图像对象的指针
* value 要设定的值
* 返回:错误编号
********************************************************************/
FvsError_t ImageFlood(FvsImage_t img, const FvsByte_t value)
{
FvsError_t nRet = FvsOK;
iFvsImage_t* image = (iFvsImage_t*)img;
if (image==NULL) return FvsMemory;
if (image->pimg!=NULL)
memset(image->pimg, (int)value, (size_t)(image->h*image->w));
return nRet;
}

/********************************************************************
* 功能:设置图像中某个象素的值
* 参数:image 指向图像对象的指针
* x X轴坐标
* y Y轴坐标
* val 要设定的值
* 返回:无
********************************************************************/
void ImageSetPixel(FvsImage_t img, const FvsInt_t x, const FvsInt_t y,
const FvsByte_t val)
{
iFvsImage_t* image = (iFvsImage_t*)img;
int address = y * image->w + x;
image->pimg[address] = val;
}

/********************************************************************
* 功能:获得图像中某个象素的值
* 参数:image 指向图像对象的指针
* x X轴坐标
* y Y轴坐标
* 返回:象素的值
********************************************************************/
FvsByte_t ImageGetPixel(const FvsImage_t img, const FvsInt_t x,
const FvsInt_t y)
{
iFvsImage_t* image = (iFvsImage_t*)img;
/* 数组中的位置 */
int address = y * image->pitch + x;
return image->pimg[address];
}

/********************************************************************
* 功能:获得图像缓冲区指针
* 参数:image 指向图像对象的指针
* 返回:指向图像内存缓冲区的指针
********************************************************************/
FvsByte_t* ImageGetBuffer(FvsImage_t img)
{
iFvsImage_t* image = (iFvsImage_t*)img;
if (image==NULL)
return NULL;
return image->pimg;
}

/********************************************************************
* 功能:获得图像宽度
* 参数:image 指向图像对象的指针
* 返回:图像宽度
********************************************************************/
FvsInt_t ImageGetWidth(const FvsImage_t img)
{
iFvsImage_t* image = (iFvsImage_t*)img;
if (image==NULL)
return -1;
return image->w;
}

/********************************************************************
* 功能:获得图像高度
* 参数:image 指向图像对象的指针
* 返回:图像高度
********************************************************************/
FvsInt_t ImageGetHeight(const FvsImage_t img)
{
iFvsImage_t* image = (iFvsImage_t*)img;
if (image==NULL)
return -1;
return image->h;
}

/********************************************************************
* 功能:获得图像缓冲区的大小
* 参数:image 指向图像对象的指针
* 返回:缓冲区大小
********************************************************************/
FvsInt_t ImageGetSize(const FvsImage_t img)
{
iFvsImage_t* image = (iFvsImage_t*)img;
if (image==NULL)
return 0;
return image->h * image->w;
}

/********************************************************************
* 功能:获得图像倾斜度
* 参数:image 指向图像对象的指针
* 返回:倾斜度
********************************************************************/
FvsInt_t ImageGetPitch(const FvsImage_t img)
{
iFvsImage_t* image = (iFvsImage_t*)img;
if (image==NULL)
return -1;
return image->pitch;
}

/********************************************************************
* 功能:比较两个图像大小
* 参数:image1 指向图像对象1的指针
* image2 指向图像对象2的指针
* 返回:若两个图像大小相等,返回true;否则返回false
********************************************************************/
FvsBool_t ImageCompareSize(const FvsImage_t image1, const FvsImage_t image2)
{
if (ImageGetWidth(image1)!=ImageGetWidth(image2))
return FvsFalse;
if (ImageGetHeight(image1)!=ImageGetHeight(image2))
return FvsFalse;
return FvsTrue;
}

12.所有资料,我已经打包,放到了csdn上,请下载;

链接稍后:

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
方案选择: 一般市面是光学指纹头和半导体的。至于区别我就不过多说了,感兴趣的回复讨论吧。我主要区别的是光学指纹头比较大放不进家里的86型开关。我就选半导体的了,但是价格非常贵,这个成本就占了一半还多呢。至于流行的“指纹模块(指纹头+控制)+自己的主控”,我就不讨论了。 于是我的方案就是:半导体指纹头+主控芯片(带算法)。 指纹头采用的是瑞典的fpc1011,就是公安局录指纹,银行使用的那种,很贵的。主控是新塘的N32901或N32903. 很多人要问,你的指纹识别算法呢?我只能说我拿到的是库函数形式,我也没能力完成识别算法,至于算法,我只在github上看到国外大牛开源的。我只是测试了下pc端,嵌入式端还没用过,重要是没时间搞,很费力。我只做了QT界面的一个识别,被我放在我的github上了。 指纹识别开关控制板电路PCB实物图: 软件开发: 开发软件:ADS1.2 代码:新唐官方关于N3290X的裸机代码开发包 固件下载工具:TurboWriter V2.20.003_N329x1 首先明确一点,自己开发一定要有开发arm9的经验,否则你遇到的问题都会卡在开发工具怎么安装,怎么编译不了,怎么设置编译器等基础问题。当然新塘的这款芯片开发是有点难度,如果你没有足够的知识和能力去解决问题,还是免了自己开发。毕竟我也是搞了好久才弄懂的。 代码由于某些原因,我只发不带指纹库的应用代码部分,是裸机程序。如果你想要带指纹库的,确定和我单独联系,我确定你不是商业行为,我在单独发给你。这个没办法,合作商要求的。 对于开发如何开发新塘N3290x这款芯片,有专门的论坛,资料也很多。开发就一定要自己看技术手册,看源代码,这个没有捷径,只能一步一步的做。 我直接给这个板子用的烧写固件吧! SpiLoaderLite_N32901.bin 是N32901的SPI启动固件代码 N32901_HB_Lcd.bin 是程序的应用代码 这两个固件用TurboWriter 烧写进SPIFLASH里就OK了。至于怎么烧写其实也比较复杂的。官方里面有说明书。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不对法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值