指紋模式識別演算法原始碼及其測試和應用方法
指紋演算法需求
指紋特徵值生成、比對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;
}
二、怎麼呼叫該原始碼演算法庫
三、測試該演算法識別率的測試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)比對不成功,則返回錯誤值。比對成功,則平臺將櫃員關聯的使用者密碼返回給終端,終端櫃面根據此密碼完成授權過程。
該專案是傳統指紋識別演算法,當然識別率不是最優的,至於更優的指紋識別演算法版本出於商業機密,暫時不能開源,哈哈,好氣是不是,不要打我。
未完待續,空了再繼續完善博文