使用Delphi接入虹软人脸识别ArcFace,开发人脸库服务器

article/2025/10/9 18:35:57

利用虹软 SDK 开发局域网人脸库服务器

一、选择开发平台
以前做单位食堂人脸识别就餐时,会用到在线人脸识别,终端设备必须并入互联网,单位对人脸信息比较敏感,客户会要求提供内部网人脸库使用。

利用人脸识别 SDK 开发人脸库服务器,多终端人脸识别,集中管理人脸库,以适用于小范围内人脸识别应用,如单位食堂、会馆、学校以及工厂的内部管理等场景。

内部网人脸库服务器的主要构成包括人脸识别,HTTP 网络通讯 ,数据库三个部分,网络通讯与数据库是常规技术,现在关键是选用人脸识别算法,人脸识别技术可不是一般程序员能够开发出来的,国内也就几家在做核心研发,比较出名的有百度,虹软件等。在比较了百度,虹软等 SDK 后,从开发难度,识别灵敏度考虑,百度的 SDK 离线文件下载就 700M,虹软件只有几十M,看来使用虹软 SDK 更容易掌握技术, 就决定选择虹软 SDK 吧。

我使用的开发工具是 DELPHI,这个已算是老人首先工具了,很多新手并不熟悉这个工具,delphi 可以调用 C++库,也可以跟 C 一样做底层开发,是很高效的开发工具。刚好虹软提供了 C++库 SDK,好利用它来做人脸识别部分。

如何利用虹软技术来做人脸识别开发,网上可查询到的资料很多,首先登录虹软开发者中心网站,注册成为虹软开发者,进入我的应用,新建应用,下载人脸识别(ArcFace)Windows(X86),语言选择C++ ,我下载的是ArcSoft_ArcFace_Windows_x86_V3.0.zip,记得此时还要复制你刚创建应用时相应的APP_IDSDK_KEY,后面编程中调用 SDK 时要用到。
在这里插入图片描述虹软提供的 SDK 很简单,并非开源,只有函数库文件并没有提供源码,提供了 PDF 文档很详细,我非C开发,DEMO 源码我无法使用,还有一组 C++库头文件,我用的是 DELPHI 开发工具,C 函数与 PAS调用约定不一样,函数说明对不上,必须自己改为 PAS 的函数说明文件,sdk 中 amcomdef.h 就有函数中要用到数据类型定义,先对照自己的开发工具版本,把数据类型定义好,在 DELPHI 中按 C++调用函数方法就可以了,这里选择 cdecl 约定调用,改造后数据类型定义与函数说明放在一个文件中,如下:

