📄 vf_getminutia.h
字号:
#ifndef __VFGETMINUTIA_H__
#define __VFGETMINUTIA_H__
#include "VF_Type.h"
#include "VF_Global.h"
#include "VF_Function.h"
//
// getMinutia: 提取指纹特征
//
sint32 getMinutia()
{
sint32 x, y, i, j, k, n;
sint32 temp;
sint32 sum;
uint8 *lpNow;
uint8 *lpDis;
sint32 r = 16; // 允许特征点靠近前景边缘的最近距离
double d, d1, d2, d0, a;
sint32 dGate = 16;
bool flag;
sint32 x11, y11;
sint32 x21, y21;
sint32 angle1, angle2, angle3, angle4;
uint8 *tempPtr[8];
uint8 *disPtr[3];
uint8 *lpOri;
sint32 tempForkNum;
sint32 tempEndNum;
sint32 ForkNum;
sint32 EndNum;
// 临时特征点数组
MINUTIA tempFork[MAXRAWMINUTIANUM];
MINUTIA tempEnd[MAXRAWMINUTIANUM];
MINUTIA ForkArr[MAXRAWMINUTIANUM];
MINUTIA EndArr[MAXRAWMINUTIANUM];
// 某点为圆心,半径为5的圆上所有点的地址偏移
sint32 SiteR5[28] = {
-5, IMGW-5, 2*IMGW-5, 3*IMGW-4, 4*IMGW-3, 5*IMGW-2, 5*IMGW-1, 5*IMGW,
5*IMGW+1, 5*IMGW+2, 4*IMGW+3, 3*IMGW+4, 2*IMGW+5, IMGW+5, 5, -IMGW+5,
-2*IMGW+5, -3*IMGW+4, -4*IMGW+3, -5*IMGW+2, -5*IMGW+1, -5*IMGW,
-5*IMGW-1, -5*IMGW-2, -4*IMGW-3, -3*IMGW-4, -2*IMGW-5, -IMGW-5
};
// 某点周围8个点的地址偏移
sint32 SiteU8[8] = {IMGW-1, IMGW, IMGW+1, 1, -IMGW+1, -IMGW, -IMGW-1, -1};
// 初始化临时特征点数组
memset((void *)&tempFork[0], 0, MAXRAWMINUTIANUM*sizeof(MINUTIA));
memset((void *)&tempEnd[0], 0, MAXRAWMINUTIANUM*sizeof(MINUTIA));
memset((void *)&ForkArr[0], 0, MAXRAWMINUTIANUM*sizeof(MINUTIA));
memset((void *)&EndArr[0], 0, MAXRAWMINUTIANUM*sizeof(MINUTIA));
bool bGood = false; // 标记阈值是否调整好,使特征点数目在允许范围内
sint32 densD = 7; // 允许两个特征点间最近的距离
sint32 loopnum; // 循环次数
sint32 trilen = 16;
// 第一遍寻找所有端点和叉点
ForkNum = 0;
EndNum = 0;
temp = 17 * IMGW;
for(y = 17; y < IMGH-17; y++)
{
for(x = 17; x < IMGW-17; x++)
{
lpNow = g_lpOrgFinger + temp + x;//原图像指针
lpOri = g_lpOrient + y*IMGW + x;//原图像方向场指针
// 不是黑点则不考虑
if(*lpNow != 0)
{
continue;
}
// 是叉点
if(IsFork(lpNow))
{
// 检查是否靠近边缘, 靠近边缘则不考虑
flag = true;
for(i = -r; i <= r && flag; i++)
{
for(j = -r; j <= r && flag; j++)
{
if(y+i<0 || y+i>=IMGH || x+j<0 || x+j>=IMGW)
{
continue;
}
if(*(g_lpOrient + temp + i*IMGW + x + j) == 255)
{
flag = false;
break;
}
}
}
// 检查该点周围方向场是否变化剧烈,变化剧烈则不考虑,因为很可能是虚假点
sum = 0;
for(i = 0; i < 28; i++)
{
sum += GetJiajiao(*(lpOri+SiteR5[(i+1)%28]), *(lpOri+SiteR5[i]));//相邻两个方向的夹角
}
if(sum > 96)
flag = false;
// 达到所有要求则记录下来该点
if(flag)
{
ForkArr[ForkNum].x = x;
ForkArr[ForkNum].y = y;
ForkNum++;
// 如果总数已经超过允许最大数目,则返回错误
if(ForkNum >= MAXRAWMINUTIANUM)
{
ForkNum = 0;
return 1;
}
}
}
else if(IsEnd(lpNow)) // 如果是端点
{
// 检查是否靠近边缘, 靠近边缘则不考虑
flag = true;
for(i = -r; i <= r && flag; i++)
{
for(j = -r; j <= r && flag; j++)
{
if(y+i<0 || y+i>=IMGH || x+j<0 || x+j>=IMGW)
{
continue;
}
if(*(g_lpOrient + temp + i*IMGW + x + j) == 255)
{
flag = false;
break;
}
}
}
// 检查该点周围方向场是否变化剧烈,变化剧烈则不考虑,因为很可能是虚假点
sum = 0;
for(i = 0; i < 28; i++)
{
sum += GetJiajiao(*(lpOri+SiteR5[(i+1)%28]), *(lpOri+SiteR5[i]));
}
if(sum > 96)
flag = false;
// 达到所有要求则记录下来该点
if(flag)
{
EndArr[EndNum].x = x;
EndArr[EndNum].y = y;
EndNum++;
// 如果总数已经超过允许最大数目,则返回错误
if(EndNum >= MAXRAWMINUTIANUM)
{
EndNum = 0;
return 1;
}
}
}
}
temp += IMGW;
}
// 初始化临时缓冲区,准备循环调整两个特征点间允许的最小距离,使之增大到一个合适的值
// 因为如果特征点非常密集的地方很有可能是质量很差的地方
for(i = 0; i < MAXRAWMINUTIANUM; i++)
{
tempEnd[i] = EndArr[i];
}
for(i = 0; i < MAXRAWMINUTIANUM; i++)
{
tempFork[i] = ForkArr[i];
}
tempForkNum = ForkNum;
tempEndNum = EndNum;
// 循环调整
bGood = false;
loopnum = 0; // 调整的次数
while(!bGood && loopnum < 32) // 最多调整32次
{
loopnum++; // 调整次数加一
// 得到新的特征点数组
for(i = 0; i < MAXRAWMINUTIANUM; i++)
{
EndArr[i] = tempEnd[i];
}
for(i = 0; i < MAXRAWMINUTIANUM; i++)
{
ForkArr[i] = tempFork[i];
}
// 新的特征点数
ForkNum = tempForkNum; // 叉点
EndNum = tempEndNum; // 端点
// 去掉虚假的端点
bGood = true;
for(i = 0; i < EndNum-1; i++)
{
flag = false;
for(j = i+1; j < EndNum; j++)
{
// 求两个端点的距离
d = sqrt((double)((EndArr[i].x-EndArr[j].x)*(EndArr[i].x-EndArr[j].x) +
(EndArr[i].y-EndArr[j].y)*(EndArr[i].y-EndArr[j].y)));
// 距离足够大则检查下一个端点
if(d > dGate && d > densD)
{
continue;
}
// 距离太小,则将其坐标置为(0,0)
if(d <= densD)
{
EndArr[j].x = 0;
EndArr[j].y = 0;
flag = true;
continue;
}
// 求第一个端点所在纹线的角度
lpNow = g_lpOrgFinger + EndArr[i].y*IMGW + EndArr[i].x;
if(GetByDis(lpNow, &lpDis, 8) != 0)
{
break;
}
angle1 = GetAngle(EndArr[i].x, EndArr[i].y, PX(lpDis), PY(lpDis));
// 求第二个端点所在纹线的角度
lpNow = g_lpOrgFinger + EndArr[j].y*IMGW + EndArr[j].x;
if(GetByDis(lpNow, &lpDis, 8) != 0)
{
continue;
}
angle2 = GetAngle(EndArr[j].x, EndArr[j].y, PX(lpDis), PY(lpDis));
// 求两个角度间的距离
angle3 = GetAngleDis(angle1, angle2);
// 如果两个角度间成锐角,则不是虚假特征点
if(angle3 > 270 || angle3 < 90)
{
continue;
}
// 求两个端点连线的角度
angle3 = GetAngle(EndArr[i].x, EndArr[i].y, EndArr[j].x, EndArr[j].y);
// 求第一个端点纹线与连线的夹角
angle3 = GetAngleDis(angle1, angle3);
// 如果夹角较大则不是虚假点
if(angle3 < 150 || angle3 > 210)
{
continue;
}
// 求第二个端点纹线与连线的夹角
angle4 = GetAngle(EndArr[j].x, EndArr[j].y, EndArr[i].x, EndArr[i].y);
angle4 = GetAngleDis(angle2, angle4);
// 如果夹角较大则不是虚假点
if(angle4 < 150 || angle4 > 210)
{
continue;
}
// 否则表示这两个点是同一条纹线上的断裂处的两个端点,坐标置原点
EndArr[j].x = 0;
EndArr[j].y = 0;
flag = true;
}
if(flag)// 表示这两个点是同一条纹线上的断裂处的两个端点,坐标置原点
{
EndArr[i].x = 0;
EndArr[i].y = 0;
}
}
// 统计新的端点数目
j = 0;
for(i = 0; i < EndNum; i++)
{
if(EndArr[i].x == 0 || EndArr[i].y == 0)
{
continue;
}
lpNow = g_lpOrgFinger + EndArr[i].y*IMGW + EndArr[i].x;
if(GetByDis(lpNow, &lpDis, 8) != 0)
{
continue;
}
j++;
// 如果端点数目还是过多,则需要调整densD
if(j >= MAXMINUTIANUM)
{
bGood = false;
break;
}
}
// 调整densD
if(!bGood)
{
densD++; // 加一
continue;
}
// 将端点装配到g_Feature
j = 0;
for(i = 0; i < EndNum; i++)
{
if(EndArr[i].x == 0 || EndArr[i].y == 0)
{
continue;
}
// 沿纹线找到第8个点
lpNow = g_lpOrgFinger + EndArr[i].y * IMGW + EndArr[i].x;
if(GetByDis(lpNow, &lpDis, 8) != 0)
{
continue;
}
// 求该点与端点连线的角度
angle1 = GetAngle(EndArr[i].x, EndArr[i].y, PX(lpDis), PY(lpDis));
angle2 = *(g_lpOrient + EndArr[i].y*IMGW + EndArr[i].x);
angle3 = GetAngleDis(angle1, angle2);
// 如果成钝角,则表示该特征点方向与该点方向场方向相反
if(angle3 > 90 && angle3 < 270)
{
g_Feature.MinutiaArr[j].Direction = angle2 + 180;
}
else // 如果成锐角,则表示该特征点方向与该点方向场方向相同
{
g_Feature.MinutiaArr[j].Direction = angle2;
}
//若两点互相匹配,则两点周围的相对方向场相似
a = g_Feature.MinutiaArr[j].Direction / EPI + PI/4.0;
x11 = EndArr[i].x + (sint32)(trilen*cos(a)+ 0.5);
y11 = EndArr[i].y + (sint32)(trilen*sin(a)+ 0.5);
g_Feature.MinutiaArr[j].Triangle[0] = *(g_lpOrient + y11*IMGW + x11);
a += PI*2/3.0;
x11 = EndArr[i].x + (sint32)(trilen*cos(a)+ 0.5);
y11 = EndArr[i].y + (sint32)(trilen*sin(a)+ 0.5);
g_Feature.MinutiaArr[j].Triangle[1] = *(g_lpOrient + y11*IMGW + x11);
a += PI*2/3.0;
x11 = EndArr[i].x + (sint32)(trilen*cos(a)+ 0.5);
y11 = EndArr[i].y + (sint32)(trilen*sin(a)+ 0.5);
g_Feature.MinutiaArr[j].Triangle[2] = *(g_lpOrient + y11*IMGW + x11);
g_Feature.MinutiaArr[j].x = EndArr[i].x; //横坐标
g_Feature.MinutiaArr[j].y = EndArr[i].y; //纵坐标
g_Feature.MinutiaArr[j].Type = VF_MINUTIA_END; //类型
j++;
}
EndNum = j; // 端点数目
// 去掉距离太近的叉点
for(i = 0; i < ForkNum-1; i++)
{
flag = false;
for(j = i+1; j < ForkNum; j++)
{
d = sqrt((double)((ForkArr[i].x-ForkArr[j].x)*(ForkArr[i].x-ForkArr[j].x) +
(ForkArr[i].y-ForkArr[j].y)*(ForkArr[i].y-ForkArr[j].y)));
if(d <= densD-2)
{
ForkArr[j].x = 0;
ForkArr[j].y = 0;
flag = true;
}
}
if(flag)
{
ForkArr[i].x = 0;
ForkArr[i].y = 0;
}
}
// 统计新的真正的叉点
j = 0;
for(i = 0; i < ForkNum; i++)
{
if(ForkArr[i].x == 0 || ForkArr[i].y == 0)
{
continue;
}
lpNow = g_lpOrgFinger + ForkArr[i].y*IMGW + ForkArr[i].x;
tempPtr[0] = lpNow;
*tempPtr[0] = 255;
// 检查是否真正叉点
n = 0;
for(k = 0; k < 8; k++)
{
if(*(lpNow + SiteU8[k]) == 0)
{
tempPtr[n+1] = lpNow + SiteU8[k];
*tempPtr[n+1] = 255;
n++;
}
}
for(k = 0; k < 4; k++)
{
*tempPtr[k] = 0;
}
if(n != 3)
{
continue;
}
j++;
// 特征点总数大于允许最大数目则要调整densD
if(EndNum + j >= MAXMINUTIANUM)
{
densD++;
bGood = false;
break;
}
}
if(!bGood)
{
continue;
}
// 装配叉点到g_Feature
j = 0;
for(i = 0; i < ForkNum; i++)
{
if(ForkArr[i].x == 0 || ForkArr[i].y == 0)
{
continue;
}
lpNow = g_lpOrgFinger + ForkArr[i].y*IMGW + ForkArr[i].x;
tempPtr[0] = lpNow;
*tempPtr[0] = 255;
n = 0;
for(k = 0; k < 8; k++)
{
if(*(lpNow + SiteU8[k]) == 0)
{
tempPtr[n+1] = lpNow + SiteU8[k];
*tempPtr[n+1] = 255;
n++;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -