1. 程式人生 > >指紋模式識別演算法原始碼及其測試和應用方法

指紋模式識別演算法原始碼及其測試和應用方法

指紋演算法需求

指紋特徵值生成、比對API庫需求:

  1. 可輸出指紋影象。影象格式為bmp,小於等於500DPI,不大於50K。
  2. 可輸出指紋模板。生成模板需要至少採集幾次指紋需說明,建議不超過三次。模板大小不超過1K。模板生成時間不大於1秒。
  3. 可輸出指紋特徵值(可以是非字串格式)。特徵值大小不超過512B。
  4. 可輸出指紋特徵值字串。字串為可見字元,長度不超1024。
  5. 指紋比對時,支援輸入指紋特徵值字串比對。
  6. 指紋比對時,支援輸入指紋影象進行比對。
  7. 指紋比對API支援多執行緒模式,支援大併發呼叫。
  8. 指紋比對支援1:1,即指紋驗證。
  9. 指紋比對支援1:N,即指紋辨識。
  10. 指紋比對時每枚比對速度要求小於0.1秒。
  11. 認假率小於0.0001% 。
  12. 拒真率小於0.75% 。
  13. 庫要求32位,但支援在64位作業系統執行。
  14. 可提供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. 指紋採集

(1)櫃員到支行以上的部門進行指紋集中採集;

(2)採集時需要同時執行並開啟平臺、客戶端、裝置,同時完成聯接;

(3)採集時至少採集櫃員的三枚手指,優先採集左手手指,同時優先採集食指、中指、大拇指;

(4)採集指紋功能由客戶端、裝置完成。指紋在裝置上獲取後,由客戶端完成模板的處理,再由客戶端上傳平臺;

(5)平臺將客戶端上傳的櫃員號、指紋影象、指紋特徵值模板、指頭標記進行處理,完成平臺使用者、櫃員、指紋的繫結。

     2.指紋比對

(一)櫃員簽到流程

(1)櫃員簽到過程中的指紋驗證是在系統平臺上完成;

(2)首先從終端櫃面輸入櫃員號,然後櫃員將註冊過的手指在裝置上按壓來實時採集指紋;

(3)裝置對實時採集的指紋影象進行處理並生成指紋特徵值,同時上傳到平臺;

(4)平臺將指紋特徵值與已採集的指紋模板進行比對,判斷合法性;

(5)比對不成功,則返回錯誤值。比對成功,則平臺將櫃員關聯的使用者密碼返回給終端;終端櫃面根據此密碼登入前端系統。

(二)業務授權流程

(1)櫃員授權過程中的指紋驗證是在系統平臺上完成;

(2)在等待輸入授權櫃員號時,具有授權許可權的櫃員將註冊過的手指按壓到裝置進行採集指紋;

(3)裝置對實時採集的指紋影象進行處理並生成指紋特徵值,同時上傳到平臺;

(4)平臺將指紋特徵值與已採集的指紋模板進行比對,判斷合法性;

(5)比對不成功,則返回錯誤值。比對成功,則平臺將櫃員關聯的使用者密碼返回給終端,終端櫃面根據此密碼完成授權過程。

該專案是傳統指紋識別演算法,當然識別率不是最優的,至於更優的指紋識別演算法版本出於商業機密,暫時不能開源,哈哈,好氣是不是,不要打我。

未完待續,空了再繼續完善博文