目前,各个手机中,都是用的电容式指纹识别模块
公司打卡机,大都是光学指纹模块,相对上者便宜一些;
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上,请下载;
链接稍后: