指紋算法需求
指紋特徵值生成、比對API庫需求:
- 可輸出指紋圖像。圖像格式爲bmp,小於等於500DPI,不大於50K。
- 可輸出指紋模板。生成模板需要至少採集幾次指紋需說明,建議不超過三次。模板大小不超過1K。模板生成時間不大於1秒。
- 可輸出指紋特徵值(可以是非字符串格式)。特徵值大小不超過512B。
- 可輸出指紋特徵值字符串。字符串爲可見字符,長度不超1024。
- 指紋比對時,支持輸入指紋特徵值字符串比對。
- 指紋比對時,支持輸入指紋圖像進行比對。
- 指紋比對API支持多線程模式,支持大併發調用。
- 指紋比對支持1:1,即指紋驗證。
- 指紋比對支持1:N,即指紋辨識。
- 指紋比對時每枚比對速度要求小於0.1秒。
- 認假率小於0.0001% 。
- 拒真率小於0.75% 。
- 庫要求32位,但支持在64位操作系統運行。
- 可提供dll、jar兩種形式API的庫。
-
環境要求
系統列表 |
|
Windows |
2003 server/xp/win7 |
Linux |
>=內核2.6 |
Aix unix |
>=5.2 |
Android |
|
廢話不多說,直接上乾貨,先附上一張指紋算法項目的思路流程圖:
一、先講解一下指紋算法源碼的思路
從指紋圖像中提取指紋特徵:
int __stdcall Analyze(BYTE *lpImage, int Width, int Height, BYTE *lpFeature, int *lpSize)
{
///////////////////////////////////////////////////////////////////////
// Width: [in] 指紋圖像寬度
// Height: [in] 指紋圖像高度
// lpImage: [in] 指紋圖像數據指針
// Resolution: [in] 指紋圖像分辨率,默認500
// lpFeature: [out] 提取的指紋特徵數據指針
// lpSize: [out] 指紋特徵數據大小
// TODO: Add your implementation code here
VF_RETURN re;
// 導入指紋圖像數據
VF_ImportFinger(lpImage, Width, Height);
// 處理指紋圖像,提取指紋特徵
re = VF_Process();
if (re != VF_OK)
return re;
// 對指紋特徵進行編碼
re = VF_FeatureEncode(&g_Feature, lpFeature, lpSize);
if (re != VF_OK)
return re;
return 0;
}
對兩個指紋進行特徵比對:
int __stdcall PatternMatch(BYTE *lpFeature1, BYTE *lpFeature2, int *lpScore)
{
// lpFeature1: [in] 第一個指紋特徵
// lpFeature2: [in] 第二個指紋特徵
// lpScore: [out] 比對的相似度
// FastMode: [in] 是否進行快速模式比對
VF_RETURN re1,re2;
MATCHRESULT mr;
FEATURE feat1, feat2;
// 第一個指紋特徵的解碼
re1 = VF_FeatureDecode(lpFeature1, &feat1);
if (re1 != VF_OK)
{
printf("圖像1解碼失敗\n");
return 0;
//return re1;
}
// 第二個指紋特徵的解碼
re2 = VF_FeatureDecode(lpFeature2, &feat2);
if (re2 != VF_OK)
{
printf("圖像2解碼失敗\n");
return 0;
//return re2;
}
*lpScore = 0;
bool FastMode = true;
if (FastMode)
{
// 快速模式的比對
VF_VerifyMatch(&feat1, &feat2, &mr, VF_MATCHMODE_IDENTIFY);
}
else
{
// 精確模式的比對
VF_VerifyMatch(&feat1, &feat2, &mr, VF_MATCHMODE_VERIFY);
}
// 匹配的相似度
//*lpScore = mr.Similarity/10;
*lpScore = mr.Similarity;
/*if (mr.MMCount < 8)
{
*lpScore = 0;
}
else
{
*lpScore = mr.Similarity;
}*/
return 0;
}
二、怎麼調用該源碼算法庫
在此不進行過多重複敘述,請移步我的另外一篇博文:https://blog.csdn.net/yanxiaolx/article/details/78730291
三、測試該算法識別率的測試demo
測試指紋算法的效果好壞,有3個指標:拒真率,認假率和識別率
測試的指紋庫github已經上傳:點擊這裏
正樣本:所有指紋全部來自同一手指
負樣本:所有指紋均來自不同手指
拒真率:正樣本測試不通過的比率
認假率:負樣本測試通過的比率
識別率:1 -(拒真率 + 認假率) / 2
第一個函數,對兩個指紋圖片的識別進行測試:
void test3()
{
char ImagePathName1[100] = "D:\\c++code\\test\\1 (1).BMP";
char ImagePathName2[100] = "D:\\c++code\\test\\1 (1).BMP";
BYTE lpFeature1[500] = { 0 };
BYTE lpFeature2[500] = { 0 };
int lpSize1 = 0, lpSize2 = 0, score = 0;
int iReturn = 0;
sprintf(ImagePathName1, "D:\\c++code\\test\\1 (13).BMP");
sprintf(ImagePathName2, "D:\\c++code\\test\\1 (14).BMP");
iReturn = AnalyzeFromFile(ImagePathName1, lpFeature1, &lpSize1);
if (iReturn != 0)
{
printf("從BMP文件中讀取圖像1失敗\n");
}
iReturn = AnalyzeFromFile(ImagePathName2, lpFeature2, &lpSize2);
if (iReturn != 0)
{
printf("從BMP文件中讀取圖像2失敗\n");
}
PatternMatch(lpFeature1, lpFeature2, &score);//對指紋進行比對
if (score >35)//原來是60
{
printf("Same Fingerprint! \n");
}
else
{
printf("Different Fingerprint! \n");
}
return;
}
測試認假率:
int count1 = 0, Arr_score1[11476] = { 0 };
void test1(double *Arr1)//測試認假率
{
char ImagePathName1[100] = "E:\\c++code\\指紋測試資料\\SyntFingerDLL\\測試分類指紋庫圖片\\0.正常\\1 (1).BMP";
char ImagePathName2[100] = "E:\\c++code\\指紋測試資料\\SyntFingerDLL\\測試分類指紋庫圖片\\0.正常\\1 (1).BMP";
BYTE lpFeature1[500] = { 0 };
BYTE lpFeature2[500] = { 0 };
int lpSize1=0, lpSize2=0, score=0;
int iReturn = 0;
//DWORD start_time = GetTickCount();
for (int i = 1; i <152; i++)//注意修改循環後面的值
{
sprintf(ImagePathName1, "E:\\c++code\\指紋測試資料\\SyntFingerDLL\\測試分類指紋庫圖片\\0.正常\\1 (%d).BMP", i);
for (int j = i+1; j <=152; j++)//儘量保證假樣本多,(n-1)*n/2
{
sprintf(ImagePathName2, "E:\\c++code\\指紋測試資料\\SyntFingerDLL\\測試分類指紋庫圖片\\0.正常\\1 (%d).BMP", j);
iReturn = AnalyzeFromFile(ImagePathName1, lpFeature1, &lpSize1);
if (iReturn != 0)
{
printf("從BMP文件中讀取圖像%d失敗\n", i);
break;
}
iReturn = AnalyzeFromFile(ImagePathName2, lpFeature2, &lpSize2);
if (iReturn != 0)
{
printf("從BMP文件中讀取圖像%d失敗\n", j);
continue;
}
PatternMatch(lpFeature1, lpFeature2, &score);//對指紋進行比對
Arr_score1[count1] = score;
count1++;
cout << count1 <<",i=" << i << ",j=" << j << endl;
}
}
//DWORD end_time = GetTickCount();
//cout << "The run time is:" << (end_time - start_time)/23436 << "ms!" << endl;
FILE *f;
f = fopen("D:\\c++code\\指紋測試資料\\認假test1\\score.txt", "w");
if (f == NULL)
{
printf("ERROR!");
return;
}
for (int i = 1; i <= 1000; i++)
{
int Y_count = 0, N_count = 0;
for (int j = 0; j < count1; j++)
{
if (Arr_score1[j]>=i-1)
{
Y_count++;
}
else
{
N_count++;
}
}
fprintf(f, "序號=%d,Y_count=%d,N_count=%d,sum=%d,認假率=%lf\n", i, Y_count, N_count, Y_count + N_count, Y_count*1.0 / (Y_count + N_count));
Arr1[i - 1] = Y_count*1.0 / (Y_count + N_count);
}
for (int j = 0; j < count1; j++)
{
fprintf(f, "序號=%d,score=%d\n", j + 1, Arr_score1[j]);
}
fclose(f);
return ;
}
測試拒真率:
int count2 = 0;
int Arr_score2[12000] = { 0 };
void test2(double *Arr2)//測試拒真率
{
char ImagePathName1[100] = "D:\\c++code\\指紋測試資料\\指紋採集2014.7.3-bmp\\1 (1)\\1 (1).BMP";
char ImagePathName2[100] = "D:\\c++code\\指紋測試資料\\指紋採集2014.7.3-bmp\\1 (1)\\1 (1).BMP";
BYTE lpFeature1[500] = { 0 };
BYTE lpFeature2[500] = { 0 };
int lpSize1 = 0, lpSize2 = 0, score = 0;
int iReturn = 0;
int N=10;//修改文件夾方便
//DWORD start_time = GetTickCount();
for (int k =1; k <= 232; k++)
{
for (int i = 1; i <= N; i++)//注意修改循環後面的值
{
sprintf(ImagePathName1, "D:\\c++code\\指紋測試資料\\指紋採集2014.7.3-bmp\\1 (%d)\\1 (%d).BMP", k, i);
for (int j = i; j <= N; j++)//不考慮比對過的重複,儘量保證真樣本多,n*(n+1)/2
{
//count++;
sprintf(ImagePathName2, "D:\\c++code\\指紋測試資料\\指紋採集2014.7.3-bmp\\1 (%d)\\1 (%d).BMP", k, j);
iReturn = AnalyzeFromFile(ImagePathName1, lpFeature1, &lpSize1);
if (iReturn != 0)
{
printf("從BMP文件中讀取圖像%d失敗\n", i);
break;
}
iReturn = AnalyzeFromFile(ImagePathName2, lpFeature2, &lpSize2);
if (iReturn != 0)
{
printf("從BMP文件中讀取圖像%d失敗\n", j);
continue;
}
PatternMatch(lpFeature1, lpFeature2, &score);//對指紋進行比對
Arr_score2[count2] = score;
count2++;
cout << count2 << ",k=" << k << ",i="<<i<<",j=" << j << endl;
}
}
}
//DWORD end_time = GetTickCount();
FILE *f;
f = fopen("D:\\c++code\\指紋測試資料\\拒真test2\\score.txt", "w");
if (f == NULL)
{
printf("ERROR!");
return ;
}
for (int i = 1; i <= 1000; i++)
{
int Y_count = 0, N_count = 0;
for (int j = 0; j < count2; j++)
{
if (Arr_score2[j]>=i-1)
{
Y_count++;
}
else
{
N_count++;
}
}
fprintf(f, "score=%d,Y_count=%d,N_count=%d,sum=%d,拒真率=%lf\n", i, Y_count, N_count, Y_count + N_count, N_count*1.0 / (Y_count + N_count));
Arr2[i - 1] = N_count*1.0 / (Y_count + N_count);
}
for (int j = 0; j < count2; j++)
{
fprintf(f, "序號=%d,score=%d\n", j + 1, Arr_score2[j]);
}
fclose(f);
return ;
}
最後輸出各種識別率,存在記事本中:
int main()
{
double Arr1[1000] = { 0 }, Arr2[1000] = { 0 }, Arr3[1000] = { 0 };
test2(Arr2);//測試拒真率
test1(Arr1);//測試認假率
for (int i = 0; i < 1000; i++)
{
Arr3[i] = 1 - (Arr1[i] + Arr2[i]) / 2;
}
FILE *f;
f = fopen("D:\\c++code\\指紋測試資料\\識別率4.txt", "w");
if (f == NULL)
{
printf("ERROR!");
return 0;
}
for (int i = 0; i <1000; i++)
{
fprintf(f, "score=%d,認假率=%lf,拒真率=%lf,識別率=%lf\n", i , Arr1[i],Arr2[i],Arr3[i]);
printf("score=%d,認假率=%lf,拒真率=%lf,識別率=%lf\n", i , Arr1[i], Arr2[i], Arr3[i]);
}
fclose(f);
//test3();
system("pause");
return 0;
}
本人一共測試了正副樣本大概各10萬對左右,在不同的閾值下,指紋的識別率分佈大概呈現正態分佈,其中score表示閾值,如下圖數據記錄:
由上圖可以看出,當score=19時,識別率=0.965707達到最優峯值。
下面舉例聊聊指紋算法在銀行的業務應用流程:
- 指紋採集
(1)櫃員到支行以上的部門進行指紋集中採集;
(2)採集時需要同時運行並打開平臺、客戶端、設備,同時完成聯接;
(3)採集時至少採集櫃員的三枚手指,優先採集左手手指,同時優先採集食指、中指、大拇指;
(4)採集指紋功能由客戶端、設備完成。指紋在設備上獲取後,由客戶端完成模板的處理,再由客戶端上傳平臺;
(5)平臺將客戶端上傳的櫃員號、指紋圖像、指紋特徵值模板、指頭標記進行處理,完成平臺用戶、櫃員、指紋的綁定。
2.指紋比對
(一)櫃員簽到流程
(1)櫃員簽到過程中的指紋驗證是在系統平臺上完成;
(2)首先從終端櫃面輸入櫃員號,然後櫃員將註冊過的手指在設備上按壓來實時採集指紋;
(3)設備對實時採集的指紋圖像進行處理並生成指紋特徵值,同時上傳到平臺;
(4)平臺將指紋特徵值與已採集的指紋模板進行比對,判斷合法性;
(5)比對不成功,則返回錯誤值。比對成功,則平臺將櫃員關聯的用戶密碼返回給終端;終端櫃面根據此密碼登錄前端系統。
(二)業務授權流程
(1)櫃員授權過程中的指紋驗證是在系統平臺上完成;
(2)在等待輸入授權櫃員號時,具有授權權限的櫃員將註冊過的手指按壓到設備進行採集指紋;
(3)設備對實時採集的指紋圖像進行處理並生成指紋特徵值,同時上傳到平臺;
(4)平臺將指紋特徵值與已採集的指紋模板進行比對,判斷合法性;
(5)比對不成功,則返回錯誤值。比對成功,則平臺將櫃員關聯的用戶密碼返回給終端,終端櫃面根據此密碼完成授權過程。
該項目是傳統指紋識別算法,當然識別率不是最優的,至於更優的指紋識別算法版本出於商業機密,暫時不能開源,哈哈,好氣是不是,不要打我。
未完待續,空了再繼續完善博文