/*#############################################################################
* 文件名:imagemanip.c
* 功能: 实现了主要的图像处理操作
* modified by PRTsinghua@hotmail.com
#############################################################################*/
#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_
- 1
- 2
- 3
- 4
- 5
前往页