如何使用 python 接入虹软 ArcFace SDK
原标题:如何使用 python 接入虹软 ArcFace SDK
原文来自:博客园 原文链接:https://www.cnblogs.com/jdjd123/p/11510224.html
公司需要在项目中使用人脸识别SDK,并且对信息安全的要求非常高,在详细了解市场上几个主流人脸识别SDK后,综合来看虹软的Arcface SDK比较符合我们的需求,它提供了免费版本,并且可以在离线环境下使用,这一点非常符合我们对安全性的要求。但有个遗憾的事情,我们的项目主要使用了Python语言,虹软官方并没有提供Python版本的SDK,因此我自己使用Python封装了Arcface C++ SDK,便于在项目中使用,这里将主要过程写出来供大家探讨下。
1.环境说明
a.注意Win64环境的Python必须使用ArcFace C++(Win64) SDK,如果平台不一致, 否则可能会出现以下错误。
1 | OSError: [WinError 193 ] % 1 不是有效的 Win32 应用程序 |
b.由于SDK中涉及到内存操作,本文使用了ctypes包和cdll包提供的以下几种方式
1 2 3 4 5 | c_ubyte_p = POINTER(c_ubyte) memcpy = cdll.msvcrt.memcpy malloc = cdll.msvcrt.malloc malloc.restype = c_void_p free = cdll.msvcrt.free |
2.Arcface SDK基本数据结构封装
在封装数据结构时,一定要注意参数类型,否则可能会导致程序出错。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | class MRECT(Structure): # 人脸框 _fields_ = [(u 'left' , c_int32), (u 'top' , c_int32), (u 'right' , c_int32), (u 'bottom' , c_int32)] class ASFVersion(Structure): # 版本信息 版本号 构建日期 版权说明 _fields_ = [ ( 'Version' , c_char_p), ( 'BuildDate' , c_char_p), ( 'CopyRight' , c_char_p)] class ASFSingleFaceInfo(Structure): # 单人脸信息 人脸框 人脸角度 _fields_ = [ ( 'faceRect' , MRECT), ( 'faceOrient' , c_int32)] class ASFMultiFaceInfo(Structure): # 多人脸信息 人脸框数组 人脸角度数组 人脸数 _fields_ = [ (u 'faceRect' , POINTER(MRECT)), (u 'faceOrient' , POINTER(c_int32)), (u 'faceNum' , c_int32)] class ASFFaceFeature(Structure): # 人脸特征 人脸特征 人脸特征长度 _fields_ = [ ( 'feature' , c_void_p), ( 'featureSize' , c_int32)] class ASFFace3DAngle(Structure): # 人脸角度信息 _fields_ = [ ( 'roll' , c_void_p), ( 'yaw' , c_void_p), ( 'pitch' , c_void_p), ( 'status' , c_void_p), ( 'num' , c_int32)] class ASFAgeInfo(Structure): # 年龄 _fields_ = [ (u 'ageArray' , c_void_p), (u 'num' , c_int32)] class ASFGenderInfo(Structure): # 性别 _fields_ = [ (u 'genderArray' , c_void_p), (u 'num' , c_int32)] class ASFLivenessThreshold(Structure): # 活体阈值 _fields_ = [ (u 'thresholdmodel_BGR' , c_float), (u 'thresholdmodel_IR' , c_int32)] class ASFLivenessInfo(Structure): # 活体信息 _fields_ = [ (u 'isLive' , c_void_p), (u 'num' , c_int32)] |
3.Arcface SDK接口封装
a.接口封装之前需要加载dll库,Arcface SDK 提供的dll都需要加载。
b.本文中图片格式使用了ASVL_PAF_RGB24_B8G8R8。
c.每个接口都需要定义返回值以及参数类型,某些参数类型依赖前文所述的基本数据结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | from arcsoft_face_struct import * from ctypes import * from enum import Enum face_dll = CDLL( "libarcsoft_face.dll" ) face_engine_dll = CDLL( "libarcsoft_face_engine.dll" ) ASF_DETECT_MODE_VIDEO = 0x00000000 ASF_DETECT_MODE_IMAGE = 0xFFFFFFFF ASF_NONE = 0x00000000 ASF_FACE_DETECT = 0x00000001 ASF_FACE_RECOGNITION = 0x00000004 ASF_AGE = 0x00000008 ASF_GENDER = 0x00000010 ASF_FACE3DANGLE = 0x00000020 ASF_LIVENESS = 0x00000080 ASF_IR_LIVENESS = 0x00000400 ASVL_PAF_RGB24_B8G8R8 = 0x201 class ArcSoftFaceOrientPriority(Enum): ASF_OP_0_ONLY = 0x1 , ASF_OP_90_ONLY = 0x2 , ASF_OP_270_ONLY = 0x3 , ASF_OP_180_ONLY = 0x4 , ASF_OP_0_HIGHER_EXT = 0x5 , activate = face_engine_dll.ASFActivation activate.restype = c_int32 activate.argtypes = (c_char_p, c_char_p) init_engine = face_engine_dll.ASFInitEngine init_engine.restype = c_int32 init_engine.argtypes = (c_long, c_int32, c_int32, c_int32, c_int32, POINTER(c_void_p)) detect_face = face_engine_dll.ASFDetectFaces detect_face.restype = c_int32 detect_face.argtypes = (c_void_p, c_int32, c_int32, c_int32, POINTER(c_ubyte), POINTER(ASFMultiFaceInfo)) extract_feature = face_engine_dll.ASFFaceFeatureExtract extract_feature.restype = c_int32 extract_feature.argtypes = (c_void_p, c_int32, c_int32, c_int32, POINTER(c_ubyte), POINTER(ASFSingleFaceInfo), POINTER(ASFFaceFeature)) compare_feature = face_engine_dll.ASFFaceFeatureCompare compare_feature.restype = c_int32 compare_feature.argtypes = (c_void_p, POINTER(ASFFaceFeature), POINTER(ASFFaceFeature), POINTER(c_float)) set_liveness_param = face_engine_dll.ASFSetLivenessParam set_liveness_param.restype = c_int32 set_liveness_param.argtypes = (c_void_p, POINTER(ASFLivenessThreshold)) process = face_engine_dll.ASFProcess process.restype = c_int32 process.argtypes = (c_void_p, c_int32, c_int32, c_int32, POINTER(c_ubyte), POINTER(ASFMultiFaceInfo), c_int32) get_age = face_engine_dll.ASFGetAge get_age.restype = c_int32 get_age.argtypes = (c_void_p, POINTER(ASFAgeInfo)) get_gender = face_engine_dll.ASFGetGender get_gender.restype = c_int32 get_gender.argtypes = (c_void_p, POINTER(ASFGenderInfo)) get_3d_angle = face_engine_dll.ASFGetFace3DAngle get_3d_angle.restype = c_int32 get_3d_angle.argtypes = (c_void_p, POINTER(ASFFace3DAngle)) get_liveness_info = face_engine_dll.ASFGetLivenessScore get_liveness_info.restype = c_int32 get_liveness_info.argtypes = (c_void_p, POINTER(ASFLivenessInfo)) |
4.封装接口调用
接下来按照下面的流程图介绍接口调用(此图使用 Microsoft Visio 2016自动生成)。
下图是按照此流程处理得到的效果图,由于画面有限,只显示了年龄、性别、活体信息。
a.激活
需要注意app_id和sdk_key需要使用字节类型。
1 2 3 4 5 6 7 | app_id = b"" sdk_key = b"" ret = arcsoft_face_func.activate(app_id, sdk_key) # 激活 if ret = = 0 or ret = = 90114 : print ( "激活成功" ) else : print ( "激活失败:" , ret) |
b.初始化
初始化需要将所有需要的功能参数一次性传入,本文使用了人脸检测、特征提取等功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | mask = arcsoft_face_func.ASF_FACE_DETECT | arcsoft_face_func.ASF_FACE_RECOGNITION | arcsoft_face_func.ASF_AGE | arcsoft_face_func.ASF_GENDER | arcsoft_face_func.ASF_FACE3DANGLE | arcsoft_face_func.ASF_LIVENESS engine = c_void_p() ret = arcsoft_face_func.init_engine(arcsoft_face_func.ASF_DETECT_MODE_IMAGE, arcsoft_face_func.ArcSoftFaceOrientPriority.ASF_OP_0_ONLY.value[ 0 ], 30 , 10 , mask, byref(engine)) if ret = = 0 : print ( "初始化成功" ) else : print ( "初始化失败:" , ret) |
c.人脸检测
本文使用了opencv读图,兼容性更好,并且自定义的数据结构记录图片信息,注意 ArcFace C++ SDK 要求传入的图像宽度需要是4的倍数,下面做了裁剪。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | class Image: def __init__( self ): self .width = 0 self .height = 0 self .imageData = None def load_image(file_path): img = cv2.imread(file_path) sp = img.shape img = cv2.resize(img, (sp[ 1 ] / / 4 * 4 , sp[ 0 ])) # 四字节对齐 image = Image() image.width = img.shape[ 1 ] image.height = img.shape[ 0 ] image.imageData = img return image ###################### 人脸检测 ################################## image1 = load_image(r "1.jpg" ) image_bytes = bytes(image1.imageData) image_ubytes = cast(image_bytes, c_ubyte_p) detect_faces = ASFMultiFaceInfo() ret = arcsoft_face_func.detect_face( engine, image1.width, image1.height, arcsoft_face_func.ASVL_PAF_RGB24_B8G8R8, image_ubytes, byref(detect_faces) ) if ret = = 0 : print ( "检测人脸成功" ) else : print ( "检测人脸失败:" , ret) |
d.特征提取
特征提取只支持单人脸,因此做了人脸处理操作,并且需要及时将提取的人脸特征拷贝一份,否则会被覆盖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | single_face1 = ASFSingleFaceInfo() single_face1.faceRect = detect_faces.faceRect[ 0 ] single_face1.faceOrient = detect_faces.faceOrient[ 0 ] face_feature = ASFFaceFeature() ret = arcsoft_face_func.extract_feature( engine, image1.width, image1.height, arcsoft_face_func.ASVL_PAF_RGB24_B8G8R8, image_ubytes, single_face1, byref(face_feature) ) if ret = = 0 : print ( "提取特征1成功" ) else : print ( "提取特征1失败:" , ret) feature1 = ASFFaceFeature() feature1.featureSize = face_feature.featureSize feature1.feature = malloc(feature1.featureSize) memcpy(c_void_p(feature1.feature), c_void_p(face_feature.feature), feature1.featureSize) |
e.特征比对
按照前文所述再提取一张人脸的特征,即可以进行下面的人脸特征比对操作
1 2 3 4 5 6 7 8 9 10 11 12 | compare_threshold = c_float() ret = arcsoft_face_func.compare_feature( engine, feature1, feature2, compare_threshold ) free(c_void_p(feature1.feature)) free(c_void_p(feature2.feature)) if ret = = 0 : print ( "特征比对成功,相似度:" , compare_threshold.value) else : print ( "特征比对失败:" , ret) |
f.年龄、性别、3D Angle
process接口目前提供了 年龄、性别、3D Angle、活体检测, 但年龄、性别、3D Angle支持多人脸,而活体只支持单人脸,因此下面分别处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | process_mask = arcsoft_face_func.ASF_AGE | arcsoft_face_func.ASF_GENDER | arcsoft_face_func.ASF_FACE3DANGLE ret = arcsoft_face_func.process( engine, image1.width, image1.height, arcsoft_face_func.ASVL_PAF_RGB24_B8G8R8, image_ubytes, byref(detect_faces), c_int32(process_mask) ) if ret = = 0 : print ( "process成功" ) else : print ( "process失败:" , ret) ######################## Age ################################ age_info = ASFAgeInfo() ret = arcsoft_face_func.get_age(engine, byref(age_info)) if ret = = 0 : print ( "get_age 成功" ) age_ptr = cast(age_info.ageArray, POINTER(c_int)) for i in range (age_info.num): print ( "face" , i, "age:" , age_ptr[i]) else : print ( "get_age 失败:" , ret) ####################### Gender ################################# gender_info = ASFGenderInfo() ret = arcsoft_face_func.get_gender(engine, byref(gender_info)) if ret = = 0 : print ( "get_gender 成功" ) gender_ptr = cast(gender_info.genderArray, POINTER(c_int)) for i in range (gender_info.num): print ( "face" , i, "gender:" , "女性" if (gender_ptr[i] = = 1 ) else ( "男性" if (gender_ptr[i] = = 0 ) else "未知" )) else : print ( "get_gender 失败:" , ret) ####################### 3D Angle ################################# angle_info = ASFFace3DAngle() ret = arcsoft_face_func.get_3d_angle(engine, byref(angle_info)) if ret = = 0 : print ( "get_3d_angle 成功" ) roll_ptr = cast(angle_info.roll, POINTER(c_float)) yaw_ptr = cast(angle_info.yaw, POINTER(c_float)) pitch_ptr = cast(angle_info.pitch, POINTER(c_float)) status_ptr = cast(angle_info.status, POINTER(c_int32)) for i in range (angle_info.num): print ( "face" , i, "roll:" , roll_ptr[i], "yaw:" , yaw_ptr[i], "pitch:" , pitch_ptr[i], "status:" , "正常" if status_ptr[i] = = 0 else "出错" ) else : print ( "get_3d_angle 失败:" , ret) |
g.RGB活体
在活体检测之前建议按照实际场景设置活体阈值,不设置即使用默认阈值,这里设置了RGB活体的阈值为0.75。并将检测的多人脸分别转为单张人脸的参数传到接口中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | ######################### 活体阈值设置 ############################### threshold_param = ASFLivenessThreshold() threshold_param.thresholdmodel_BGR = 0.75 ret = arcsoft_face_func.set_liveness_param(engine,threshold_param) if ret = = 0 : print ( "set_liveness_param成功" ) else : print ( "set_liveness_param 失败:" , ret) temp_face_info = ASFMultiFaceInfo() temp_face_info.faceNum = 1 LP_MRECT = POINTER(MRECT) temp_face_info.faceRect = LP_MRECT(MRECT(malloc(sizeof(MRECT)))) LP_c_long = POINTER(c_long) temp_face_info.faceOrient = LP_c_long(c_long(malloc(sizeof(c_long)))) for i in range (detect_faces.faceNum): temp_face_info.faceRect[ 0 ] = detect_faces.faceRect[i] temp_face_info.faceOrient[ 0 ] = detect_faces.faceOrient[i] ret = arcsoft_face_func.process( engine, image1.width, image1.height, arcsoft_face_func.ASVL_PAF_RGB24_B8G8R8, image_ubytes, byref(temp_face_info), c_int32(arcsoft_face_func.ASF_LIVENESS) ) if ret = = 0 : print ( "process成功" ) else : print ( "process失败:" , ret) ## RGB活体检测 ret = arcsoft_face_func.process( engine, image1.width, image1.height, arcsoft_face_func.ASVL_PAF_RGB24_B8G8R8, image_ubytes, byref(temp_face_info), c_int32(arcsoft_face_func.ASF_LIVENESS) ) if ret = = 0 : print ( "process成功" ) else : print ( "process失败:" , ret) liveness_info = ASFLivenessInfo() ret = arcsoft_face_func.get_liveness_info(engine, byref(liveness_info)) if ret = = 0 : print ( "get_liveness_info 成功" ) liveness_ptr = cast(liveness_info.isLive, POINTER(c_int)) print ( "face" , i, "liveness:" , "非真人" if (liveness_ptr[ 0 ] = = 0 ) else ( "真人" if (liveness_ptr[ 0 ] = = 1 ) else ( "不确定" if (liveness_ptr[ 0 ] = = - 1 ) else ( "传入人脸数>1" if (liveness_ptr[ 0 ] = = - 2 ) else (liveness_ptr[ 0 ]) ) ) )) else : print ( "get_liveness_info 失败:" , ret) |
写在最后,欢迎大家交流指正,若有需要下载其他语言的SDK或demo,可至其官网下载~https://ai.arcsoft.com.cn/product/arcface.html?utm_source=cnblogs&utm_medium=referral
免责声明:本文来自互联网新闻客户端自媒体,不代表本网的观点和立场。
合作及投稿邮箱:E-mail:editor@tusaishared.com
下一篇:什么叫训练模型?
热门资源
Python 爬虫(二)...
所谓爬虫就是模拟客户端发送网络请求,获取网络响...
TensorFlow从1到2...
原文第四篇中,我们介绍了官方的入门案例MNIST,功...
TensorFlow从1到2...
“回归”这个词,既是Regression算法的名称,也代表...
机器学习中的熵、...
熵 (entropy) 这一词最初来源于热力学。1948年,克...
TensorFlow2.0(10...
前面的博客中我们说过,在加载数据和预处理数据时...
智能在线
400-630-6780
聆听.建议反馈
E-mail: support@tusaishared.com