百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

在 Android 中使用 FaceOnLive SDK 构建人脸识别应用

toyiye 2024-07-16 05:56 15 浏览 0 评论

照片由 安托万·博维利亚 on Unsplash

作为一名使用人脸识别技术的 Android 开发人员,我尝试了多种方法和库,例如将 FaceNet 与 MLKit 的人脸检测、CameraX 和 dlib-android 一起使用。使用服务器端推理,将人脸图像发送到远程 API 以验证其相似性,也是一个很好的策略,但需要额外的后端服务,并且由于网络调用而在应用程序中引入延迟。此外,人脸识别技术还需要处理实时摄像头,这完全排除了服务器端推理。

我有机会尝试了 FaceOnLive 的人脸和活体检测 Android SDK,我想分享我对 SDK 及其在现有 Android 应用程序中的集成的经验。

我们在哪里使用人脸识别技术?

人脸识别可以通过将图片与存储在数据库中的现有图像进行匹配来验证一个人的身份。有几个用例,其中包括:

  • 校园和办公室的考勤监控:可靠的人脸识别应用程序可以取代指纹或身份证扫描机制,以标记工厂工人、办公室员工或教育校园学生的日常出勤情况。结合实时检测,代理出勤的机会大大降低。此外,还消除了包括特殊硬件设备(传感器或指纹扫描仪)的需要。
  • eKYC(e了解您的客户):这是授权组织和代理对特定可用服务验证和验证客户个人数据的过程。使用人脸识别技术,可以在众多文档中轻松匹配eKYC流程中的客户面孔与官方文件中给出的图片进行匹配。
  • 客户分析:通过识别进入购物中心或商店的客户,可以通过分析他们的购物历史和在商店中的存在来定制许多服务。由于人脸识别系统也可以用作身份验证手段,因此免结账解决方案也是可能的,因为它们可以通过面部图片验证人的身份,从而直接确定人的付款方式。店主可以分析常客并定制特定服务,以最大限度地保留和满足客户。
  • 具有面部识别功能的视频监控:它涉及使用安全摄像头和软件通过分析视频源及其面部特征来识别人员。它可以帮助识别刑事调查中的嫌疑人或监控商店中的顾客行为。

FaceOnLive SDK的功能

  • 设备端推理,无需对每个人脸检测请求进行服务器调用。它还确保了最少的延迟并改善了整体用户体验。
  • 使用内置的人脸活体检测,我们不需要编写单独的逻辑来确定相机帧中检测到的人脸的活体。这也消除了在关键应用程序中使用人脸代理。

Android 设置

在本节中,我们将讨论将 FaceOnLive 人脸识别和活体检测 SDK 集成到 Android 应用程序中所需的步骤。

SDK 入门

一旦我们收到 API 密钥,就应该将其添加到 license 文件中 app/src/main/assets

 BsTr9o4f4R/rM3TxbCWVb/hrOJuOIdz8ArQ/t2IgQFFUQzGHOLNNaMJiK/fUfr5zo005zoTA/cm6 
 VoZ6iGl+/hZGA3R5T/VWwhxekbw8JVz9sNesU6rMG5+1cNSN75trH2tpzdCPZ28ZDnZlttmiuUoC 
 9QazRe1xKi5tUXa+xgIxzL0vE6UW2dLKWaEXjn3fSJfLxXWw0q+UZP0hQAXb5Y9Yl/NVi7y3d0xT 
 Vq6/weuMQkgLcNdLqFRvQXup0M9W/pvuhaubySAxHCKVY8wToygN2iM78cOkyyAbGVwZeGQP0Jfd 
 46VZo+w+KCNw355j3osVVMghrOcVZnfbp1dNyg== 

然后,我们可以通过通过传递从 assets 目录 license 中的文件中读取的 API 密钥来调用 FaceSDK.setActivation 我们 onCreate MainActivity 的方法中的 SDK,

 val license_str = application.assets.open("license").bufferedReader().use{ 
     it.readText() 
 } 
 var ret = FaceSDK.setActivation(license_str) 
 if (ret == FaceSDK.SDK_SUCCESS) { 
     ret = FaceSDK.init(assets) 
 } 

