公司需要在项目中使用人脸识别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. 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. class MRECT(Structure): # 人脸框
  2.     _fields_ = [(u'left', c_int32),
  3.            (u'top', c_int32),
  4.            (u'right', c_int32),
  5.            (u'bottom', c_int32)] class ASFVersion(Structure): # 版本信息 版本号 构建日期 版权说明
  6.     _fields_ = [ ('Version', c_char_p),
  7.            ('BuildDate', c_char_p),
  8.            ('CopyRight', c_char_p)] class ASFSingleFaceInfo(Structure): # 单人脸信息 人脸框 人脸角度
  9.    _fields_ = [ ('faceRect', MRECT),
  10.           ('faceOrient', c_int32)] class ASFMultiFaceInfo(Structure): # 多人脸信息 人脸框数组 人脸角度数组 人脸数
  11.     _fields_ = [ (u'faceRect', POINTER(MRECT)),
  12.            (u'faceOrient', POINTER(c_int32)),
  13.            (u'faceNum', c_int32)] class ASFFaceFeature(Structure): # 人脸特征 人脸特征 人脸特征长度
  14.     _fields_ = [ ('feature', c_void_p),
  15.            ('featureSize', c_int32)] class ASFFace3DAngle(Structure): # 人脸角度信息
  16.     _fields_ = [ ('roll', c_void_p),
  17.            ('yaw', c_void_p),
  18.            ('pitch', c_void_p),
  19.            ('status', c_void_p),
  20.            ('num', c_int32)] class ASFAgeInfo(Structure): # 年龄
  21.     _fields_ = [ (u'ageArray', c_void_p),
  22.            (u'num', c_int32)] class ASFGenderInfo(Structure): # 性别
  23.     _fields_ = [ (u'genderArray', c_void_p),
  24.            (u'num', c_int32)] class ASFLivenessThreshold(Structure): # 活体阈值
  25.     _fields_ = [ (u'thresholdmodel_BGR', c_float),
  26.            (u'thresholdmodel_IR', c_int32)] class ASFLivenessInfo(Structure): # 活体信息
  27.     _fields_ = [ (u'isLive', c_void_p),
  28.            (u'num', c_int32)]
3.Arcface SDK接口封装

a. 接口封装之前需要加载dll库,Arcface SDK 提供的dll都需要加载。 

b. 本文中图片格式使用了ASVL_PAF_RGB24_B8G8R8。 

c. 每个接口都需要定义返回值以及参数类型,某些参数类型依赖前文所述的基本数据结构。 

点击(此处)折叠或打开

  1. from arcsoft_face_struct import * from ctypes import * from enum import Enum

  2. face_dll = CDLL("libarcsoft_face.dll")
  3. face_engine_dll = CDLL("libarcsoft_face_engine.dll")

  4. ASF_DETECT_MODE_VIDEO = 0x00000000
  5. ASF_DETECT_MODE_IMAGE = 0xFFFFFFFF ASF_NONE = 0x00000000
  6. ASF_FACE_DETECT = 0x00000001 ASF_FACE_RECOGNITION = 0x00000004
  7. ASF_AGE = 0x00000008
  8. ASF_GENDER = 0x00000010
  9. ASF_FACE3DANGLE = 0x00000020
  10. ASF_LIVENESS = 0x00000080
  11. ASF_IR_LIVENESS = 0x00000400
  12. ASVL_PAF_RGB24_B8G8R8 = 0x201

  13. class ArcSoftFaceOrientPriority(Enum):
  14.     ASF_OP_0_ONLY = 0x1,
  15.     ASF_OP_90_ONLY = 0x2,
  16.     ASF_OP_270_ONLY = 0x3,
  17.     ASF_OP_180_ONLY = 0x4,
  18.     ASF_OP_0_HIGHER_EXT = 0x5,

  19. activate = face_engine_dll.ASFActivation
  20. activate.restype = c_int32
  21. activate.argtypes = (c_char_p, c_char_p)

  22. init_engine = face_engine_dll.ASFInitEngine
  23. init_engine.restype = c_int32
  24. init_engine.argtypes = (c_long, c_int32, c_int32, c_int32, c_int32, POINTER(c_void_p))

  25. detect_face = face_engine_dll.ASFDetectFaces
  26. detect_face.restype = c_int32
  27. detect_face.argtypes = (c_void_p, c_int32, c_int32, c_int32, POINTER(c_ubyte), POINTER(ASFMultiFaceInfo))

  28. extract_feature = face_engine_dll.ASFFaceFeatureExtract
  29. extract_feature.restype = c_int32
  30. extract_feature.argtypes = (c_void_p, c_int32, c_int32, c_int32, POINTER(c_ubyte),
  31.                             POINTER(ASFSingleFaceInfo), POINTER(ASFFaceFeature))

  32. compare_feature = face_engine_dll.ASFFaceFeature
  33. Compare compare_feature.restype = c_int32
  34. compare_feature.argtypes = (c_void_p, POINTER(ASFFaceFeature), POINTER(ASFFaceFeature), POINTER(c_float))

  35. set_liveness_param = face_engine_dll.ASFSetLivenessParam
  36. set_liveness_param.restype = c_int32
  37. set_liveness_param.argtypes = (c_void_p, POINTER(ASFLivenessThreshold))

  38. process = face_engine_dll.ASFProcess process.restype =
  39. c_int32 process.argtypes = (c_void_p, c_int32, c_int32, c_int32, POINTER(c_ubyte),
  40.                             POINTER(ASFMultiFaceInfo), c_int32)

  41. get_age = face_engine_dll.ASFGetAge
  42. get_age.restype = c_int32
  43. get_age.argtypes = (c_void_p, POINTER(ASFAgeInfo))

  44. get_gender = face_engine_dll.ASFGetGender
  45. get_gender.restype = c_int32
  46. get_gender.argtypes = (c_void_p, POINTER(ASFGenderInfo))

  47. get_3d_angle = face_engine_dll.ASFGetFace3DAngle
  48. get_3d_angle.restype = c_int32
  49. get_3d_angle.argtypes = (c_void_p, POINTER(ASFFace3DAngle))

  50. get_liveness_info = face_engine_dll.ASFGetLivenessScore
  51. get_liveness_info.restype = c_int32
  52. get_liveness_info.argtypes = (c_void_p, POINTER(ASFLivenessInfo))
