我们使用人脸检测sdk进行人脸识别的过程大致可以分为下面几个步骤:
- 加载model文件并初始化接口
- 获取授权
- 调用人脸检测获取关键点
- 通知描画关键点
- 释放接口资源
这里的步骤3和4在接收到camera数据后反复执行。我们可以把这几个步骤抽象成数据流来进行封装。face++检测人脸后不断地生成人脸坐标数据,所以整个检测过程就是人脸坐标数据生产的过程,这里与flow的概念对应上了。
face++需要通过处理camera数据来获取人脸关键点,所以camera又是face++人脸检测api的数据提供者。下图体现了数据流动的过程:
看到这里大家也认为我们通过flow实现这个过程更合理了吧!下面我们分步来实现detect flow的创建.
第一步我们通过flow的builder创建一个flow:
private val imageChannel = Channel()
flow {
while (currentCoroutineContext().isActive) {
emit(imageChannel.receive())
}
}
这里使用Channel来接收图像数据并发射。在detect flow处理图像数据前我们还需要一些初始化face++的工作,这些工作在onStart中处理。
flow.onStart {
var ret = -1
context.assets.open("megviifacepp_model").use { ios ->
modelBuffer = ByteArray(ios.available())
ios.read(modelBuffer)
FaceppApi.getInstance().setLogLevel(4)
ret = FaceppApi.getInstance().initHandle(modelBuffer)
}
if (ret != 0) {
Log.d("dragon_debug", " onStart open failed!")
throw RuntimeException("init")
}
if (requestTakeLicense && modelBuffer != null) {
Log.d("dragon_debug", " onStart takeLicense")
ret = takeLicense(context, modelBuffer!!)
}
if (ret != 0) {
Log.d("dragon_debug", " onStart takeLicense failed!")
throw RuntimeException("takeLicense")
}
ret = FaceDetectApi.getInstance().initFaceDetect()
DLmkDetectApi.getInstance().initDLmkDetect()
if (ret != 0) {
if (requestTakeLicense) {
Log.d("dragon_debug", " onStart initFaceDetect error")
throw RuntimeException("error")
}
requestTakeLicense = true
Log.d("dragon_debug", " onStart initFaceDetect retry exception")
throw RuntimeException("initFace")
}
val config = FaceDetectApi.getInstance().faceppConfig
config.face_confidence_filter = 0.6f
config.detectionMode = FaceDetectApi.FaceppConfig.DETECTION_MODE_TRACKING
FaceDetectApi.getInstance().faceppConfig = config
}
face++初始化操作包括加载模型、检测接口授权、人脸检测接口初始化等步骤。
检测接口授权处理有些特别,face++的接口授权只在授权过期的时候执行。所以这里配合flow的retryWhen来处理过期授权。
flow.retryWhen { cause, attempt ->
Log.d("dragon_debug", " retryWhen $cause attempt $attempt")
if (attempt > 1) {
false
} else {
(cause as? RuntimeException)?.message?.equals("initFace") ?: false
}
}
首先尝试初始化face++接口,如果初始化失败,这里会抛出异常RuntimeException("init")。retryWhen捕获这个异常后发起重试处理,当onStart再次执行时,我们会尝试获取接口权限。
face++接口授权初始化成功后,我们可以使用face++的人脸检测接口了。这里使用map将图像数据转换成人脸坐标数据,转换操作由face++人脸检测接口完成。
flow.map { image ->
val faces = FaceDetectApi.getInstance().detectFace(image)
faces.forEach { face ->
FaceDetectApi.getInstance().getLandmark(face, FaceDetectApi.LMK_84, true)
}
block.invoke(faces)
faces
}
得到的人脸坐标数据通过block回调通知画面描画。
face++接口的释放操作在onCompletion中,
flow.onCompletion {
Log.d("dragon_debug", " onCompletion ")
FaceppApi.getInstance().ReleaseHandle()
DLmkDetectApi.getInstance().releaseDlmDetect()
}