`unit faceUtils;
interface
usesTypes;
varFFaceHandle: pointer; 
constASF_DETECT_MODE_VIDEO = $00000000; //Video 模式,一般用于多帧连续检测ASF_DETECT_MODE_IMAGE = $FFFFFFFF; // Image 模式,一般用于静态图的单次检测ASF_NONE = $00000000; //无属性ASF_FACE_DETECT = $00000001; //此处 detect 可以是 tracking 或者 detection 两个引擎之一,具体
的选择由 detect mode 确定ASF_FACERECOGNITION = $00000004; //人脸特征ASF_AGE = $00000008; //年龄ASF_GENDER = $00000010; //性别ASF_FACE3DANGLE = $00000020; //3D 角度ASF_LIVENESS = $00000080; //RGB 活体ASF_IR_LIVENESS = $00000400; //红外活体
//检测时候 Orientation 优先级别ASF_OP_0_ONLY = $1; // 0, 0, ...ASF_OP_90_ONLY = $2; // 90, 90, ...ASF_OP_270_ONLY = $3; // 270, 270, ...ASF_OP_180_ONLY = $4; // 180, 180, ...ASF_OP_0_HIGHER_EXT = $5; // 0, 90, 270, 180, 0, 90, 270, 180, ...
/// 当前 SDK 版本,VIDEO 模式下支持 ASF_OP_0_HIGHER_EXT 检测,IMAGE 模式不支持ASF_OC_0 = $1; // 0 degreeASF_OC_90 = $2; // 90 degreeASF_OC_270 = $3; // 270 degreeASF_OC_180 = $4; // 180 degreeASF_OC_30 = $5; // 30 degreeASF_OC_60 = $6; // 60 degreeASF_OC_120 = $7; // 120 degreeASF_OC_150 = $8; // 150 degreeASF_OC_210 = $9; // 210 degreeASF_OC_240 = $A; // 240 degreeASF_OC_300 = $B; // 300 degreeASF_OC_330 = $C; // 330 degreeASVL_PAF_I420 = $601;ASVL_PAF_RGB24_B8G8R8 = $201; // 513MOK = (0);MERR_NONE = (0);
type//检测模型ASF_DetectModel = (ASF_DETECT_MODEL_RGB = $1 //RGB 图像检测模型//预留扩展其他检测模型);//人脸比对可选的模型ASF_CompareModel = (ASF_LIFE_PHOTO = $1, //用于生活照之间的特征比对,推荐阈值 0.80ASF_ID_PHOTO = $2 //用于证件照或生活照与证件照之间的特征比对,推荐阈值 0.82);
//duDWORD,LongInt,Cardinal 三种数据 zhi 类型都一样,都是 32 位无符号 dao 整型(无符号就是没
有负的,
//最小值内为 0,和之相对的有符号 32 位整型就是最常用容的 Integer)
typeMPChar = pchar;Mint8 = Shortint; // -128..127 signed 8-bitPMint8 = ^Mint8;Muint8 = Byte; //0..255 unsigned 8-bitPMuint8 = ^Muint8;Mint16 = Smallint; //-32768..32767 signed 16-bitPMint16 = ^Mint16;Muint16 = WORD; //word 是 2 字节 unsigned 16-bitPMuint16 = ^Muint16;MInt32 = Longint; //-2147483648..2147483647 signed 32-bitPMint32 = ^MInt32;MUInt32 = DWORD; //Longword unsigned 32-bitPMuint32 = ^MUInt32;pMByte = ^MByte;MByte = Byte; //0..255 unsigned 8-bitMWord = WORD; //word 是 2 字节 unsigned 16-bitMFloat = Single; // 这里必须是 Single ,4bytes 32 位浮点数.
//不能 double;8 bytes 64 位浮点数 否则无法输出人脸比较pMRECT = ^MRECT;MRECT = recordleft: MInt32;top: MInt32;right: MInt32;bottom: MInt32;end;//单人脸信息LPASF_SingleFaceInfo = ^ASF_SingleFaceInfo;ASF_SingleFaceInfo = recordfaceRect: MRECT; // 人脸框信息faceOrient: MInt32; // 输入图像的角度,可以参考 ArcFaceCompare_OrientCodeend;//多人脸信息LPASF_MultiFaceInfo = ^ASF_MultiFaceInfo;ASF_MultiFaceInfo = recordfaceRect: array of MRECT; // 人脸框数组faceOrient: array of MInt32; // 人脸角度数组faceNum: MInt32; // 检测到的人脸个数faceID: MInt32; //在 VIDEO 模式下有效,IMAGE 模式下为空end;LPASF_FaceFeature = ^ASF_FaceFeature;ASF_FaceFeature = recordfeature: pMByte; //array[0..2048] of byte; // pMuint8; // 人脸特征信息featureSize: MInt32; // 人脸特征信息长度end;LPASF_FaceAngle = ^ASF_FaceAngle;ASF_FaceAngle = recordroll: MFloat;yaw: MFloat;pitch: MFloat;status: integer; //0: 正常,其他数值:出错num: PMuint32end;LPASF_AgeInfo = ^ASF_AgeInfo;ASF_AgeInfo = recordageArray: MInt32; // "0" 代表不确定,num: MInt32; // 检测的人脸个数end;//结构体LPASVLOFFSCREEN = ^ASVLOFFSCREEN;ASVLOFFSCREEN = recordu32PixelArrayFormat: MUInt32; //BMP 格式必须为 ASVL_PAF_RGB24_B8G8R8,i32Width: MInt32; //图像的每行像素数i32Height: MInt32;ppu8Plane: array[0..3] of pMUInt8; //ppu8Plane 为一个批向 byte 数组的指针数组,//这里面会保存我们刚刚转换后的图片数据。可以传递四幅图片。pi32Pitch: array[0..3] of MInt32; 。end;LPASVLOF = ^ASVLOFFSCREEN;__tag_ASVL_OFFSCREEN = ASVLOFFSCREEN;ASF_ImageData = ASVLOFFSCREEN;LPASF_ImageData = ^ASVLOFFSCREEN;LPASF_LivenessInfo = ^ASF_LivenessInfo;ASF_LivenessInfo = recordisLive: pMInt32; // [out] 判断是否真人, 0:非真人;// 1:真人;// -1:不确定;// -2:传入人脸数>1;// -3: 人脸过小// -4: 角度过大// -5: 人脸超出边界num: MInt32;end;
// 激活文件信息LPASF_ActiveFileInfo = ^ASF_ActiveFileInfo;ASF_ActiveFileInfo = recordstartTime: MPChar; //开始时间endTime: MPChar; //截止时间platform: MPChar; //平台sdkType: MPChar; //sdk 类型appId: MPChar; //APPIDsdkKey: MPChar; //SDKKEYsdkVersion: MPChar; //SDK 版本号fileVersion: MPChar; //激活文件版本号end;//版本信息LPASF_VERSION = ^ASF_VERSION;ASF_VERSION = recordVersion: MPChar; // 版本号BuildDate: MPChar; // 构建日期CopyRight: MPChar; // Copyrightend;
function ASFActivation(appId: PAnsiChar; sdkKey: PAnsiChar): integer; cdecl;
function ASFInitEngine(detectMode: MInt32;detectFaceOrientPriority: MInt32;detectFaceScaleVal: MInt32; //人脸在图片中所占比例,有效数值为 2-32 ;detectFaceMaxNum: MInt32;combinedMask: MInt32; pEngine: pointer): integer; cdecl;
function ASFDetectFaces(pEngine: pointer; width: MInt32; height: MInt32;format: MInt32; imgData: PMuint8; detectedFaces: pointer): integer; cdecl;
function ASFDetectFacesEx(pEngine: pointer;imgData: LPASF_ImageData; // [in] 图片数据detectedFaces: LPASF_MultiFaceInfo; // [out] 检测到的人脸信息detectModel: ASF_DetectModel = ASF_DETECT_MODEL_RGB ): integer; cdecl; // [out]
function ASFFaceFeatureExtract(pEngine: pointer; width: MInt32; height: MInt32;format: MInt32; imgData: PMuint8;faceInfo: pointer; // [in] 单张人脸位置和角度信息feature: pointer): integer; cdecl; // [out] 人脸特征
function ASFGetFace3DAngle(pEngine: pointer; // [in] 引擎 handlep3DAngleInfo: LPASF_FaceAngle): integer; cdecl; // [out] 检测到脸部 3D 角度信息
function ASFProcess_IR(hEngine: pointer; width: MInt32; height: MInt32; format: MInt32; imgData: 
PMuint8; var detectedFaces: ASF_MultiFaceInfo; combinedMask: MInt32): integer; cdecl;
function ASFFaceFeatureCompare(pEngine: pointer; // [in] 引擎 handlevar feature1: ASF_FaceFeature; // [in] 待比较人脸特征 1var feature2: ASF_FaceFeature; // [in] 待比较人脸特征 2var confidenceLevel: MFloat; // [out] 比较结果,置信度数值compareModel: ASF_CompareModel = ASF_LIFE_PHOTO): integer; cdecl;
function ASFGetAge(pEngine: pointer; // [in] 引擎 handleageInfo: LPASF_AgeInfo // [out] 检测到的年龄信息): integer; cdecl;
function ASFGetLivenessScore(pEngine: pointer; // [in] 引擎 handlelivenessInfo: LPASF_LivenessInfo // [out] 检测 RGB 活体结果): integer; cdecl;
function ASFGetVersion(): ASF_VERSION; cdecl;
implementation
varFMASK: MInt32;iRet: integer;appId, sdkKey: string;
constDllName = 'libarcsoft_face_engine.dll';
function ASFActivation; cdecl; external DllName;
function ASFInitEngine; cdecl; external DllName;
function ASFDetectFaces; cdecl; external DllName;
function ASFDetectFacesEx; cdecl; external DllName;
function ASFFaceFeatureExtract; cdecl; external DllName;
function ASFGetFace3DAngle; cdecl; external DllName;
function ASFProcess_IR; cdecl; external DllName;
function ASFFaceFeatureCompare; cdecl; external DllName;
function ASFGetAge; cdecl; external DllName;
function ASFGetVersion; cdecl; external DllName;
function ASFGetLivenessScore; cdecl; external DllName;
initializationFFaceHandle := nil;FMASK := ASF_FACE_DETECTor ASF_FACERECOGNITIONor ASF_AGE or ASF_GENDERor ASF_FACE3DANGLEor ASF_LIVENESS or ASF_IR_LIVENESS;appId := '9JwgKfrScuRs4vJ6huhPzbuqjS7ZTnK3ktYyk75j8xVS';sdkKey := 'AGjZPqgvNFnwy4brfUHNpnZXfTaivDubCK6evzGqMmMk';iRet := ASFActivation(pchar(appId), pchar(sdkKey));iRet := ASFInitEngine(ASF_DETECT_MODE_IMAGE,ASF_OP_0_HIGHER_EXT,16, //2-32 最小人脸5, FMASK, @FFACEHandle);
…
END;

经过测试 ,虹软 SDK 完全能正常工作,能检测到人脸,并比较两张人脸图片的相似度。下面正式开发我的人脸库服务器。

二、编写人脸库 WEB 服务器
DELPHI 开发人脸库服务器,必须实现人脸图片的接收、保存以及信息的返回,采用第三方控件RealThinClient 控件集来实现 webserver功能,MYSQL 数据库来保存人脸特征信息及身份信息,另外为了访问 MYSQL 数据库,还采用 MySQL.Dac 控件集。在这里插入图片描述
Mysql 数据库采用嵌入式安装,以便于打包,在 mysql 中创建库 facelib , facelib 中创建两个表dbfacelibdbuserlib 结构如下:

CREATE TABLE `dbfacelib` (
人脸库服务器
WebServer
ArcFace SDK
Mysql
客户端人脸识别`row_id` int(11) NOT NULL AUTO_INCREMENT,`feature` varchar(1536) CHARACTER SET ascii DEFAULT NULL,`session` varchar(32) CHARACTER SET ascii DEFAULT NULL,`id` varchar(10) CHARACTER SET ascii DEFAULT NULL,PRIMARY KEY (`row_id`),UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=429 DEFAULT CHARSET=utf8;
CREATE TABLE `dbuserlib` (`row_id` int(11) NOT NULL AUTO_INCREMENT,`facerowid` int(11) DEFAULT '0',`id` varchar(20) CHARACTER SET ascii DEFAULT NULL,`name` varchar(10) DEFAULT NULL,`sex` varchar(4) DEFAULT NULL,`descript` varchar(100) DEFAULT NULL,`identificatin` varchar(18) DEFAULT NULL,`address` varchar(40) DEFAULT NULL,`mobile` varchar(11) DEFAULT NULL,`marriage` smallint(6) DEFAULT NULL,`birthday` date DEFAULT NULL,`url` varchar(120) DEFAULT NULL,PRIMARY KEY (`row_id`),UNIQUE KEY `id` (`id`),KEY `facerowid` (`facerowid`)
) ENGINE=MyISAM AUTO_INCREMENT=429 DEFAULT CHARSET=utf8;

dbfacelibfeature 字段用于保存人脸特征串,人脸比较是不用建索引的,独条比较,搜索到相似度大于 80 时返回 rowid ,再在 dbuserlib 中查询对应身份信息。返回给客户端。

DELPHI 的 IDE 上 点击菜单“File->New->Application”,新建一个应用,form1 改名 frMain, 在 frMain上放一个TRtcHttpServer 命名 两个 TRtcDataProvider 改名为 rtcGetrtcPut ,分别实现 httpsever的 GET 命令与 put 命令的解释执行,rtcGet 实现了 web 网页解释输出,数据库访问输出,rtcPut 则负责实现文件上传,人脸上传后识别、保存、比较等功能,工程保存为 facesrvr.dpr

rtcGetrtcPutCheckRequestDataReceived 事件中代码如下:

procedure TfrMainDm.rtcGetCheckRequest(Sender: TRtcConnection);
varfname, FileName: string;
beginwith TRtcDataServer(Sender) do beginif (Request.Method = 'PUT') then exit;FileName := UpperCase(Request.FileName);if FileName = '/' then FileName := '/index.html';fname := GetFullFileName(FileName);if (fname <> '') and (File_Exists(fname)) then begin{ We will store the file name in our request,so we don't have to recreate it again later. }Request.Info['fname'] := fname;{ We need to set the Response.ContentLength,to tell the RtcDataServer how large the content(data) in our response will be.If we do not set the Response.ContentLength,RtcDataServer will assume that the first event whichcalls Write has prepared the complete response andwill calculate the ContentLength for us. }Response.ContentLength := File_Size(fname);Response.ContentType := aMimeTable.GetFileMIMEType(FileName);// showmessage(FileName+' ContentType='+Response.ContentType);{ We will send the Response Header out,so we don't have to call Writein case our File size is zero. }WriteHeader;Accept;end else if posEx(FileName, '/IP/TIME/QRCODE/SYSTIME/PRINT/BROAD/JOBS') > 0 then beginAccept;end else if ExtractFileExt(FileName) = '.JSON' thenAccept;end;
end;
procedure TfrMainDm.rtcGetDataReceived(Sender: TRtcConnection);
varsvr: TRtcDataServer absolute Sender;url, FileName, NamePart, extPart, OutputData: string;abar: TQRCode;i: integer;ms: TmemoryStream;sql, data, msg: string;ret, lastid: integer;li: TStringDynArray;Fsobook: string;
varfname: string;params: TRtcHttpValues;rowid, mchid: string;len: cardinal;MyConn: TMyConnection;RtcRes: TRtcValue;qry: TMyQuery;cdds: TCopyDataStruct;wnd: HWND;
beginwith svr do beginif Request.Complete then beginFileName := LowerCase(Request.FileName);NamePart := getNamePart(pchar(FileName));extPart := ExtractFileExt(FileName);if FileName = '/time' then beginWrite(DateTimeToStr(Now));end else begin // 读取文件返回客户端// Check if we have to send more data.if Response.ContentLength > Response.ContentOut then beginfname := Request.Info['fname'];.if File_Size(fname) = Response.ContentLength then beginlen := Response.ContentLength - Response.ContentOut;if len > 16000 then len := 16000;Write(Read_File(fname, Response.ContentOut, len));end else{ Disconnect the client, because our filehas changed and we have sentthe wrong header and file beginning out. }Disconnect;end;end;end; //Request.Completeend; //svr
end;
procedure TfrMainDm.rtcGetDataSent(Sender: TRtcConnection);
varfname, FileName: string;len: cardinal;
beginwith TRtcDataServer(Sender) doif Request.Complete then beginFileName := UpperCase(Request.FileName);if (FileName <> '/') 
and (pos(FileName, '/IP/TIME') > 0) then begin end else beginif Response.ContentLength > Response.ContentOut then beginfname := Request.Info['fname'];if File_Size(fname) = Response.ContentLength then beginlen := Response.ContentLength - Response.ContentOut;if len > 16000 then len := 16000;Write(Read_File(fname, Response.ContentOut, len));end elseDisconnect;end;end;end;
end;
procedure TfrMainDm.rtcPutCheckRequest(Sender: TRtcConnection);
beginwith Sender as TRtcDataServer do beginif (Request.Method = 'PUT') thenAccept;end;
end;
procedure TfrMainDm.rtcPutDataReceived(Sender: TRtcConnection);
varsvr: TRtcDataServer absolute Sender;ret, i, rowid: integer;blod, FileName, Path, FullName, tmp: ansistring;pblod: Pstring;ContentType, FileExt, queryString, username, id: string;
varMemStream: TMemoryStream;MyIStream: TStreamAdapter;imgData: ASF_ImageData;Stride: integer;ScanLines: array of Byte;
varfeaturestr: ansistring;EncodeStr: ansistring;featureSize: integer;feature1: ASF_FaceFeature; // [in] 待比较人脸特征 1feature2: ASF_FaceFeature; // [in] 待比较人脸特征 2similar, confidenceLevel: MFloat;
varMyConn: TMyConnection;qry: TMyQuery;sql, data, url: string;sj: ISuperObject;
beginwith svr do beginFileName := Utf8ToAnsi(URLDecode(svr.Request.FileName));Path := GetUrlPath(FileName); //showmessage(FilePath);FullName := rootPath + FileName;if Request.Started then beginsvr.OpenSession(); end;tmp := curPath + 'temp\' + svr.Session.ID + '.tmp';blod := Read;rtcInfo.Write_File(tmp, blod, Request.ContentIn - length(blod), rtc_ShareDenyNone);if FileName = '/register' then begini := pos('?', Request.URI); //queryString := Utf8ToAnsi(URLDecode(copy(Request.URI, i + 1, length(Request.URI) - i)));Request.Params.Text := queryString;path := Request.Params['path'];id := Request.Params['id'];username := Request.Params['name'];if id = '' then beginWrite('{"ret":-1,"errmsg":"not id"}'); exit;end;if Request.Complete then beginContentType := Request.ContentType;FileExt := aMimeTable.GetDefaultFileExt(ContentType); svr.CloseSession(svr.Session.ID);tryimagesBlod := Read_File(tmp);finallyend;tryfeatureSize := getFeature(imagesBlod, featurestr);exceptWrite('{"ret":-1,"errmsg":"error blod base64 featureSize=' + inttostr(featureSize) + ' "}'); 
//ret==0 无影响 <0 影响行数 >0 新行号exit;end;featurestr := EncodeString(featurestr);if featureSize = 0 then beginWrite('{"ret":-1,"errmsg":"not face "}'); Response.Status(200, 'OK');exit;end;FullName := rootPath + '\facelib\' + svr.Session.ID + FileExt; //保存的人脸文件Delete_File(FullName); //先删除同名文件rtcInfo.Rename_File(tmp, FullName); // 临时文件改为(移动到)文件名 MyConn := TMyConnection.Create(nil);tryMyConn.Server := fServer;MyConn.Username := fUsername;MyConn.Password := fPassword;MyConn.Port := fPort;MyConn.Options.Charset := 'utf8';MyConn.Options.UseUnicode := true;MyConn.Database := 'facelib';MyConn.Open;MyConn.StartTransaction;ret := 0;trysql := ' INSERT INTO dbfacelib(id,feature,session) '+ ' VALUES("' + id + '","' + featurestr + '","' + svr.Session.ID + '") '
+ ' ON DUPLICATE KEY UPDATE session=VALUES(session) ,feature=VALUES(feature) ;';ret := ExecutSql(MyConn, sql, 1); // // 0 --无影响,-影响行数 ,+ 插入后行号if ret > 0 then beginsql := ' INSERT INTO dbuserlib(facerowid,name,id,url) '+ ' VALUES(' + inttostr(ret) + ',"' + username + '","' + id + '","' + '/facelib/' + 
svr.Session.ID + FileExt + '" ) '+ ' ON DUPLICATE KEY UPDATE facerowid=VALUES(facerowid),url=VALUES(url);';ret := ExecutSql(MyConn, sql); end;MyConn.Commit;Write('{"ret":' + inttostr(abs(ret)) + '}');excepton E: Exception do beginWrite('{"ret":-1}'); //0--无影响 - 影响行数 + 返回插入行号MyConn.RollBack;raise Exception.Create(E.Message);end;end;finallyMyConn.Free;end;Response.Status(200, 'OK');Write('');end; // Request.Completeend else if FileName = '/search' then beginif Request.Complete then beginContentType := Request.ContentType;FileExt := aMimeTable.GetDefaultFileExt(ContentType);svr.CloseSession(svr.Session.ID);tryimagesBlod := Read_File(tmp);finallyend;tryfeatureSize := getFeature(imagesBlod, featurestr);exceptWrite('{"ret":-1,"errmsg":"error imges blod"}'); //ret==0 无影响 <0 影响行数 >0 新
行号exit;end;// featurestr := EncodeString(featurestr); //不要压缩Delete_File(tmp);if featureSize = 0 then beginWrite('{"ret":-1,"errmsg":"not face "}'); //ret==0 无影响 <0 影响行数 >0 新行号Response.Status(200, 'OK');exit;end;MyConn := TMyConnection.Create(nil);qry := TMyQuery.Create(nil);feature1.featureSize := 1032;GetMem(feature1.feature, 1033);CopyMemory(feature1.feature, pchar(featurestr), 1032);feature2.featureSize := 1032;GetMem(Feature2.feature, 1033);tryMyConn.Server := fServer;MyConn.Username := fUsername;MyConn.Password := fPassword;MyConn.Port := fPort;MyConn.Options.Charset := 'utf8';MyConn.Options.UseUnicode := true;MyConn.Database := 'facelib';MyConn.Open;MyConn.StartTransaction;qry.Connection := MyConn;sql := ' select row_id,feature from dbfacelib; ';qry.Close;qry.SQL.Text := string(sql);qry.Open;qry.DisableControls;qry.First;rowid := 0; similar := 0;while not qry.Eof do begindata := qry.FieldByName('feature').AsString;data := DecodeString(data); CopyMemory(Feature2.feature, pchar(data), 1032);ret := ASFFaceFeatureCompare(FFACEHandle, feature1, Feature2, confidenceLevel, 
ASF_LIFE_PHOTO);if (ret = 0) and (confidenceLevel > 0.75) then beginsimilar := confidenceLevel;rowid := qry.FieldByName('row_id').AsInteger;if (similar > 0.8) then Break;end; qry.Next;end;if (rowid > 0) then begin
sql := ' SELECT row_id, id,url,name,sex, identificatin,address,marriage,
TIMESTAMPDIFF(YEAR, birthday, CURDATE())as age '+' FROM dbuserlib WHERE facerowid =' + inttostr(rowid);qry.Close;qry.SQL.Text := string(sql);qry.Open;if qry.RecordCount = 1 then beginid := qry.FieldByName('id').AsString;url := qry.FieldByName('url').AsString;sj := JSonFromDataSet(qry);sj.AsArray[0].D['similar'] := similar;sj.AsArray[0].i['ret'] := 0;Write(sj.AsArray[0].AsString);end elseWrite('{"ret":-1,"errmsg":"face not register ,not find in Records "} ');end else beginWrite('{"ret":-1,"errmsg":"face not register,not find in Records "} ');end;qry.EnableControls;MyConn.Commit;finally FreeMem(feature2.feature);FreeMem(feature1.feature);qry.Free;MyConn.Free;end;Response.Status(200, 'OK');end;end else beginif Request.Complete then beginsvr.CloseSession(svr.Session.ID);ContentType := Request.ContentType;FileExt := aMimeTable.GetDefaultFileExt(ContentType);FullName := rootPath + '\files\' + FileName;ChangeFileExt(FullName, FileExt);Delete_File(FullName); //先删除同名文件rtcInfo.Rename_File(tmp, FullName); Response.Status(200, 'OK'); // Set response status to a standard "OK"Write('"ret":-1,"msg":"' + ansitoUtf8(FileName + '上传成功.') + '"'); end;end;end;
end;

上述代码省掉非必要部分,但已可实现众人脸库功能了,而且性能还是不错的,运行非常稳定,但由于虹软人脸串比较函数没有独立出来,要与引擎放在同一库文件,而且必须引用引擎指针,因此无法把人脸比较功能做成 MYSQLUDF 自定义函数, 人脸比较速度不快,在 2000 人脸数时,搜索比较人脸可以在 500 毫秒返回,10 个并发可以使用,接近百度人脸库速度,但人脸数比较多时,性能下降,比如 10000人脸,返回时间达 6 秒,2 个并发时返回时间要 10 多秒无法使用。但局域网内人脸库本来适用人数就不多,如果可改成多个线程并行比较,可提高查询比较速度。

为了减少编幅,没有提供服务程序部分代码,所以程序不能做服务启动,只能手动启动,如果必要的话,自己再添加此功能。

三、人脸库服务器的安装打包
人脸库服务器就此完成,使用方法,先启动 mysql 服务,再启动. 可以先测试一下,在浏览器中打入 http://127.0.0.1:8080/可登录进入测试页面,先注册上传一些人脸,再上传人脸搜索对比,转出相似度 80%匹配的人脸信息如下。在这里插入图片描述
如果人脸库服务器安装在与测试不同的电脑,应该防火墙设置 8080 端口例外通过程序的打包比较复杂,方法如下:
1 打包 MYSQL 并设置为服务启动。
2 打包 facesrvr 并设置为服务启动
3 安装完毕,设置防火墙端口例外通过

完整演示下载地址:
https://icloud.cdn.bcebos.com/%E8%99%B9%E8%BD%AF%E4%BA%BA%E8%84%B8%E5%BA%93%E6%9C%8D%E5%8A%A1%E5%99%A8.exe

注意要使用到虹软开发平台 appIdsdkKey 在安装目录编辑 facesrvr.ini 文件,设置

[arcface]
appId= 你的 appId
sdkKey= 你的 sdkKey

源码下载:
链接: https://pan.baidu.com/s/1PvCgEvfE8CltFhXuzmwVHA
提取码: mfqn

了解更多人脸识别产品相关内容请到虹软视觉开放平台哦


http://chatgpt.dhexx.cn/article/kYsN1uYh.shtml

相关文章

如何使用百度云人脸识别服务(V3版接口python语言) (九)批量向人脸库中添加人脸

千呼万唤始出来,终于要开始操作人脸库了 今天就先说说如何向人脸库中批量添加人脸吧(这也是应评论的要求) 首先若是想要批量向人脸库中添加人脸,我们先得要有足够量的人脸啊啊啊啊啊 本人作为一名高中生极度无聊的弄到了我学校所有学生的学业水平考试照片,作为测试(本…

怎么把百度云人脸识别添加到百度云人脸库中

1.首先注册一个百度云账号,然后在产品服务,人工智能里找到人脸识别,创建一个应用,在应用里创建一个名为人脸的群组 2.在index控制器中写两个方法 public function face_group(){$groupname=face;$client=$this->init_face();$ret=$client->getGroupList();if ($ret[…

人脸库对比(百度人脸识别)(Java版)

系列文章&#xff1a; 一、JavaFX摄像&#xff1a;https://blog.csdn.net/haoranhaoshi/article/details/85880893 二、JavaFX拍照&#xff1a;https://blog.csdn.net/haoranhaoshi/article/details/85930981 三、百度人脸识别&#xff0d;&#xff0d;人脸对比&am…

人脸识别接口_DS-K5603-Z 海康威视人员通道人脸识别组件 1万人脸库 10.1英寸LCD触摸显示屏_DS-K5603-Z_DS-K5603-Z...

DS-K5603-Z 海康威视人员通道人脸识别组件 英寸LCD触摸显示屏 支持10000人脸库 支持人脸识别、刷卡或相互组合的识别方式,1:1比对时间≤1S/人,1:N比对时间≤人 DS-K5603-Z DS-K5603-Z海康人脸识别组件海康人脸识别组件 DS-K5603-Z 产品简介 DS-K5603-Z人脸识别组件是一款高…

python+opencv人脸识别(用耶鲁大学的Yale人脸库训练cnn)3

用耶鲁大学的Yale人脸库&#xff0c;里面包含15个人&#xff0c;每人11张照片&#xff0c;主要包括光照条件的变化&#xff0c;表情的变化&#xff0c;接下来我会把自己的几张照片混进去&#xff0c;看看训练过后能不能被神经网络良好的识别。https://blog.csdn.net/weixin_393…

百度智能云人脸库的创建与使用

搜索百度智能云人脸识别云服务 点立即使用,再登百度之类的账号 创建应用 新建组 添加图片

阿里云实现人脸登录(人脸库 OSS)

我自认为不想做curd程序员&#xff0c;但是免不了的会对数据基本原子操作进行处理&#xff0c;项目开发过程中的增删改查少不了的&#xff0c;但是又不甘心于curd下去&#xff0c;所以想要在掌握现有知识的基础上&#xff0c;甚至逼迫自己去学习一些东西&#xff0c;去接触新的…

毕业设计 - 基于JAVA人脸识别管理系统(人脸搜索与人脸库管理)

文章目录 【背景/简介】【技术框架】【核心开发】【功能展示】一、人脸库管理二、人脸识别记录管理 【核心代码】【总结】 基于JAVA的人脸识别管理系统作品分享一下&#xff0c;希望能帮助到有需要的同学们。 【背景/简介】 人脸搜索与人脸库管理主要用在人脸通用场景&#xf…

定位基本方法 3

节点定位方法 3. 基于移动 BS 的定位 在基于静态信标节点的定位方法中&#xff0c;定位系统的定位精度与静态信标节点的部署密度和质量直接相关。为了获得好的定位精度&#xff0c;就需要在部署区域中放置大量的静态信标节点&#xff0c;这势必会导致定位成本的大幅上升。为此…

定位基本方法 1

节点定位方法 1. 节点定位的计算 在 WSNs 的定位中&#xff0c;未知节点通过一定的技术和方法能够获得定位自身所需的坐标、角度或距离信息&#xff0c;从而利用节点位置的计算方法计算自身位置。下面我们将介绍几种较为典型的位置计算方法&#xff1a;三边测量法、三角测量法…

浅谈自适应滤波器---(快速RLS算法)

在上一篇博客中&#xff08;浅谈自适应滤波器&#xff09;我给大家介绍了关于自适应滤波器的一些入门级的知识&#xff0c;并分析了常规RLS算法单次迭代的计算量级为O[N2]&#xff0c;当阶数N增大时相应的计算量显著增大&#xff0c;为了将计算量级降低到O[N]&#xff0c;人们提…

图像处理自适应滤波

图像处理基础(2)&#xff1a;自适应中值滤波器(基于OpenCV实现) 标签&#xff1a; opencv滤波器 2017-02-08 19:44 986人阅读 评论(0) 收藏 举报 分类&#xff1a; DIP&#xff08;8&#xff09; 版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载…

自适应滤波器及其应用 - 自适应噪声抵消器

传统IIR和FIR滤波器在处理输入信号的过程中滤波器参数固定&#xff0c;当环境发生变化时&#xff0c;滤波器无法实现原先设定的目标。自适应滤波器根据当前自身的状态和环境调整滤波器权系数。 1 自适应滤波器理论 其中&#xff0c;x(n)是输入信号&#xff0c;y(n)是输出信号&…

自适应数字滤波器

文章目录 前言一、自适应滤波器的特点和构成二、最陡下降法三、最小均方算法(LMS)总结 前言 本文的主要内容是自适应数字滤波器的介绍&#xff0c;包含其特点与构成、最陡下降法、最小均方算法以及最小二乘算法等内容。 一、自适应滤波器的特点和构成 自适应滤波器的特点&…

浅谈自适应滤波器---(自适应陷波器)

陷波器顾名思义就是对特定频率的信号有着很强的衰减的滤波器&#xff0c;也即阻带带宽极窄的带阻滤波器。在传统的数字陷波器设计中&#xff0c;为了能使某一频率信号得到足够大的衰减&#xff0c;通常的做法就是把阶数选的足够高来达到很大的衰减&#xff1b;但同时计算量也变…

自适应滤波器及LMS自适应算法的理解

分享一篇以前写现代信号处理的课程论文。 ————————————————————

自适应中值滤波器和自适应局部(均值)滤波器的设计 python+matlab各实现

要求是&#xff1a;自适应中值滤波器和自适应均值滤波器的设计&#xff0c;分别使用python和matlab去实现 一.原理 1.自适应中值滤波器 2.自适应局部滤波器&#xff0c;也叫自适应均值滤波器 二.设计流程 1.自适应中值滤波器 ①生成椒盐噪声 利用rand()函数生成[0,1]的随…

自适应滤波去噪

自适应滤波器具有在未知环境下良好的运作并跟踪输入统计量随时间变化的能力。尽管对于不同的应用有不同的实现结构&#xff0c;但是他们都有一个基本的特征&#xff1a;输入向量X(n)和期望响应d(n)被用来计算估计误差e(n)&#xff0c;即e(n)d(n)-X(n)&#xff0c;并利用此误差信…

自适应滤波(LMS,RLS)

1.背景及相关知识介绍 自适应滤波存在于信号处理、控制、图像处理等许多不同领域&#xff0c;它是一种智能更有针对性的滤波方法&#xff0c;通常用于去噪。 图中x&#xff08;j&#xff09;表示 j 时刻的输入信号值&#xff0c;y&#xff08;j&#xff09;表示 j 时刻的输出信…

自适应滤波

自适应阵列处理是一种空间滤波技术&#xff0c;它包含空间阵列和自通应处理两个部分。根据空时等效性原理&#xff0c;从理论上来讲&#xff0c;时域的各种统计自适应信号处理技术均可应用于空域的自适应阵列处理 自适应滤波已在时域处理中广为应用&#xff0c;其实现可以来用…