目录
实现效果
实现步骤
●注册虹软账号
新手注册指南
●导入虹软SDK
新手接入指南
●实现自定义相机
由于这里主要将的是人脸识别的技术,因此相机方面的代码,可以在案例源码中查看。
●引擎激活
在引入SDK首次启动APP的时候需要进行引擎激活,它需要传入你创建应用的APP_ID和SDK_KEY,由于激活引擎是联网操作因此需要添加权限并在子线程中进行。
/**
* 激活引擎
*/
private fun activeengine() {
//激活引擎
Thread(Runnable {
val activeCode = faceEngine.activeOnline(this@MainActivity, Constants.APP_ID, Constants.SDK_KEY)
val msg = if(activeCode == ErrorInfo.MOK){
"引擎激活成功"
}else if (activeCode == ErrorInfo.MERR_ASF_ALREADY_ACTIVATED){
"引擎已经激活无需再次激活"
}else{
"引擎激活失败"
}
val activeFileInfo = ActiveFileInfo()
val res = faceEngine.getActiveFileInfo(this@MainActivity, activeFileInfo)
if (res == ErrorInfo.MOK) {
Log.i("激活信息", activeFileInfo.toString())
}
runOnUiThread {
Toast.makeText(this@MainActivity,msg,Toast.LENGTH_SHORT).show()
}
}).start()
}
●进行人脸识别
1.初始化引擎
/**
* 初始化人脸识别引擎
*/
private fun initEngine() {
faceEngine = FaceEngine()
//初始化人脸识别引擎
afCode = faceEngine!!.init(
this,
FaceEngine.ASF_DETECT_MODE_VIDEO,//检测模式,支持VIDEO模式(ASF_DETECT_MODE_VIDEO)和IMAGE模式(ASF_DETECT_MODE_IMAGE)
FaceEngine.ASF_OP_270_ONLY,//人脸检测角度,支持0度(ASF_OP_0_ONLY),90度(ASF_OP_90_ONLY),180度(ASF_OP_180_ONLY),270度(ASF_OP_270_ONLY),全角度检测(ASF_OP_0_HIGHER_EXT),建议使用单一指定角度检测,性能比全角度检测更佳,IMAGE模式(ASF_DETECT_MODE_IMAGE)为了提高检测识别率不支持全角度(ASF_OP_0_HIGHER_EXT)检测
16,//识别的最小人脸比例(图片长边与人脸框长边的比值),在VIDEO模式(ASF_DETECT_MODE_VIDEO)下有效值范围[2,32],推荐值16;在IMAGE模式(ASF_DETECT_MODE_IMAGE)下有效值范围[2,32],推荐值30
20,// 引擎最多能检测出的人脸数,有效值范围[1,50]
FaceEngine.ASF_FACE_DETECT or FaceEngine.ASF_AGE or FaceEngine.ASF_FACE3DANGLE or FaceEngine.ASF_GENDER or FaceEngine.ASF_LIVENESS//需要启用的功能组合,可多选
)
}
2.进行识别
这里是对相机回调的图像数据进行识别。
override fun onPreview(data: ByteArray, camera: Camera) {
if (facerectview != null) {
facerectview.clearRect()
}
val faceInfoList = ArrayList()
//人脸识别
var code = faceEngine!!.detectFaces(
data,//图像数据
facecamera.previewSize!!.width,//图像的宽度,为4的倍数
facecamera.previewSize!!.height,//图像的高度,NV21(CP_PAF_NV21)格式为2的倍数;BGR24(CP_PAF_BGR24)、GRAY(CP_PAF_GRAY)、DEPTH_U16(CP_PAF_DEPTH_U16)格式无限制
FaceEngine.CP_PAF_NV21,// 图像的颜色空间格式,支持NV21(CP_PAF_NV21)、BGR24(CP_PAF_BGR24)、GRAY(CP_PAF_GRAY)、DEPTH_U16(CP_PAF_DEPTH_U16)
faceInfoList//人脸列表,传入后赋值
)
if (code == ErrorInfo.MOK && faceInfoList.size > 0) {
//RGB活体、年龄、性别、三维角度检测,在调用该函数后,可以调用getLiveness(List) ,getAge(List),getGender(List),getFace3DAngle(List)分别获取 RGB活体、年龄、性别、三维角度的检测结果;
// RGB活体最多支持 1 个人脸信息的检测,超过部分返回未知; 年龄、性别、三维角度最多支持4个人脸信息的检测,超过部分返回未知
code = faceEngine!!.process(
data,//图像数据
camera.parameters.previewSize.width,//图像的宽度,为4的倍数
camera.parameters.previewSize.height,//图像的高度,NV21(CP_PAF_NV21)格式为2的倍数,BGR24(CP_PAF_BGR24)格式无限制
FaceEngine.CP_PAF_NV21,//图像的颜色空间格式,支持NV21(CP_PAF_NV21)、BGR24(CP_PAF_BGR24)
faceInfoList,//人脸列表
processMask//检测的属性(ASF_AGE、ASF_GENDER、ASF_FACE3DANGLE、ASF_LIVENESS),支持多选,注:检测的属性须在引擎初始化接口(init(Context, long, int, int, int, int))的combinedMask参数中启用
)
if (code != ErrorInfo.MOK) {
return
}
} else {
return
}
val ageInfoList = ArrayList()//年龄识别
val genderInfoList = ArrayList()//性别识别
val face3DAngleList = ArrayList()//3D角度识别
val faceLivenessInfoList = ArrayList()//活体检测
val ageCode = faceEngine!!.getAge(ageInfoList)
val genderCode = faceEngine!!.getGender(genderInfoList)
val face3DAngleCode = faceEngine!!.getFace3DAngle(face3DAngleList)
val livenessCode = faceEngine!!.getLiveness(faceLivenessInfoList)
//有其中一个的错误码不为0,return
if (ageCode or genderCode or face3DAngleCode or livenessCode != ErrorInfo.MOK) {
return
}
val drawInfoList = CopyOnWriteArrayList()
for (i in faceInfoList.indices) {
val adjustRect = adjustRect(faceInfoList[i].rect)
val drawInfoBean = DrawInfoBean(adjustRect,ageInfoList[i].age,genderInfoList[i].gender,faceLivenessInfoList[i].liveness)
drawInfoList.add(drawInfoBean)
}
//绘制人脸区域及人脸信息
facerectview.setFaceRectDatas(drawInfoList)
}
3.校正人脸区域
这里由于相机返回的图像角度和大小与预览图像的角度和大小不一致因此,我们需要对人脸识别出来的区域进行角度和大小的校正:
/**
* 校正绘制的矩形
* 由于识别的图像的方向与预览的图像的方向不一致,
* 所以需要对检测出来的区域进行校正,当然这里我是针对前置摄像头进行校正,如果是后置摄像头则校正方法不一样
*/
private fun adjustRect(rect: Rect):Rect{
val justRect = Rect()
//根据屏幕与分辨率宽高的比值缩放人脸的矩形区域
val scalWidthVal = facerectview.width.toFloat()/facecamera.previewSize!!.height.toFloat()
val scalHeightVal = facerectview.height.toFloat()/facecamera.previewSize!!.width.toFloat()
rect.left = (scalHeightVal*rect.left).toInt()
rect.right = (scalHeightVal*rect.right).toInt()
rect.top = (scalWidthVal*rect.top).toInt()
rect.bottom = (scalWidthVal*rect.bottom).toInt()
justRect.left = facerectview.width - rect.top
justRect.right =facerectview.width - rect.bottom
justRect.top = facerectview.height - rect.left
justRect.bottom = facerectview.height - rect.right
return justRect
}
校正完之后我们将识别出来的信息封装到DrawInfoBean类中,方便绘制人脸信息。
class DrawInfoBean(var rect: Rect,var age:Int,var gender:Int,var isAlive:Int) {
}
●绘制人脸信息
这里包括绘制人脸区域、性别、年龄和是否是活体。这里我用自定义View进行绘制。
class FaceRectView : View {
private var faceRectList = CopyOnWriteArrayList()
private val mPaint = Paint(Paint.ANTI_ALIAS_FLAG)
init {
mPaint.color = Color.GREEN
mPaint.style = Paint.Style.STROKE
mPaint.strokeWidth = 5f
mPaint.textSize = 80f
mPaint.textAlign = Paint.Align.CENTER
}
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (faceRectList.size > 0) {
for (drawinfo in faceRectList) {
drawIsAlive(drawinfo,canvas)
drawFaceRect(drawinfo,canvas)
drawGenderAndAge(drawinfo, canvas)
}
}
}
/**
* 绘制人脸矩形
*/
private fun drawFaceRect(drawinfo: DrawInfoBean, canvas: Canvas) {
mPaint.style = Paint.Style.STROKE
canvas.drawRect(drawinfo.rect, mPaint)
}
/**
* 绘制是否是活体
*/
private fun drawIsAlive(drawinfo: DrawInfoBean, canvas: Canvas) {
mPaint.style = Paint.Style.FILL
val isAlive = when(drawinfo.isAlive){
LivenessInfo.ALIVE->{
mPaint.color = Color.GREEN
"活体"
}
LivenessInfo.NOT_ALIVE->{
mPaint.color = Color.RED
"非活体"
}
else->{
mPaint.color = Color.YELLOW
"未知"
}
}
canvas.drawText(isAlive,(drawinfo.rect.left + drawinfo.rect.width() / 2).toFloat(), drawinfo.rect.top.toFloat() + mPaint.textSize, mPaint)
}
/**
* 绘制性别和年龄
*/
private fun drawGenderAndAge(drawinfo: DrawInfoBean, canvas: Canvas) {
mPaint.style = Paint.Style.FILL
val gender = when (drawinfo.gender) {
GenderInfo.MALE -> "男"
GenderInfo.FEMALE -> "女"
else -> "未知"
}
canvas.drawText(
"性别:$gender 年龄:${drawinfo.age}",
(drawinfo.rect.left + drawinfo.rect.width() / 2).toFloat(), drawinfo.rect.bottom.toFloat() - 10, mPaint
)
}
/**
* 清空人脸矩形
*/
fun clearRect() {
faceRectList.clear()
postInvalidate()
}
/**
* 设置人脸绘制数据
*/
fun setFaceRectDatas(data: CopyOnWriteArrayList) {
faceRectList = data
postInvalidate()
}
}
案例源码
https://github.com/myml666/Face