4.封装接口调用 

接下来按照下面的流程图介绍接口调用(此图使用 Microsoft Visio 2016自动生成)。
如何使用 python 接入虹软 ArcFace SDK-LMLPHP
下图是按照此流程处理得到的效果图,由于画面有限,只显示了年龄、性别、活体信息。
如何使用 python 接入虹软 ArcFace SDK-LMLPHP

a. 激活 

需要注意app_id和sdk_key需要使用字节类型。

点击(此处)折叠或打开

  1. app_id = b""
  2. sdk_key = b""
  3. ret = arcsoft_face_func.activate(app_id, sdk_key) # 激活
  4. if ret == 0 or ret == 90114:
  5.     print("激活成功")
  6. else:
  7.     print("激活失败:", ret)

b. 初始化 

 初始化需要将所有需要的功能参数一次性传入,本文使用了人脸检测、特征提取等功能。

点击(此处)折叠或打开

  1. mask = arcsoft_face_func.ASF_FACE_DETECT | \
  2.             arcsoft_face_func.ASF_FACE_RECOGNITION | \
  3.             arcsoft_face_func.ASF_AGE | \
  4.             arcsoft_face_func.ASF_GENDER | \
  5.             arcsoft_face_func.ASF_FACE3DANGLE |\
  6.             arcsoft_face_func.ASF_LIVENESS

  7.     engine = c_void_p()
  8.     ret = arcsoft_face_func.init_engine(arcsoft_face_func.ASF_DETECT_MODE_IMAGE,
  9.                                         arcsoft_face_func.ArcSoftFaceOrientPriority.ASF_OP_0_ONLY.value[0],
  10.                                    30, 10, mask, byref(engine))
  11.     if ret == 0:
  12.         print("初始化成功")
  13.     else:
  14.         print("初始化失败:", ret)

c. 人脸检测 

本文使用了opencv读图,兼容性更好,并且自定义的数据结构记录图片信息,注意 ArcFace C++ SDK 要求传入的图像宽度需要是4的倍数,下面做了裁剪。