ret 表示来自 SDK 的激活响应。它可以根据 SDK 激活过程的结果假设多个值,并应相应地进行处理,

if (ret != FaceSDK.SDK_SUCCESS) {(
    var message = "SDK initialization failed"
    if (ret == FaceSDK.SDK_LICENSE_KEY_ERROR) {
        message = "License key error!"
    } else if (ret == FaceSDK.SDK_LICENSE_APPID_ERROR) {
        message = "App ID error!"
    } else if (ret == FaceSDK.SDK_LICENSE_EXPIRED) {
        message = "License key expired!"
    } else if (ret == FaceSDK.SDK_NO_ACTIVATED) {
        message = "Activation failed!"
    } else if (ret == FaceSDK.SDK_INIT_ERROR) {
        message = "Engine init error!"
    }
}

SDK 现在已准备好用于人脸识别。在下一步中,我们将注册必须被 SDK 识别的人脸(主题)。

注册人脸以进行识别

向 SDK 注册用户(来源:作者)

人脸识别器需要图像,必须从实时摄像头馈送中识别出人的基本事实。此映像使用应用程序用户提供的用户名或 ID 进行注释,并存储在本地设备数据库中。

在实时检测来自摄像头源的人脸时,将检测到的人脸与已注册或作为基本事实提供给 SDK 的人脸进行比较。如果检测到的人脸与数据集中存在的图像匹配,并且在给定特定阈值的情况下具有相似性,则 SDK 会得出结论,检测到的人脸属于图像匹配度最高的人。

// (1) Initialize face detection API
val faceDetectionParam = FaceDetectionParam()
faceDetectionParam.check_liveness = true
// liveness_level determines the model used for liveness detection
// 0 -> model v0
// 1 -> model v2-fast
faceDetectionParam.check_liveness_level = 0

// (2) Perform face detection
// bitmap -> input image containing faces
var faceBoxes: List<FaceBox>? = FaceSDK.faceDetection(bitmap, faceDetectionPar

// (3) Interpret the face detection results
if(faceBoxes.isNullOrEmpty()) {
    // No faces found in the image
} 
else if (faceBoxes.size > 1) {
    // Multiple faces found in the image
} 
else {
    // (4) Extract face templates (Features) from the input image
    val templates = FaceSDK.templateExtraction(bitmap, faceBoxes[0])

    // (5) Save `templates` locally, for instance in an SQLite database
    // The cropped face can also be obtained with Utils.cropFace
    // From the image, crop the region which bounds the face
    val faceImage = Utils.cropFace(bitmap, faceBoxes[0])
}

在下一步中,我们将探讨如何将人脸识别 SDK 与 CameraX 集成以显示实时摄像头源(预览版)。

使用人脸检测创建实时预览

使用 SDK 进行人脸检测和活体检测(来源:作者)

我们通过向 Fotoapparat 提供必要的相机参数和 CameraView 对象来创建实时相机预览,

val PREVIEW_WIDTH = 720
val PREVIEW_HEIGHT = 1280
private lateinit var cameraView: CameraView
private lateinit var faceView: FaceView
private lateinit var fotoapparat: Fotoapparat
private var activeCamera: Camera = Camera.FrontCamera

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_camera_kt)

    cameraView = findViewById(R.id.preview)
    faceView = findViewById(R.id.faceView)
    fotoapparat = Fotoapparat.with(this)
        .into(cameraView)
        .lensPosition(activeCamera.lensPosition)
        .frameProcessor(FaceFrameProcessor())
        .previewResolution { Resolution(PREVIEW_HEIGHT,PREVIEW_WIDTH) }
        .build()
        
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
        == PackageManager.PERMISSION_DENIED
    ) {
        ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 1)
    } else {
        fotoapparat.start()
    }

    ...
}

我们还需要编写一个实现 FaceFrameProcessor ,其中包含对相机产生的每一帧调用 process( frame: Frame ) 的方法,我们执行以下任务,

使用 SDK 识别人脸的示意图(来源:作者)

  • 使用 FaceSDK.yuv2Bitmap 将 YUV 图像转换为对象中包含的 Bitmap RGB 图像。让我们 inputImage 称之为我们的理解。
  • 执行人脸检测, inputImage 使用 FaceSDK.faceDetection 时返回 FaceBox