点击(此处)折叠或打开

  1. class Image:
  2.     def __init__(self):
  3.         self.width = 0
  4.         self.height = 0
  5.         self.imageData = None

  6. def load_image(file_path):
  7.     img = cv2.imread(file_path)
  8.     sp = img.shape
  9.     img = cv2.resize(img, (sp[1]//4*4, sp[0]))# 四字节对齐

  10.     image = Image()
  11.     image.width = img.shape[1]
  12.     image.height = img.shape[0]
  13.     image.imageData = img
  14.     return image

点击(此处)折叠或打开

  1. ###################### 人脸检测 ##################################

  2.     image1 = load_image(r"1.jpg")
  3.     image_bytes = bytes(image1.imageData)
  4.     image_ubytes = cast(image_bytes, c_ubyte_p)

  5.     detect_faces = ASFMultiFaceInfo()
  6.     ret = arcsoft_face_func.detect_face(
  7.         engine,
  8.         image1.width,
  9.         image1.height,
  10.         arcsoft_face_func.ASVL_PAF_RGB24_B8G8R8,
  11.         image_ubytes,
  12.         byref(detect_faces)
  13.     )

  14.     if ret == 0:
  15.         print("检测人脸成功")
  16.     else:
  17.         print("检测人脸失败:", ret)

d. 特征提取 

特征提取只支持单人脸,因此做了人脸处理操作,并且需要及时将提取的人脸特征拷贝一份,否则会被覆盖。

点击(此处)折叠或打开

  1. single_face1 = ASFSingleFaceInfo()
  2.     single_face1.faceRect = detect_faces.faceRect[0]
  3.     single_face1.faceOrient = detect_faces.faceOrient[0]

  4.     face_feature = ASFFaceFeature()
  5.     ret = arcsoft_face_func.extract_feature(
  6.         engine,
  7.         image1.width,
  8.         image1.height,
  9.         arcsoft_face_func.ASVL_PAF_RGB24_B8G8R8,
  10.         image_ubytes,
  11.         single_face1,
  12.         byref(face_feature)
  13.     )

  14.     if ret == 0:
  15.         print("提取特征1成功")
  16.     else:
  17.         print("提取特征1失败:", ret)

  18.     feature1 = ASFFaceFeature()
  19.     feature1.featureSize = face_feature.featureSize
  20.     feature1.feature = malloc(feature1.featureSize)
  21.     memcpy(c_void_p(feature1.feature),
  22.            c_void_p(face_feature.feature),
  23.            feature1.featureSize)

e. 特征比对 

按照前文所述再提取一张人脸的特征,即可以进行下面的人脸特征比对操作 

点击(此处)折叠或打开

  1. compare_threshold = c_float()
  2.     ret = arcsoft_face_func.compare_feature(
  3.         engine, feature1, feature2, compare_threshold
  4.     )

  5.     free(c_void_p(feature1.feature))
  6.     free(c_void_p(feature2.feature))

  7.     if ret == 0:
  8.         print("特征比对成功,相似度:", compare_threshold.value)
  9.     else:
  10.         print("特征比对失败:", ret)

f. 年龄、性别、3D Angle 

process接口目前提供了 年龄、性别、3D Angle、活体检测, 但年龄、性别、3D Angle支持多人脸,而活体只支持单人脸,因此下面分别处理。 

点击(此处)折叠或打开

  1. process_mask = arcsoft_face_func.ASF_AGE | \
  2.                    arcsoft_face_func.ASF_GENDER | \
  3.                    arcsoft_face_func.ASF_FACE3DANGLE

  4.     ret = arcsoft_face_func.process(
  5.         engine,
  6.         image1.width,
  7.         image1.height,
  8.         arcsoft_face_func.ASVL_PAF_RGB24_B8G8R8,
  9.         image_ubytes,
  10.         byref(detect_faces),
  11.         c_int32(process_mask)
  12.     )

  13.     if ret == 0:
  14.         print("process成功")
  15.     else:
  16.         print("process失败:", ret)

点击(此处)折叠或打开

  1. ######################## Age ################################

  2.     age_info = ASFAgeInfo()
  3.     ret = arcsoft_face_func.get_age(engine, byref(age_info))

  4.     if ret == 0:
  5.         print("get_age 成功")
  6.         age_ptr = cast(age_info.ageArray, POINTER(c_int))
  7.         for i in range(age_info.num):
  8.             print("face", i, "age:", age_ptr[i])
  9.     else:
  10.         print("get_age 失败:", ret)

  11. ####################### Gender #################################

  12.     gender_info = ASFGenderInfo()
  13.     ret = arcsoft_face_func.get_gender(engine, byref(gender_info))

  14.     if ret == 0:
  15.         print("get_gender 成功")
  16.         gender_ptr = cast(gender_info.genderArray, POINTER(c_int))
  17.         for i in range(gender_info.num):
  18.             print("face", i, "gender:",
  19.                   "女性" if (gender_ptr[i] == 1) else (
  20.                       "男性" if (gender_ptr[i] == 0) else "未知"
  21.                   ))
  22.     else:
  23.         print("get_gender 失败:", ret)

  24. ####################### 3D Angle #################################

  25.     angle_info = ASFFace3DAngle()
  26.     ret = arcsoft_face_func.get_3d_angle(engine, byref(angle_info))

  27.     if ret == 0:
  28.         print("get_3d_angle 成功")
  29.         roll_ptr = cast(angle_info.roll, POINTER(c_float))
  30.         yaw_ptr = cast(angle_info.yaw, POINTER(c_float))
  31.         pitch_ptr = cast(angle_info.pitch, POINTER(c_float))
  32.         status_ptr = cast(angle_info.status, POINTER(c_int32))
  33.         for i in range(angle_info.num):
  34.             print("face", i,
  35.                   "roll:", roll_ptr[i],
  36.                   "yaw:", yaw_ptr[i],
  37.                   "pitch:", pitch_ptr[i],
  38.                   "status:", "正常" if status_ptr[i] == 0 else "出错")

  39.     else:
  40.         print("get_3d_angle 失败:", ret)

g. RGB活体 

在活体检测之前建议按照实际场景设置活体阈值,不设置即使用默认阈值,这里设置了RGB活体的阈值为0.75。并将检测的多人脸分别转为单张人脸的参数传到接口中。


点击(此处)折叠或打开

  1. ######################### 活体阈值设置 ###############################
  2.     threshold_param = ASFLivenessThreshold()
  3.     threshold_param.thresholdmodel_BGR = 0.75
  4.     ret = arcsoft_face_func.set_liveness_param(engine,threshold_param)

  5.     if ret == 0:
  6.         print("set_liveness_param成功")
  7.     else:
  8.         print("set_liveness_param 失败:", ret)

  9.     temp_face_info = ASFMultiFaceInfo()
  10.     temp_face_info.faceNum = 1
  11.     LP_MRECT = POINTER(MRECT)
  12.     temp_face_info.faceRect = LP_MRECT(MRECT(malloc(sizeof(MRECT))))
  13.     LP_c_long = POINTER(c_long)
  14.     temp_face_info.faceOrient = LP_c_long(c_long(malloc(sizeof(c_long))))

  15.     for i in range(detect_faces.faceNum):
  16.         temp_face_info.faceRect[0] = detect_faces.faceRect[i]
  17.         temp_face_info.faceOrient[0] = detect_faces.faceOrient[i]

  18.         ret = arcsoft_face_func.process(
  19.             engine,
  20.             image1.width,
  21.             image1.height,
  22.             arcsoft_face_func.ASVL_PAF_RGB24_B8G8R8,
  23.             image_ubytes,
  24.             byref(temp_face_info),
  25.             c_int32(arcsoft_face_func.ASF_LIVENESS)
  26.         )

  27.         if ret == 0:
  28.             print("process成功")
  29.         else:
  30.             print("process失败:", ret)
  31.         ## RGB活体检测
  32.         ret = arcsoft_face_func.process(
  33.             engine,
  34.             image1.width,
  35.             image1.height,
  36.             arcsoft_face_func.ASVL_PAF_RGB24_B8G8R8,
  37.             image_ubytes,
  38.             byref(temp_face_info),
  39.             c_int32(arcsoft_face_func.ASF_LIVENESS)
  40.         )

  41.         if ret == 0:
  42.             print("process成功")
  43.         else:
  44.             print("process失败:", ret)

  45.         liveness_info = ASFLivenessInfo()
  46.         ret = arcsoft_face_func.get_liveness_info(engine, byref(liveness_info))

  47.         if ret == 0:
  48.             print("get_liveness_info 成功")
  49.             liveness_ptr = cast(liveness_info.isLive, POINTER(c_int))
  50.             print("face", i, "liveness:",
  51.                   "非真人" if (liveness_ptr[0] == 0) else (
  52.                       "真人" if (liveness_ptr[0] == 1) else (
  53.                           "不确定" if (liveness_ptr[0] == -1) else (
  54.                               "传入人脸数>1" if (liveness_ptr[0] == -2) else
  55.                               (liveness_ptr[0])
  56.                           )
  57.                       )
  58.                   ))
  59.         else:
  60.             print("get_liveness_info 失败:", ret)

最后,欢迎大家指教哦~

推荐你们了解虹软人脸识别SDK~
如何使用 python 接入虹软 ArcFace SDK-LMLPHP虹软人脸识别开放平台







09-03 23:49