人脸活体检测(来源:作者)

  • 检查是否 faceBox.liveness 超过某个阈值,如果没有,则从 process 该方法返回,因为该脸可能不属于物理人。
  • inputImageFaceBox 使用 FaceSDK.templateExtractionsubjectTemplates 提取人脸模板
  • 读取存储在数据库中的模板,并将它们中的每一个与 subjectTemplates 返回表示 Float 相似性的 using FaceSDK.similarityCalculation 进行比较。如果相似性大于给定的阈值,请将其与我们在早期迭代中发现的最大相似性进行比较。
inner class FaceFrameProcessor : FrameProcessor {

    override fun process(frame: Frame) {

        // (1) Convert the YUV frame received from the camera to a
        //     RGB bitmap
        val bitmap = FaceSDK.yuv2Bitmap(frame.image, frame.size.width, frame.size.height, cameraOrientation)

        // (2) Initiate face detection
        val faceDetectionParam = FaceDetectionParam()
        faceDetectionParam.check_liveness = true
        // liveness_level determines the model used for liveness detection
        // 0 -> model v0
        // 1 -> model v2-fast
        faceDetectionParam.check_liveness_level = 1
        val faceBoxes = FaceSDK.faceDetection(bitmap, faceDetectionParam)

        // (3) Provide faceBoxes to the faceView
        //     for rendering them on the screen
        runOnUiThread {
            faceView.setFaceBoxes(faceBoxes)
        }
        if(faceBoxes.size > 0) {
            val faceBox = faceBoxes[0]

            // Check if liveness score is above a given threshold
            if (faceBox.liveness > 0.7) {

                // (4) Compare faces stored in database
                //     and determine the greatest match
                val templates = FaceSDK.templateExtraction(bitmap, faceBox)
                var maxSimiarlity = 0f
                var maximiarlityPerson: Person? = null
                for (person in DBManager.personList) {
                    val similarity = FaceSDK.similarityCalculation(templates, person.templates)
                    if (similarity > maxSimiarlity) {
                        maxSimiarlity = similarity
                        maximiarlityPerson = person
                    }
                }
                if (maxSimiarlity > SettingsActivity.getMatchThreshold(context)) {
                    recognized = true
                    val identifiedPerson = maximiarlityPerson
                    val identifiedSimilarity = maxSimiarlity
                    // (5) Navigate the user to the next screen where the
                    //     similarity score can be displayed along with the 
                    //     detected face parameters
                }
            }
        }
    }
}

查看人脸参数

除了人脸及其活度分数的边界框外,SDK 还返回检测到的人脸的偏航、俯仰和滚动。让我们讨论一下这些参数中的每一个:

偏航、俯仰和横滚的图示。源

  • 偏航:它表示从头部直向上的轴的旋转程度。将头部向侧面转动将改变, yaw 如果头部向左转动,则为正,如果头部向右转,则为负数(假设脸在观众面前)。对于直视,为 yaw 0。
  • 俯仰:与偏航类似,它测量绕穿过双耳的轴的旋转程度。向上看如果使 pitch 它为负,而向下看将使它为正。
  • 滚动:它测量围绕穿过鼻子和后脑勺的轴的旋转程度。它测量脸部的侧向倾斜度。

对于每个 FaceBox 返回的 FaceSDK.faceDetection 参数,这些参数可以通过属性访问轻松检索,

val faceDetectionParam = FaceDetectionParam()
faceDetectionParam.check_liveness = true
faceDetectionParam.check_liveness_level = 1
val faceBoxes = FaceSDK.faceDetection(bitmap, faceDetectionParam)

val faceBox = faceBoxes[0]
val yaw = faceBox.yaw
val pitch = faceBox.pitch
val roll = faceBox.roll

结论

我希望博客内容丰富,读者会考虑在他们的 Android 应用程序中使用 FaceOnLive SDK 进行人脸识别。如有疑问和建议,可以在 Medium 上写下评论,或直接与我联系。祝你有美好的一天!

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码